Date.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* JavaScript date/time computation and creation functions. */
  6. #ifndef js_Date_h
  7. #define js_Date_h
  8. /*
  9. * Dates in JavaScript are defined by IEEE-754 double precision numbers from
  10. * the set:
  11. *
  12. * { t ∈ ℕ : -8.64e15 ≤ t ≤ +8.64e15 } ∪ { NaN }
  13. *
  14. * The single NaN value represents any invalid-date value. All other values
  15. * represent idealized durations in milliseconds since the UTC epoch. (Leap
  16. * seconds are ignored; leap days are not.) +0 is the only zero in this set.
  17. * The limit represented by 8.64e15 milliseconds is 100 million days either
  18. * side of 00:00 January 1, 1970 UTC.
  19. *
  20. * Dates in the above set are represented by the |ClippedTime| class. The
  21. * double type is a superset of the above set, so it *may* (but need not)
  22. * represent a date. Use ECMAScript's |TimeClip| method to produce a date from
  23. * a double.
  24. *
  25. * Date *objects* are simply wrappers around |TimeClip|'d numbers, with a bunch
  26. * of accessor methods to the various aspects of the represented date.
  27. */
  28. #include "mozilla/FloatingPoint.h"
  29. #include "mozilla/MathAlgorithms.h"
  30. #include "js/Conversions.h"
  31. #include "js/Value.h"
  32. struct JSContext;
  33. namespace JS {
  34. /**
  35. * Re-query the system to determine the current time zone adjustment from UTC,
  36. * including any component due to DST. If the time zone has changed, this will
  37. * cause all Date object non-UTC methods and formatting functions to produce
  38. * appropriately adjusted results.
  39. *
  40. * Left to its own devices, SpiderMonkey itself may occasionally call this
  41. * method to attempt to keep up with system time changes. However, no
  42. * particular frequency of checking is guaranteed. Embedders unable to accept
  43. * occasional inaccuracies should call this method in response to system time
  44. * changes, or immediately before operations requiring instantaneous
  45. * correctness, to guarantee correct behavior.
  46. */
  47. extern JS_PUBLIC_API(void)
  48. ResetTimeZone();
  49. class ClippedTime;
  50. inline ClippedTime TimeClip(double time);
  51. /*
  52. * |ClippedTime| represents the limited subset of dates/times described above.
  53. *
  54. * An invalid date/time may be created through the |ClippedTime::invalid|
  55. * method. Otherwise, a |ClippedTime| may be created using the |TimeClip|
  56. * method.
  57. *
  58. * In typical use, the user might wish to manipulate a timestamp. The user
  59. * performs a series of operations on it, but the final value might not be a
  60. * date as defined above -- it could have overflowed, acquired a fractional
  61. * component, &c. So as a *final* step, the user passes that value through
  62. * |TimeClip| to produce a number restricted to JavaScript's date range.
  63. *
  64. * APIs that accept a JavaScript date value thus accept a |ClippedTime|, not a
  65. * double. This ensures that date/time APIs will only ever receive acceptable
  66. * JavaScript dates. This also forces users to perform any desired clipping,
  67. * as only the user knows what behavior is desired when clipping occurs.
  68. */
  69. class ClippedTime
  70. {
  71. double t;
  72. explicit ClippedTime(double time) : t(time) {}
  73. friend ClippedTime TimeClip(double time);
  74. public:
  75. // Create an invalid date.
  76. ClippedTime() : t(mozilla::UnspecifiedNaN<double>()) {}
  77. // Create an invalid date/time, more explicitly; prefer this to the default
  78. // constructor.
  79. static ClippedTime invalid() { return ClippedTime(); }
  80. double toDouble() const { return t; }
  81. bool isValid() const { return !mozilla::IsNaN(t); }
  82. };
  83. // ES6 20.3.1.15.
  84. //
  85. // Clip a double to JavaScript's date range (or to an invalid date) using the
  86. // ECMAScript TimeClip algorithm.
  87. inline ClippedTime
  88. TimeClip(double time)
  89. {
  90. // Steps 1-2.
  91. const double MaxTimeMagnitude = 8.64e15;
  92. if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
  93. return ClippedTime(mozilla::UnspecifiedNaN<double>());
  94. // Step 3.
  95. return ClippedTime(ToInteger(time) + (+0.0));
  96. }
  97. // Produce a double Value from the given time. Because times may be NaN,
  98. // prefer using this to manual canonicalization.
  99. inline Value
  100. TimeValue(ClippedTime time)
  101. {
  102. return DoubleValue(JS::CanonicalizeNaN(time.toDouble()));
  103. }
  104. // Create a new Date object whose [[DateValue]] internal slot contains the
  105. // clipped |time|. (Users who must represent times outside that range must use
  106. // another representation.)
  107. extern JS_PUBLIC_API(JSObject*)
  108. NewDateObject(JSContext* cx, ClippedTime time);
  109. // Year is a year, month is 0-11, day is 1-based. The return value is a number
  110. // of milliseconds since the epoch.
  111. //
  112. // Consistent with the MakeDate algorithm defined in ECMAScript, this value is
  113. // *not* clipped! Use JS::TimeClip if you need a clipped date.
  114. JS_PUBLIC_API(double)
  115. MakeDate(double year, unsigned month, unsigned day);
  116. // Year is a year, month is 0-11, day is 1-based, and time is in milliseconds.
  117. // The return value is a number of milliseconds since the epoch.
  118. //
  119. // Consistent with the MakeDate algorithm defined in ECMAScript, this value is
  120. // *not* clipped! Use JS::TimeClip if you need a clipped date.
  121. JS_PUBLIC_API(double)
  122. MakeDate(double year, unsigned month, unsigned day, double time);
  123. // Takes an integer number of milliseconds since the epoch and returns the
  124. // year. Can return NaN, and will do so if NaN is passed in.
  125. JS_PUBLIC_API(double)
  126. YearFromTime(double time);
  127. // Takes an integer number of milliseconds since the epoch and returns the
  128. // month (0-11). Can return NaN, and will do so if NaN is passed in.
  129. JS_PUBLIC_API(double)
  130. MonthFromTime(double time);
  131. // Takes an integer number of milliseconds since the epoch and returns the
  132. // day (1-based). Can return NaN, and will do so if NaN is passed in.
  133. JS_PUBLIC_API(double)
  134. DayFromTime(double time);
  135. // Takes an integer year and returns the number of days from epoch to the given
  136. // year.
  137. // NOTE: The calculation performed by this function is literally that given in
  138. // the ECMAScript specification. Nonfinite years, years containing fractional
  139. // components, and years outside ECMAScript's date range are not handled with
  140. // any particular intelligence. Garbage in, garbage out.
  141. JS_PUBLIC_API(double)
  142. DayFromYear(double year);
  143. // Takes an integer number of milliseconds since the epoch and an integer year,
  144. // returns the number of days in that year. If |time| is nonfinite, returns NaN.
  145. // Otherwise |time| *must* correspond to a time within the valid year |year|.
  146. // This should usually be ensured by computing |year| as |JS::DayFromYear(time)|.
  147. JS_PUBLIC_API(double)
  148. DayWithinYear(double time, double year);
  149. } // namespace JS
  150. #endif /* js_Date_h */