-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Date time operations cleanup #1
Changes from all commits
4cb8037
aa66ace
98fb071
acbb12c
b54a20c
0413123
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -32018,7 +32018,7 @@ <h1>Time Values and Time Range</h1> | |||||
<p>Time measurement in ECMAScript is analogous to time measurement in POSIX, in particular sharing definition in terms of the proleptic Gregorian calendar, an <dfn id="epoch">epoch</dfn> of midnight at the beginning of 1 January 1970 UTC, and an accounting of every day as comprising exactly 86,400 seconds (each of which is 1000 milliseconds long).</p> | ||||||
<p>An ECMAScript <dfn variants="time values">time value</dfn> is a Number, either a finite integral Number representing an instant in time to millisecond precision or *NaN* representing no specific instant. A time value that is a multiple of <emu-eqn>24 × 60 × 60 × 1000 = 86,400,000</emu-eqn> (i.e., is equal to 86,400,000 × _d_ for some integer _d_) represents the instant at the start of the UTC day that follows the epoch by _d_ whole UTC days (preceding the epoch for negative _d_). Every other finite time value _t_ is defined relative to the greatest preceding time value _s_ that is such a multiple, and represents the instant that occurs within the same UTC day as _s_ but follows it by (_t_ - _s_) milliseconds.</p> | ||||||
<p>Time values do not account for UTC leap seconds—there are no time values representing instants within positive leap seconds, and there are time values representing instants removed from the UTC timeline by negative leap seconds. However, the definition of time values nonetheless yields piecewise alignment with UTC, with discontinuities only at leap second boundaries and zero difference outside of leap seconds.</p> | ||||||
<p>A Number can exactly represent all integers from -9,007,199,254,740,992 to 9,007,199,254,740,992 (<emu-xref href="#sec-number.min_safe_integer"></emu-xref> and <emu-xref href="#sec-number.max_safe_integer"></emu-xref>). A time value supports a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds. This yields a supported time value range of exactly -100,000,000 days to 100,000,000 days relative to midnight at the beginning of 1 January 1970 UTC.</p> | ||||||
<p>A Number can exactly represent all integers from -9,007,199,254,740,992 to 9,007,199,254,740,992 (<emu-xref href="#sec-number.min_safe_integer"></emu-xref> and <emu-xref href="#sec-number.max_safe_integer"></emu-xref>). A time value used for the [[DateValue]] internal slot of a Date instance supports a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds (exactly -100,000,000 days to 100,000,000 days relative to midnight at the beginning of 01 January, 1970 UTC).</p> | ||||||
<p>The exact moment of midnight at the beginning of 1 January 1970 UTC is represented by the time value *+0*<sub>𝔽</sub>.</p> | ||||||
<emu-note> | ||||||
<p>The 400 year cycle of the proleptic Gregorian calendar contains 97 leap years. This yields an average of 365.2425 days per year, which is 31,556,952,000 milliseconds. Therefore, the maximum range a Number could represent exactly with millisecond precision is approximately -285,426 to 285,426 years relative to 1970. The smaller range supported by a time value as specified in this section is approximately -273,790 to 273,790 years relative to 1970.</p> | ||||||
|
@@ -32027,79 +32027,65 @@ <h1>Time Values and Time Range</h1> | |||||
|
||||||
<emu-clause id="sec-day-number-and-time-within-day"> | ||||||
<h1>Day Number and Time within Day</h1> | ||||||
<p>A given time value _t_ belongs to day number</p> | ||||||
<p>A given time value _t_ identifies a <dfn>day number</dfn> and a specific time within the day.</p> | ||||||
<emu-eqn id="eqn-Day" aoid="Day">Day(_t_) = 𝔽(floor(ℝ(_t_ / msPerDay)))</emu-eqn> | ||||||
<emu-eqn id="eqn-TimeWithinDay" aoid="TimeWithinDay">TimeWithinDay(_t_) = 𝔽(ℝ(_t_) modulo ℝ(msPerDay))</emu-eqn> | ||||||
<p>where the number of milliseconds per day is</p> | ||||||
<emu-eqn id="eqn-msPerDay" aoid="msPerDay">msPerDay = *86400000*<sub>𝔽</sub></emu-eqn> | ||||||
<p>The remainder is called the time within the day:</p> | ||||||
<emu-eqn id="eqn-TimeWithinDay" aoid="TimeWithinDay">TimeWithinDay(_t_) = 𝔽(ℝ(_t_) modulo ℝ(msPerDay))</emu-eqn> | ||||||
</emu-clause> | ||||||
|
||||||
<emu-clause id="sec-year-number"> | ||||||
<h1>Year Number</h1> | ||||||
<p>ECMAScript uses a proleptic Gregorian calendar to map a day number to a year number and to determine the month and date within that year. In this calendar, leap years are precisely those which are (divisible by 4) and ((not divisible by 100) or (divisible by 400)). The number of days in year number _y_ is therefore defined by</p> | ||||||
<emu-eqn id="eqn-DaysInYear" aoid="DaysInYear"> | ||||||
DaysInYear(_y_) | ||||||
= *365*<sub>𝔽</sub> if (ℝ(_y_) modulo 4) ≠ 0 | ||||||
= *366*<sub>𝔽</sub> if (ℝ(_y_) modulo 4) = 0 and (ℝ(_y_) modulo 100) ≠ 0 | ||||||
= *365*<sub>𝔽</sub> if (ℝ(_y_) modulo 100) = 0 and (ℝ(_y_) modulo 400) ≠ 0 | ||||||
= *366*<sub>𝔽</sub> if (ℝ(_y_) modulo 400) = 0 | ||||||
</emu-eqn> | ||||||
<p>All non-leap years have 365 days with the usual number of days per month and leap years have an extra day in February. The day number of the first day of year _y_ is given by:</p> | ||||||
<p>ECMAScript uses a proleptic Gregorian calendar in which common years have 365 days and leap years (those that are divisible by 4 but not divisible by 100 and those that are divisible by 400) have 366 days by means of adding a 29th day to February. The day number of the first day of year _y_ changes by 365 days for every year of difference between _y_ and 1970 plus one day for each of those that is a leap year, and is defined as:</p> | ||||||
<emu-eqn id="eqn-DaysFromYear" aoid="DayFromYear">DayFromYear(_y_) = 𝔽(365 × (ℝ(_y_) - 1970) + floor((ℝ(_y_) - 1969) / 4) - floor((ℝ(_y_) - 1901) / 100) + floor((ℝ(_y_) - 1601) / 400))</emu-eqn> | ||||||
<p>The time value of the start of a year is:</p> | ||||||
<emu-eqn id="eqn-TimeFromYear" aoid="TimeFromYear">TimeFromYear(_y_) = msPerDay × DayFromYear(_y_)</emu-eqn> | ||||||
<p>A time value determines a year by:</p> | ||||||
<emu-eqn id="eqn-YearFromTime" aoid="YearFromTime">YearFromTime(_t_) = the largest integral Number _y_ (closest to +∞) such that TimeFromYear(_y_) ≤ _t_</emu-eqn> | ||||||
<p>The leap-year function is *1*<sub>𝔽</sub> for a time within a leap year and otherwise is *+0*<sub>𝔽</sub>:</p> | ||||||
<emu-eqn id="eqn-InLeapYear" aoid="InLeapYear"> | ||||||
InLeapYear(_t_) | ||||||
= *+0*<sub>𝔽</sub> if DaysInYear(YearFromTime(_t_)) = *365*<sub>𝔽</sub> | ||||||
= *1*<sub>𝔽</sub> if DaysInYear(YearFromTime(_t_)) = *366*<sub>𝔽</sub> | ||||||
</emu-eqn> | ||||||
<p>The leap-year function is *1*<sub>𝔽</sub> for a leap year and otherwise is *+0*<sub>𝔽</sub>:</p> | ||||||
<emu-eqn id="eqn-LeapDaysInYear" aoid="LeapDaysInYear" oldids="eqn-InLeapYear">LeapDaysInYear(_y_) = DayFromYear(_y_ + 1) - DayFromYear(_y_) - 365</emu-eqn> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this run into problems in the year +275760, since DayFromYear is called on an out-of-range year? At first glance the DayFromYear definition seems to handle this OK. |
||||||
</emu-clause> | ||||||
|
||||||
<emu-clause id="sec-month-number"> | ||||||
<h1>Month Number</h1> | ||||||
<p>Months are identified by an integral Number in the inclusive interval from *+0*<sub>𝔽</sub> to *11*<sub>𝔽</sub>. The mapping MonthFromTime(_t_) from a time value _t_ to a month number is defined by:</p> | ||||||
<emu-eqn id="eqn-MonthFromTime" aoid="MonthFromTime"> | ||||||
MonthFromTime(_t_) | ||||||
= *+0*<sub>𝔽</sub> if *+0*<sub>𝔽</sub> ≤ DayWithinYear(_t_) < *31*<sub>𝔽</sub> | ||||||
= *1*<sub>𝔽</sub> if *31*<sub>𝔽</sub> ≤ DayWithinYear(_t_) < *59*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *2*<sub>𝔽</sub> if *59*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *90*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *3*<sub>𝔽</sub> if *90*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *120*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *4*<sub>𝔽</sub> if *120*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *151*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *5*<sub>𝔽</sub> if *151*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *181*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *6*<sub>𝔽</sub> if *181*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *212*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *7*<sub>𝔽</sub> if *212*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *243*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *8*<sub>𝔽</sub> if *243*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *273*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *9*<sub>𝔽</sub> if *273*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *304*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *10*<sub>𝔽</sub> if *304*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *334*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
= *11*<sub>𝔽</sub> if *334*<sub>𝔽</sub> + InLeapYear(_t_) ≤ DayWithinYear(_t_) < *365*<sub>𝔽</sub> + InLeapYear(_t_) | ||||||
<p>Months are identified by an integer in the range 0 to 11, inclusive. A month value of 0 specifies January; 1 specifies February; 2 specifies March; 3 specifies April; 4 specifies May; 5 specifies June; 6 specifies July; 7 specifies August; 8 specifies September; 9 specifies October; 10 specifies November; and 11 specifies December.</p> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
(This "inclusive interval" language is new since tc39#2848, so we should preserve it.) |
||||||
<p>The position of a month _m_ within a year depends upon whether or not the year is a leap year:</p> | ||||||
<emu-eqn id="eqn-DaysWithinYearBeforeMonth" aoid="DaysWithinYearBeforeMonth">DaysWithinYearBeforeMonth(_m_, _leapDaysInYear_) | ||||||
= *+0*<sub>𝔽</sub> if _m_ = 0 | ||||||
= *31*<sub>𝔽</sub> if _m_ = 1 | ||||||
= *59*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 2 | ||||||
= *90*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 3 | ||||||
= *120*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 4 | ||||||
= *151*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 5 | ||||||
= *181*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 6 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In how far is this commit ("Normative: Use Number arithmetic for constructing dates and times") still normative? Now that the version of ECMA-262 this PR is based on is already clarified to use Number arithmetic. I guess my doubt would be about the changes to MakeDay. If we consider that currently "underspecified" then maybe it's not normative. (I haven't gone through the new definition with the various examples given in tc39#1087, though.) If the MakeDay changes are normative, on the other hand, then I'd recommend splitting them out of this commit into a separate one, so that it's immediately clear to reviewers what they have to pay the most attention to. |
||||||
= *212*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 7 | ||||||
= *243*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 8 | ||||||
= *273*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 9 | ||||||
= *304*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 10 | ||||||
= *334*<sub>𝔽</sub> + _leapDaysInYear_ if _m_ = 11 | ||||||
</emu-eqn> | ||||||
<emu-clause id="sec-MonthFromTime" aoid="MonthFromTime" oldids="eqn-MonthFromTime"> | ||||||
<h1>MonthFromTime ( _t_ )</h1> | ||||||
<p>The abstract operation MonthFromTime maps a time value _t_ to a month number. It performs the following steps:</p> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend updating this definition to use a structured header with types so that you get the standard prose. (although I hope it's still possible to specify |
||||||
<emu-alg> | ||||||
1. Let _day_ be DayWithinYear(_t_). | ||||||
1. Let _leapDaysInYear_ be LeapDaysInYear(YearFromTime(_t_)). | ||||||
1. Let _month_ be *+0*<sub>𝔽</sub>. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. month is a Number here but later on it's treated as a mathematical value (MV 1 is added to it and it's passed to DaysWithinYearBeforeMonth which takes a MV). So, it should either be consistently a Number or consistently a MV. I'm not sure which. MV seemed most in line with the definition of a month number in "Months are identified..." above, but on the other hand the result of MonthFromTime is returned directly to JS in some places, like Date.prototype.getMonth, and so those places would have to be passed through 𝔽. (For precision or overflow purposes it doesn't matter which one we choose, since it's always exactly 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, or 11.) |
||||||
1. Repeat, while _month_ < *11*<sub>𝔽</sub>, | ||||||
1. If _day_ < DaysWithinYearBeforeMonth(_month_ + 1, _leapDaysInYear_), return _month_. | ||||||
1. Set _month_ to _month_ + *1*<sub>𝔽</sub>. | ||||||
1. Return *11*<sub>𝔽</sub>. | ||||||
</emu-alg> | ||||||
</emu-clause> | ||||||
<p>where</p> | ||||||
<emu-eqn id="eqn-DayWithinYear" aoid="DayWithinYear">DayWithinYear(_t_) = Day(_t_) - DayFromYear(YearFromTime(_t_))</emu-eqn> | ||||||
<p>A month value of *+0*<sub>𝔽</sub> specifies January; *1*<sub>𝔽</sub> specifies February; *2*<sub>𝔽</sub> specifies March; *3*<sub>𝔽</sub> specifies April; *4*<sub>𝔽</sub> specifies May; *5*<sub>𝔽</sub> specifies June; *6*<sub>𝔽</sub> specifies July; *7*<sub>𝔽</sub> specifies August; *8*<sub>𝔽</sub> specifies September; *9*<sub>𝔽</sub> specifies October; *10*<sub>𝔽</sub> specifies November; and *11*<sub>𝔽</sub> specifies December. Note that <emu-eqn>MonthFromTime(*+0*<sub>𝔽</sub>) = *+0*<sub>𝔽</sub></emu-eqn>, corresponding to Thursday, 1 January 1970.</p> | ||||||
<p>Note that <emu-eqn>MonthFromTime(0) = 0</emu-eqn>, corresponding to Thursday, 01 January, 1970.</p> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Double-check the types here — I think this might be |
||||||
</emu-clause> | ||||||
|
||||||
<emu-clause id="sec-date-number"> | ||||||
<h1>Date Number</h1> | ||||||
<p>A date number is identified by an integral Number in the inclusive interval from *1*<sub>𝔽</sub> to *31*<sub>𝔽</sub>. The mapping DateFromTime(_t_) from a time value _t_ to a date number is defined by:</p> | ||||||
<emu-eqn aoid="DateFromTime"> | ||||||
DateFromTime(_t_) | ||||||
= DayWithinYear(_t_) + *1*<sub>𝔽</sub> if MonthFromTime(_t_) = *+0*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *30*<sub>𝔽</sub> if MonthFromTime(_t_) = *1*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *58*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *2*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *89*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *3*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *119*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *4*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *150*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *5*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *180*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *6*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *211*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *7*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *242*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *8*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *272*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *9*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *303*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *10*<sub>𝔽</sub> | ||||||
= DayWithinYear(_t_) - *333*<sub>𝔽</sub> - InLeapYear(_t_) if MonthFromTime(_t_) = *11*<sub>𝔽</sub> | ||||||
</emu-eqn> | ||||||
<p>A date number is identified by an integral Number in the inclusive interval from *1*<sub>𝔽</sub> to *31*<sub>𝔽</sub>. The mapping from a time value _t_ to a date number is defined by:</p> | ||||||
<emu-eqn aoid="DateFromTime">DateFromTime(_t_) = DayWithinYear(_t_) - DaysWithinYearBeforeMonth(MonthFromTime(_t_), LeapDaysInYear(YearFromTime(_t_))) + 1</emu-eqn> | ||||||
</emu-clause> | ||||||
|
||||||
<emu-clause id="sec-week-day"> | ||||||
|
@@ -32359,12 +32345,14 @@ <h1> | |||||
1. If _year_ is not finite or _month_ is not finite or _date_ is not finite, return *NaN*. | ||||||
1. Let _y_ be 𝔽(! ToIntegerOrInfinity(_year_)). | ||||||
1. Let _m_ be 𝔽(! ToIntegerOrInfinity(_month_)). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could consider changing m to a mathematical value, since we only take ℝ(m) below anyway. But maybe the current way is more consistent, I'm not sure. |
||||||
1. Let _dt_ be 𝔽(! ToIntegerOrInfinity(_date_)). | ||||||
1. Let _ym_ be _y_ + 𝔽(floor(ℝ(_m_) / 12)). | ||||||
1. If _ym_ is not finite, return *NaN*. | ||||||
1. Let _mn_ be 𝔽(ℝ(_m_) modulo 12). | ||||||
1. Find a finite time value _t_ such that YearFromTime(_t_) is _ym_ and MonthFromTime(_t_) is _mn_ and DateFromTime(_t_) is *1*<sub>𝔽</sub>; but if this is not possible (because some argument is out of range), return *NaN*. | ||||||
1. Return Day(_t_) + _dt_ - *1*<sub>𝔽</sub>. | ||||||
1. Let _d_ be 𝔽(! ToIntegerOrInfinity(_date_)). | ||||||
1. Let _wholeYears_ be _y_ + 𝔽(floor(ℝ(_m_) / 12)). | ||||||
1. If _wholeYears_ is not finite, return *NaN*. | ||||||
1. Let _monthWithinYear_ be 𝔽(ℝ(_m_) modulo 12). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this has to be a mathematical value, since it's passed to DaysWithinYearBeforeMonth which takes a mathematical value as its first argument. |
||||||
1. Let _leapDaysInYear_ be LeapDaysInYear(TimeFromYear(_wholeYears_)). | ||||||
1. Let _day_ be DayFromYear(_wholeYears_) + DaysWithinYearBeforeMonth(_monthWithinYear_, _leapDaysInYear_) *1*<sub>𝔽</sub>. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
1. If _day_ is not finite, return *NaN*. | ||||||
1. Return _day_. | ||||||
</emu-alg> | ||||||
</emu-clause> | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the distinction made here between "time value" and "time value used for the [[DateValue]] internal slot". Compare this to the types used in LocalTime (finite time value → integral Number) and UTC (Number → time value). This "integral Number" language I think is obfuscating a bit what is actually going on. I think what these are really trying to say is "finite time value → finite time value that can go in a [[DateValue]] internal slot" and "time value from a [[DateValue]] internal slot → time value", respectively. But that rename might be a harder case to make convincingly, because it only matters in the case of a time value near the very edge of the range, with a UTC offset.
There are probably a few more places where we'd have to clarify this distinction, like this line:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if separate terminology is the way to go, like "exact time value" and "local-adjusted time value".