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