Skip to content

Commit

Permalink
Editorial: rename TimeZone "nanoseconds" slot/AOs
Browse files Browse the repository at this point in the history
Now that we've limited TimeZone's [[OffsetNanoseconds]] internal slot
to minute precision, this commit refactors TimeZone to clarify that only
minutes are allowed in that slot and related abstract operations.

Changes:
* Renames TimeZone's [[OffsetNanoseconds]] internal slot to
  [[OffsetMinutes]]
* Changes ParseTimeZoneIdentifier to return an [[OffsetMinutes]] field
  instead of an [[OffsetNanoseconds]] field.
* Changes FormatOffsetTimeZoneIdentifier to expect a minutes argument.

The goal of this change is to avoid the complexity and potential
confusion from a slot and AOs that deal with "nanoseconds" values
that nonetheless are restricted to minutes.
  • Loading branch information
justingrant committed Jul 18, 2023
1 parent 861aba9 commit 5eca86f
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 55 deletions.
18 changes: 9 additions & 9 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ export function ParseTimeZoneIdentifier(identifier) {
if (OFFSET_IDENTIFIER.test(identifier)) {
// The regex limits the input to minutes precision
const { offsetNanoseconds } = ParseDateTimeUTCOffset(identifier);
return { offsetNanoseconds };
return { offsetMinutes: offsetNanoseconds / 6e10 };
}
return { tzName: identifier };
}
Expand Down Expand Up @@ -2127,8 +2127,8 @@ export function ToTemporalTimeZoneSlotValue(temporalTimeZoneLike) {
const { tzName, offset, z } = ParseTemporalTimeZoneString(identifier);
if (tzName) {
// tzName is any valid identifier string in brackets, and could be an offset identifier
const { offsetNanoseconds } = ParseTimeZoneIdentifier(tzName);
if (offsetNanoseconds !== undefined) return FormatOffsetTimeZoneIdentifier(offsetNanoseconds);
const { offsetMinutes } = ParseTimeZoneIdentifier(tzName);
if (offsetMinutes !== undefined) return FormatOffsetTimeZoneIdentifier(offsetMinutes);

const record = GetAvailableNamedTimeZoneIdentifier(tzName);
if (!record) throw new RangeError(`Unrecognized time zone ${tzName}`);
Expand All @@ -2140,7 +2140,7 @@ export function ToTemporalTimeZoneSlotValue(temporalTimeZoneLike) {
if (hasSubMinutePrecision) {
throw new RangeError(`Seconds not allowed in offset time zone: ${offset}`);
}
return FormatOffsetTimeZoneIdentifier(offsetNanoseconds);
return FormatOffsetTimeZoneIdentifier(offsetNanoseconds / 6e10);
}

export function ToTemporalTimeZoneIdentifier(slotValue) {
Expand Down Expand Up @@ -2210,7 +2210,7 @@ export function GetOffsetStringFor(timeZone, instant) {
// But in the polyfill, we re-use it to provide clearer error messages.
function formatOffsetStringNanoseconds(offsetNs) {
const offsetMinutes = MathTrunc(offsetNs / 6e10);
let offsetStringMinutes = FormatOffsetTimeZoneIdentifier(offsetMinutes * 6e10);
let offsetStringMinutes = FormatOffsetTimeZoneIdentifier(offsetMinutes);
const subMinuteNanoseconds = MathAbs(offsetNs) % 6e10;
if (!subMinuteNanoseconds) return offsetStringMinutes;

Expand Down Expand Up @@ -2739,9 +2739,9 @@ export function GetNamedTimeZoneOffsetNanoseconds(id, epochNanoseconds) {
return +utc.minus(epochNanoseconds);
}

export function FormatOffsetTimeZoneIdentifier(offsetNanoseconds) {
const sign = offsetNanoseconds < 0 ? '-' : '+';
const absoluteMinutes = MathAbs(offsetNanoseconds / 6e10);
export function FormatOffsetTimeZoneIdentifier(offsetMinutes) {
const sign = offsetMinutes < 0 ? '-' : '+';
const absoluteMinutes = MathAbs(offsetMinutes);
const intHours = MathFloor(absoluteMinutes / 60);
const hh = ISODateTimePartString(intHours);
const intMinutes = absoluteMinutes % 60;
Expand All @@ -2751,7 +2751,7 @@ export function FormatOffsetTimeZoneIdentifier(offsetNanoseconds) {

export function FormatDateTimeUTCOffsetRounded(offsetNanoseconds) {
offsetNanoseconds = RoundNumberToIncrement(bigInt(offsetNanoseconds), 60e9, 'halfExpand').toJSNumber();
return FormatOffsetTimeZoneIdentifier(offsetNanoseconds);
return FormatOffsetTimeZoneIdentifier(offsetNanoseconds / 6e10);
}

export function GetUTCEpochNanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) {
Expand Down
14 changes: 7 additions & 7 deletions polyfill/lib/timezone.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export class TimeZone {
constructor(identifier) {
let stringIdentifier = ES.RequireString(identifier);
const parseResult = ES.ParseTimeZoneIdentifier(identifier);
if (parseResult.offsetNanoseconds !== undefined) {
stringIdentifier = ES.FormatOffsetTimeZoneIdentifier(parseResult.offsetNanoseconds);
if (parseResult.offsetMinutes !== undefined) {
stringIdentifier = ES.FormatOffsetTimeZoneIdentifier(parseResult.offsetMinutes);
} else {
const record = ES.GetAvailableNamedTimeZoneIdentifier(stringIdentifier);
if (!record) throw new RangeError(`Invalid time zone identifier: ${stringIdentifier}`);
Expand All @@ -51,8 +51,8 @@ export class TimeZone {
instant = ES.ToTemporalInstant(instant);
const id = GetSlot(this, TIMEZONE_ID);

const offsetNanoseconds = ES.ParseTimeZoneIdentifier(id).offsetNanoseconds;
if (offsetNanoseconds !== undefined) return offsetNanoseconds;
const offsetMinutes = ES.ParseTimeZoneIdentifier(id).offsetMinutes;
if (offsetMinutes !== undefined) return offsetMinutes * 6e10;

return ES.GetNamedTimeZoneOffsetNanoseconds(id, GetSlot(instant, EPOCHNANOSECONDS));
}
Expand Down Expand Up @@ -80,8 +80,8 @@ export class TimeZone {
const Instant = GetIntrinsic('%Temporal.Instant%');
const id = GetSlot(this, TIMEZONE_ID);

const offsetNanoseconds = ES.ParseTimeZoneIdentifier(id).offsetNanoseconds;
if (offsetNanoseconds !== undefined) {
const offsetMinutes = ES.ParseTimeZoneIdentifier(id).offsetMinutes;
if (offsetMinutes !== undefined) {
const epochNs = ES.GetUTCEpochNanoseconds(
GetSlot(dateTime, ISO_YEAR),
GetSlot(dateTime, ISO_MONTH),
Expand All @@ -94,7 +94,7 @@ export class TimeZone {
GetSlot(dateTime, ISO_NANOSECOND)
);
if (epochNs === null) throw new RangeError('DateTime outside of supported range');
return [new Instant(epochNs.minus(offsetNanoseconds))];
return [new Instant(epochNs.minus(offsetMinutes * 6e10))];
}

const possibleEpochNs = ES.GetNamedTimeZoneEpochNanoseconds(
Expand Down
2 changes: 1 addition & 1 deletion spec/intl.html
Original file line number Diff line number Diff line change
Expand Up @@ -2543,7 +2543,7 @@ <h1>Temporal.ZonedDateTime.prototype.toLocaleString ( [ _locales_ [ , _options_
1. Let _dateTimeFormat_ be ! OrdinaryCreateFromConstructor(%DateTimeFormat%, %DateTimeFormat.prototype%, « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], [[Era]], [[Year]], [[Month]], [[Day]], [[DayPeriod]], [[Hour]], [[Minute]], [[Second]], [[FractionalSecondDigits]], [[TimeZoneName]], [[HourCycle]], [[Pattern]], [[BoundFormat]] »).
1. Let _timeZone_ be ? ToTemporalTimeZoneIdentifier(_zonedDateTime_.[[TimeZone]]).
1. Let _timeZoneParseResult_ be ? ParseTimeZoneIdentifier(_timeZone_).
1. If _timeZoneParseResult_.[[OffsetNanoseconds]] is not ~empty~, throw a *RangeError* exception.
1. If _timeZoneParseResult_.[[OffsetMinutes]] is not ~empty~, throw a *RangeError* exception.
1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_timeZone_).
1. If _timeZoneIdentifierRecord_ is ~empty~, throw a *RangeError* exception.
1. Set _timeZone_ to _timeZoneIdentifierRecord_.[[PrimaryIdentifier]].
Expand Down
12 changes: 6 additions & 6 deletions spec/mainadditions.html
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,8 @@ <h1>
<emu-alg>
1. Let _systemTimeZoneIdentifier_ be SystemTimeZoneIdentifier().
1. <ins>Let _parseResult_ be ! ParseTimeZoneIdentifier(_systemTimeZoneIdentifier_).</ins>
1. If <del>IsTimeZoneOffsetString(_systemTimeZoneIdentifier_) is *true*</del><ins>_parseResult_.[[OffsetNanoseconds]] is not ~empty~</ins>, then
1. Let _offsetNs_ be <del>ParseTimeZoneOffsetString(_systemTimeZoneIdentifier_)</del><ins>_parseResult_.[[OffsetNanoseconds]]</ins>.
1. If <del>IsTimeZoneOffsetString(_systemTimeZoneIdentifier_) is *true*</del><ins>_parseResult_.[[OffsetMinutes]] is not ~empty~</ins>, then
1. Let _offsetNs_ be <del>ParseTimeZoneOffsetString(_systemTimeZoneIdentifier_)</del><ins>_parseResult_.[[OffsetMinutes]] × (6 × 10<sup>10</sup>)</ins>.
1. Else,
1. Let _offsetNs_ be GetNamedTimeZoneOffsetNanoseconds(_systemTimeZoneIdentifier_, ℤ(ℝ(_t_) × 10<sup>6</sup>)).
1. Let _offsetMs_ be truncate(_offsetNs_ / 10<sup>6</sup>).
Expand Down Expand Up @@ -360,8 +360,8 @@ <h1>
<emu-alg>
1. Let _systemTimeZoneIdentifier_ be SystemTimeZoneIdentifier().
1. <ins>Let _parseResult_ be ! ParseTimeZoneIdentifier(_systemTimeZoneIdentifier_).</ins>
1. If <del>IsTimeZoneOffsetString(_systemTimeZoneIdentifier_) is *true*</del><ins>_parseResult_.[[OffsetNanoseconds]] is not ~empty~</ins>, then
1. Let _offsetNs_ be <del>ParseTimeZoneOffsetString(_systemTimeZoneIdentifier_)</del><ins>_parseResult_.[[OffsetNanoseconds]]</ins>.
1. If <del>IsTimeZoneOffsetString(_systemTimeZoneIdentifier_) is *true*</del><ins>_parseResult_.[[OffsetMinutes]] is not ~empty~</ins>, then
1. Let _offsetNs_ be <del>ParseTimeZoneOffsetString(_systemTimeZoneIdentifier_)</del><ins>_parseResult_.[[OffsetMinutes]] × (6 × 10<sup>10</sup>)</ins>.
1. Else,
1. Let _possibleInstants_ be GetNamedTimeZoneEpochNanoseconds(_systemTimeZoneIdentifier_, ℝ(YearFromTime(_t_)), ℝ(MonthFromTime(_t_)) + 1, ℝ(DateFromTime(_t_)), ℝ(HourFromTime(_t_)), ℝ(MinFromTime(_t_)), ℝ(SecFromTime(_t_)), ℝ(msFromTime(_t_)), 0, 0).
1. NOTE: The following steps ensure that when _t_ represents local time repeating multiple times at a negative time zone transition (e.g. when the daylight saving time ends or the time zone offset is decreased due to a time zone rule change) or skipped local time at a positive time zone transition (e.g. when the daylight saving time starts or the time zone offset is increased due to a time zone rule change), _t_ is interpreted using the time zone offset before the transition.
Expand Down Expand Up @@ -430,8 +430,8 @@ <h1>
<emu-alg>
1. Let _systemTimeZoneIdentifier_ be SystemTimeZoneIdentifier().
1. <ins>Let _parseResult_ be ! ParseTimeZoneIdentifier(_systemTimeZoneIdentifier_).</ins>
1. If <del>IsTimeZoneOffsetString(_systemTimeZoneIdentifier_) is *true*</del><ins>_parseResult_.[[OffsetNanoseconds]] is not ~empty~</ins>, then
1. Let _offsetNs_ be <del>ParseTimeZoneOffsetString(_systemTimeZoneIdentifier_)</del><ins>_parseResult_.[[OffsetNanoseconds]]</ins>.
1. If <del>IsTimeZoneOffsetString(_systemTimeZoneIdentifier_) is *true*</del><ins>_parseResult_.[[OffsetMinutes]] is not ~empty~</ins>, then
1. Let _offsetNs_ be <del>ParseTimeZoneOffsetString(_systemTimeZoneIdentifier_)</del><ins>_parseResult_.[[OffsetMinutes]] × (6 × 10<sup>10</sup>)</ins>.
1. Else,
1. Let _offsetNs_ be GetNamedTimeZoneOffsetNanoseconds(_systemTimeZoneIdentifier_, ℤ(ℝ(_tv_) × 10<sup>6</sup>)).
1. <del>Let _offset_ be 𝔽(truncate(_offsetNs_ / 10<sup>6</sup>)).</del>
Expand Down
Loading

0 comments on commit 5eca86f

Please sign in to comment.