Date.h (9524B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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" // mozilla::{IsFinite,}, mozilla::UnspecifiedNaN 33 #include "mozilla/MathAlgorithms.h" // mozilla::Abs 34 35 #include "js/CharacterEncoding.h" // JS::Latin1Chars 36 #include "js/Conversions.h" // JS::ToInteger 37 #include "js/RealmOptions.h" // JS::RTPCallerTypeToken 38 #include "js/TypeDecls.h" 39 #include "js/Value.h" // JS::CanonicalizeNaN, JS::DoubleValue, JS::Value 40 41 namespace JS { 42 43 /** 44 * Re-query the system to determine the current time zone adjustment from UTC, 45 * including any component due to DST. If the time zone has changed, this will 46 * cause all Date object non-UTC methods and formatting functions to produce 47 * appropriately adjusted results. 48 * 49 * Left to its own devices, SpiderMonkey itself may occasionally try to detect 50 * system time changes. However, no particular frequency of checking is 51 * guaranteed. Embedders unable to accept occasional inaccuracies should call 52 * this method in response to system time changes, or immediately before 53 * operations requiring instantaneous correctness, to guarantee correct 54 * behavior. 55 */ 56 extern JS_PUBLIC_API void 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 double t = mozilla::UnspecifiedNaN<double>(); 81 82 explicit ClippedTime(double time) : t(time) {} 83 friend ClippedTime TimeClip(double time); 84 85 public: 86 // Create an invalid date. 87 ClippedTime() = default; 88 89 // Create an invalid date/time, more explicitly; prefer this to the default 90 // constructor. 91 static ClippedTime invalid() { return ClippedTime(); } 92 93 double toDouble() const { return t; } 94 95 bool isValid() const { return !std::isnan(t); } 96 }; 97 98 // ES6 20.3.1.15. 99 // 100 // Clip a double to JavaScript's date range (or to an invalid date) using the 101 // ECMAScript TimeClip algorithm. 102 inline ClippedTime TimeClip(double time) { 103 // Steps 1-2. 104 const double MaxTimeMagnitude = 8.64e15; 105 if (!std::isfinite(time) || mozilla::Abs(time) > MaxTimeMagnitude) { 106 return ClippedTime(mozilla::UnspecifiedNaN<double>()); 107 } 108 109 // Step 3. 110 return ClippedTime(ToInteger(time)); 111 } 112 113 // Produce a double Value from the given time. Because times may be NaN, 114 // prefer using this to manual canonicalization. 115 inline Value TimeValue(ClippedTime time) { 116 return CanonicalizedDoubleValue(time.toDouble()); 117 } 118 119 // Create a new Date object whose [[DateValue]] internal slot contains the 120 // clipped |time|. (Users who must represent times outside that range must use 121 // another representation.) 122 extern JS_PUBLIC_API JSObject* NewDateObject(JSContext* cx, ClippedTime time); 123 124 /** 125 * Create a new Date object for a year/month/day-of-month/hour/minute/second. 126 * 127 * The created date is initialized with the time value 128 * 129 * TimeClip(UTC(MakeDate(MakeDay(year, mon, mday), 130 * MakeTime(hour, min, sec, 0.0)))) 131 * 132 * where each function/operation is as specified in ECMAScript. 133 */ 134 extern JS_PUBLIC_API JSObject* NewDateObject(JSContext* cx, int year, int mon, 135 int mday, int hour, int min, 136 int sec); 137 138 /** 139 * On success, returns true, setting |*isDate| to true if |obj| is a Date 140 * object or a wrapper around one, or to false if not. Returns false on 141 * failure. 142 * 143 * This method returns true with |*isDate == false| when passed an ES6 proxy 144 * whose target is a Date, or when passed a revoked proxy. 145 */ 146 extern JS_PUBLIC_API bool ObjectIsDate(JSContext* cx, Handle<JSObject*> obj, 147 bool* isDate); 148 149 // Year is a year, month is 0-11, day is 1-based. The return value is a number 150 // of milliseconds since the epoch. 151 // 152 // Consistent with the MakeDate algorithm defined in ECMAScript, this value is 153 // *not* clipped! Use JS::TimeClip if you need a clipped date. 154 JS_PUBLIC_API double MakeDate(double year, unsigned month, unsigned day); 155 156 // Year is a year, month is 0-11, day is 1-based, and time is in milliseconds. 157 // The return value is a number of milliseconds since the epoch. 158 // 159 // Consistent with the MakeDate algorithm defined in ECMAScript, this value is 160 // *not* clipped! Use JS::TimeClip if you need a clipped date. 161 JS_PUBLIC_API double MakeDate(double year, unsigned month, unsigned day, 162 double time); 163 164 // Takes an integer number of milliseconds since the epoch and returns the 165 // year. Can return NaN, and will do so if NaN is passed in. 166 JS_PUBLIC_API double YearFromTime(double time); 167 168 // Takes an integer number of milliseconds since the epoch and returns the 169 // month (0-11). Can return NaN, and will do so if NaN is passed in. 170 JS_PUBLIC_API double MonthFromTime(double time); 171 172 // Takes an integer number of milliseconds since the epoch and returns the 173 // day (1-based). Can return NaN, and will do so if NaN is passed in. 174 JS_PUBLIC_API double DayFromTime(double time); 175 176 // Takes an integer year and returns the number of days from epoch to the given 177 // year. 178 // NOTE: The calculation performed by this function is literally that given in 179 // the ECMAScript specification. Nonfinite years, years containing fractional 180 // components, and years outside ECMAScript's date range are not handled with 181 // any particular intelligence. Garbage in, garbage out. 182 JS_PUBLIC_API double DayFromYear(double year); 183 184 // Takes an integer number of milliseconds since the epoch and an integer year, 185 // returns the number of days in that year. If |time| is nonfinite, returns NaN. 186 // Otherwise |time| *must* correspond to a time within the valid year |year|. 187 // This should usually be ensured by computing |year| as 188 // |JS::DayFromYear(time)|. 189 JS_PUBLIC_API double DayWithinYear(double time, double year); 190 191 // The callback will be a wrapper function that accepts a double (the time 192 // to clamp and jitter) and a JS::RTPCallerTypeToken (a wrapper for 193 // mozilla::RTPCallerType) that can be used to decide the proper clamping 194 // behavior to use. Inside the JS Engine, other parameters that may be needed 195 // are all constant, so they are handled inside the wrapper function 196 using ReduceMicrosecondTimePrecisionCallback = 197 double (*)(double, JS::RTPCallerTypeToken, JSContext*); 198 199 // Set a callback into the toolkit/components/resistfingerprinting function that 200 // will centralize time resolution and jitter into one place. 201 // Defining such a callback requires all Realms that are created afterwards 202 // to have a set JS::RTPCallerTypeToken, via RealmBehaviors or 203 // JS::SetRealmReduceTimerPrecisionCallerType. 204 JS_PUBLIC_API void SetReduceMicrosecondTimePrecisionCallback( 205 ReduceMicrosecondTimePrecisionCallback callback); 206 207 // Get the previously set ReduceMicrosecondTimePrecisionCallback callback or 208 // nullptr. 209 JS_PUBLIC_API ReduceMicrosecondTimePrecisionCallback 210 GetReduceMicrosecondTimePrecisionCallback(); 211 212 // Sets the time resolution for fingerprinting protection, and whether jitter 213 // should occur. If resolution is set to zero, then no rounding or jitter will 214 // occur. This is used if the callback above is not specified. 215 JS_PUBLIC_API void SetTimeResolutionUsec(uint32_t resolution, bool jitter); 216 217 // Returns whether a given string follows the Date Time String Format. 218 JS_PUBLIC_API bool IsISOStyleDate(JSContext* cx, const JS::Latin1Chars& str); 219 220 } // namespace JS 221 222 #endif /* js_Date_h */