/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* JavaScript date/time computation and creation functions. */ #ifndef js_Date_h #define js_Date_h /* * Dates in JavaScript are defined by IEEE-754 double precision numbers from * the set: * * { t ∈ ℕ : -8.64e15 ≤ t ≤ +8.64e15 } ∪ { NaN } * * The single NaN value represents any invalid-date value. All other values * represent idealized durations in milliseconds since the UTC epoch. (Leap * seconds are ignored; leap days are not.) +0 is the only zero in this set. * The limit represented by 8.64e15 milliseconds is 100 million days either * side of 00:00 January 1, 1970 UTC. * * Dates in the above set are represented by the |ClippedTime| class. The * double type is a superset of the above set, so it *may* (but need not) * represent a date. Use ECMAScript's |TimeClip| method to produce a date from * a double. * * Date *objects* are simply wrappers around |TimeClip|'d numbers, with a bunch * of accessor methods to the various aspects of the represented date. */ #include "mozilla/FloatingPoint.h" #include "mozilla/MathAlgorithms.h" #include "js/Conversions.h" #include "js/Value.h" struct JSContext; namespace JS { class ClippedTime; inline ClippedTime TimeClip(double time); /* * |ClippedTime| represents the limited subset of dates/times described above. * * An invalid date/time may be created through the |ClippedTime::invalid| * method. Otherwise, a |ClippedTime| may be created using the |TimeClip| * method. * * In typical use, the user might wish to manipulate a timestamp. The user * performs a series of operations on it, but the final value might not be a * date as defined above -- it could have overflowed, acquired a fractional * component, &c. So as a *final* step, the user passes that value through * |TimeClip| to produce a number restricted to JavaScript's date range. * * APIs that accept a JavaScript date value thus accept a |ClippedTime|, not a * double. This ensures that date/time APIs will only ever receive acceptable * JavaScript dates. This also forces users to perform any desired clipping, * as only the user knows what behavior is desired when clipping occurs. */ class ClippedTime { double t; explicit ClippedTime(double time) : t(time) {} friend ClippedTime TimeClip(double time); public: // Create an invalid date. ClippedTime() : t(mozilla::UnspecifiedNaN()) {} // Create an invalid date/time, more explicitly; prefer this to the default // constructor. static ClippedTime invalid() { return ClippedTime(); } double toDouble() const { return t; } bool isValid() const { return !mozilla::IsNaN(t); } }; // ES6 20.3.1.15. // // Clip a double to JavaScript's date range (or to an invalid date) using the // ECMAScript TimeClip algorithm. inline ClippedTime TimeClip(double time) { // Steps 1-2. const double MaxTimeMagnitude = 8.64e15; if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude) return ClippedTime(mozilla::UnspecifiedNaN()); // Step 3. return ClippedTime(ToInteger(time) + (+0.0)); } // Produce a double Value from the given time. Because times may be NaN, // prefer using this to manual canonicalization. inline Value TimeValue(ClippedTime time) { return DoubleValue(JS::CanonicalizeNaN(time.toDouble())); } // Create a new Date object whose [[DateValue]] internal slot contains the // clipped |time|. (Users who must represent times outside that range must use // another representation.) extern JS_PUBLIC_API(JSObject*) NewDateObject(JSContext* cx, ClippedTime time); // Year is a year, month is 0-11, day is 1-based. The return value is a number // of milliseconds since the epoch. // // Consistent with the MakeDate algorithm defined in ECMAScript, this value is // *not* clipped! Use JS::TimeClip if you need a clipped date. JS_PUBLIC_API(double) MakeDate(double year, unsigned month, unsigned day); // Takes an integer number of milliseconds since the epoch and returns the // year. Can return NaN, and will do so if NaN is passed in. JS_PUBLIC_API(double) YearFromTime(double time); // Takes an integer number of milliseconds since the epoch and returns the // month (0-11). Can return NaN, and will do so if NaN is passed in. JS_PUBLIC_API(double) MonthFromTime(double time); // Takes an integer number of milliseconds since the epoch and returns the // day (1-based). Can return NaN, and will do so if NaN is passed in. JS_PUBLIC_API(double) DayFromTime(double time); } // namespace JS #endif /* js_Date_h */