tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */