Limit offset time zones to minute precision

Modify/add tests for https://github.com/tc39/proposal-temporal/pull/2607
This commit is contained in:
Justin Grant 2023-06-22 18:39:31 -07:00 committed by Philip Chimento
parent 60e475248d
commit 6f146e6f30
93 changed files with 680 additions and 377 deletions

View File

@ -3,19 +3,25 @@
/*---
esid: sec-temporal.duration.compare
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
features: [Temporal]
---*/
const duration1 = new Temporal.Duration(0, 0, 0, 31);
const duration2 = new Temporal.Duration(0, 1);
let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]";
assert.sameValue(Temporal.Duration.compare(duration1, duration2, { relativeTo }), 0, "rounded HH:MM is accepted in string");
let result;
let relativeTo;
relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]";
assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }), "no other rounding is accepted for offset");
const action = (relativeTo) => Temporal.Duration.compare(duration1, duration2, { relativeTo });
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }), "rounded HH:MM not accepted as offset in property bag");
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
assert.sameValue(result, 0, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
assert.sameValue(result, 0, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -3,20 +3,23 @@
/*---
esid: sec-temporal.duration.prototype.add
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let result;
let relativeTo;
const action = (relativeTo) => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]";
const result = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounded HH:MM is accepted in string");
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]";
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "no other rounding is accepted for offset");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "rounded HH:MM not accepted as offset in property bag");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -3,20 +3,25 @@
/*---
esid: sec-temporal.duration.prototype.round
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]";
const result = instance.round({ largestUnit: "years", relativeTo });
TemporalHelpers.assertDuration(result, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "rounded HH:MM is accepted in string");
let result;
let relativeTo;
relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]";
assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "no other rounding is accepted for offset");
const action = (relativeTo) => instance.round({ largestUnit: "years", relativeTo });
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "rounded HH:MM not accepted as offset in property bag");
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -3,20 +3,24 @@
/*---
esid: sec-temporal.duration.prototype.subtract
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]";
const result = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounded HH:MM is accepted in string");
let result;
let relativeTo;
const action = (relativeTo) => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]";
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "no other rounding is accepted for offset");
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "rounded HH:MM not accepted as offset in property bag");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -3,19 +3,24 @@
/*---
esid: sec-temporal.duration.prototype.total
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]";
const result = instance.total({ unit: "days", relativeTo });
assert.sameValue(result, 367, "rounded HH:MM is accepted in string");
let result;
let relativeTo;
relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]";
assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "no other rounding is accepted for offset");
const action = (relativeTo) => instance.total({ unit: "days", relativeTo });
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "rounded HH:MM not accepted as offset in property bag");
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
assert.sameValue(result, 366, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
assert.sameValue(result, 366, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -8,7 +8,7 @@ features: [Temporal]
---*/
const epoch = new Temporal.Instant(0n);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
assert.sameValue(Temporal.Instant.compare(str, epoch), 0, "UTC offset determined from offset part of string (first argument)");
assert.sameValue(Temporal.Instant.compare(epoch, str), 0, "UTC offset determined from offset part of string (second argument)");

View File

@ -7,7 +7,7 @@ description: Instant strings with UTC offset fractional part are not confused wi
features: [Temporal]
---*/
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
const result = Temporal.Instant.from(str);
assert.sameValue(result.epochNanoseconds, 0n, "UTC offset determined from offset part of string");

View File

@ -8,7 +8,7 @@ features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
const result = instance.equals(str);
assert.sameValue(result, true, "UTC offset determined from offset part of string");

View File

@ -9,7 +9,7 @@ features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
const result = instance.since(str);
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC offset determined from offset part of string");

View File

@ -17,5 +17,3 @@ function test(timeZoneIdentifier, expected, description) {
test("UTC", "1970-01-01T00:00:00+00:00", "offset of UTC is +00:00");
test("+01:00", "1970-01-01T01:00:00+01:00", "positive offset");
test("-05:00", "1969-12-31T19:00:00-05:00", "negative offset");
test("+00:44:59.123456789", "1970-01-01T00:44:59.123456789+00:45", "sub-minute offset");
test("-00:00:10.987654321", "1969-12-31T23:59:49.012345679+00:00", "sub-minute offset that rounds to zero");

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.instant.prototype.tostring
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone strings with UTC offset are not confused with time
features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.toString({ timeZone });
assert.sameValue(result.substr(-6), "+01:46", "Time zone string determined from offset");
assert.sameValue(result.substr(-6), "+01:46", "Time zone string determined from bracket name");

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.instant.prototype.tozoneddatetime
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.toZonedDateTime({ timeZone, calendar: "iso8601" });
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.instant.prototype.tozoneddatetimeiso
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.toZonedDateTimeISO(timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -9,7 +9,7 @@ features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
const result = instance.until(str);
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC offset determined from offset part of string");

View File

@ -3,11 +3,11 @@
/*---
esid: sec-temporal.now.zoneddatetime
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = Temporal.Now.zonedDateTime("iso8601", timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -3,11 +3,11 @@
/*---
esid: sec-temporal.now.zoneddatetimeiso
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = Temporal.Now.zonedDateTimeISO(timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const date = new Temporal.PlainDate(2000, 5, 2);

View File

@ -28,13 +28,14 @@ info: |
ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
sec-temporal.plaindate.prototype.tozoneddatetime step 6.a:
a. Set _temporalTime_ to ? ToTemporalTime(_temporalTime_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime to PlainTime

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.plaindate.prototype.tozoneddatetime
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.toZonedDateTime(timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const pdt = Temporal.PlainDateTime.from(datetime);

View File

@ -28,13 +28,14 @@ info: |
ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
sec-temporal.plaindatetime.prototype.until step 3:
3. Set _other_ ? ToTemporalDateTime(_other_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
assert(new Temporal.PlainDateTime(1970, 1, 1, 1, 1, 1, 1, 0, 999).equals(datetime));

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const diff = new Temporal.PlainDateTime(1970, 1, 1).since(datetime);

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.toZonedDateTime(timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const diff = new Temporal.PlainDateTime(1970, 1, 1).until(datetime);

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const pdt = new Temporal.PlainDateTime(2000, 5, 2);

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const time = Temporal.PlainTime.from(datetime);

View File

@ -28,13 +28,14 @@ info: |
ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
sec-temporal.plaintime.prototype.equals step 3:
3. Set _other_ to ? ToTemporalTime(_other_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
assert(new Temporal.PlainTime(1, 1, 1, 1, 0, 999).equals(datetime));

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const diff = new Temporal.PlainTime().since(datetime);

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.plaintime.prototype.tozoneddatetime
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.PlainTime();
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.toZonedDateTime({ plainDate: new Temporal.PlainDate(2000, 5, 2), timeZone });
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -35,7 +35,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const diff = new Temporal.PlainTime().until(datetime);

View File

@ -16,10 +16,6 @@ const valid = [
["\u221201:00", "-01:00"],
["\u22120650", "-06:50"],
["\u221208", "-08:00"],
["+01:00:00", "+01:00"],
["-010000", "-01:00"],
["+03:30:00.000000001", "+03:30:00.000000001"],
["-033000.1", "-03:30:00.1"],
["UTC"],
];
for (const [zone, id = zone] of valid) {
@ -28,7 +24,7 @@ for (const [zone, id = zone] of valid) {
assert.sameValue(result.id, id, `id for ${zone} should be ${id}`);
}
const invalid = ["+00:01.1", "-01.1"];
const invalid = ["+00:01.1", "-01.1", "+01:00:00", "-010000", "+03:30:00.000000001", "-033000.1"];
for (const zone of invalid) {
assert.throws(RangeError, () => new Temporal.TimeZone(zone), `should throw for ${zone}`);
}

View File

@ -21,6 +21,10 @@ const primitives = [
"-01.1",
"1994-11-05T08:15:30+25:00",
"1994-11-05T13:15:30-25:00",
"+01:00:00",
"-010000",
"+03:30:00.000000001",
"-033000.1",
7,
4.2,
12n,

View File

@ -16,10 +16,6 @@ const valids = [
["\u221201:00", "-01:00"],
["\u22120650", "-06:50"],
["\u221208", "-08:00"],
["+01:00:00", "+01:00"],
["-010000", "-01:00"],
["+03:30:00.000000001", "+03:30:00.000000001"],
["-033000.1", "-03:30:00.1"],
["UTC"],
["1994-11-05T08:15:30-05:00", "-05:00"],
["1994-11-05T08:15:30\u221205:00", "-05:00"],

View File

@ -3,11 +3,11 @@
/*---
esid: sec-temporal.timezone.from
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = Temporal.TimeZone.from(timeZone);
assert.sameValue(result.id, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.id, "+01:46", "Time zone string determined from bracket name");

View File

@ -28,13 +28,14 @@ info: |
ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
sec-temporal.timezone.prototype.getinstantfor step 3:
3. Set _dateTime_ ? ToTemporalDateTime(_dateTime_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const conversionTimeZone = new Temporal.TimeZone("UTC"); // should not be used to interpret the argument

View File

@ -17,4 +17,3 @@ function test(timeZoneIdentifier, expectedOffsetString, description) {
test("UTC", "+00:00", "offset of UTC is +00:00");
test("+01:00", "+01:00", "positive offset");
test("-05:00", "-05:00", "negative offset");
test("+00:44:59.123456789", "+00:44:59.123456789", "sub-minute offset is not rounded");

View File

@ -31,7 +31,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const instant = new Temporal.Instant(1001n);
const pdt = tz.getPlainDateTimeFor(instant);

View File

@ -9,7 +9,7 @@ features: [Temporal]
---*/
const instance = new Temporal.TimeZone("UTC");
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const str = "1970-01-01T00:02:00.000000000+00:02[+01:30]";
const result = instance.getPlainDateTimeFor(str);
TemporalHelpers.assertPlainDateTime(result, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 0, "UTC offset determined from offset part of string");

View File

@ -28,13 +28,14 @@ info: |
ii. 1. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
sec-temporal.timezone.prototype.getpossibleinstantsfor step 3:
3. Set _dateTime_ ? ToTemporalDateTime(_dateTime_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const conversionTimeZone = new Temporal.TimeZone("UTC"); // should not be used to interpret the argument

View File

@ -16,11 +16,23 @@ includes: [temporalHelpers.js]
Symbol(),
3600_000_000_000n,
{},
{ valueOf() { return 3600_000_000_000; } },
{
valueOf() {
return 3600_000_000_000;
}
}
].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime));
assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }));
assert.throws(
TypeError,
() => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime),
`invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})`
);
assert.throws(
TypeError,
() => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }),
`invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})`
);
});

View File

@ -3,13 +3,13 @@
/*---
esid: sec-temporal.zoneddatetime.compare
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.ZonedDateTime(1588371269_012_345_679n, "+01:45:30.987654321");
const instance = new Temporal.ZonedDateTime(1588371240_000_000_000n, "+01:46");
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result1 = Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance);
assert.sameValue(result1, 0, "Time zone string determined from bracket name (first argument)");

View File

@ -3,12 +3,32 @@
/*---
esid: sec-temporal.zoneddatetime.compare
description: ZonedDateTime strings with UTC offset fractional part are not confused with time fractional part
description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
features: [Temporal]
---*/
const datetime = new Temporal.ZonedDateTime(29_012_345_679n, "+00:01:30.987654321");
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
const datetime = new Temporal.ZonedDateTime(30_000_000_000n, "+01:35");
let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
assert.sameValue(Temporal.ZonedDateTime.compare(str, datetime), 0, "Time zone determined from bracket name (first argument)");
assert.sameValue(Temporal.ZonedDateTime.compare(datetime, str), 0, "Time zone determined from bracket name (second argument)");
assert.sameValue(
Temporal.ZonedDateTime.compare(str, datetime),
0,
"Time zone determined from bracket name (first argument)"
);
assert.sameValue(
Temporal.ZonedDateTime.compare(datetime, str),
0,
"Time zone determined from bracket name (second argument)"
);
str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
assert.throws(
RangeError,
() => Temporal.ZonedDateTime.compare(str, datetime),
"Trailing zeroes not allowed for sub-minute time zone identifiers (first argument)"
);
assert.throws(
RangeError,
() => Temporal.ZonedDateTime.compare(datetime, str),
"Trailing zeroes not allowed for sub-minute time zone identifiers (second argument)"
);

View File

@ -16,9 +16,17 @@ includes: [temporalHelpers.js]
Symbol(),
3600_000_000_000n,
{},
{ valueOf() { return 3600_000_000_000; } },
{
valueOf() {
return 3600_000_000_000;
}
}
].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }));
assert.throws(
TypeError,
() => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }),
`invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})`
);
});

View File

@ -3,11 +3,11 @@
/*---
esid: sec-temporal.zoneddatetime.from
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone });
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -3,11 +3,18 @@
/*---
esid: sec-temporal.zoneddatetime.from
description: ZonedDateTime strings with UTC offset fractional part are not confused with time fractional part
description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
features: [Temporal]
---*/
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
const result = Temporal.ZonedDateTime.from(str);
assert.sameValue(result.timeZoneId, "+00:01:30.987654321", "Time zone determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:35", "Time zone determined from bracket name");
str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
assert.throws(
RangeError,
() => Temporal.ZonedDateTime.from(str),
"Trailing zeroes not allowed for sub-minute time zone identifiers"
);

View File

@ -1,43 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.from
description: Fuzzy matching behaviour with UTC offsets in ISO 8601 strings and offset option
includes: [temporalHelpers.js]
features: [Temporal]
---*/
["use", "ignore", "prefer", "reject"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from("1970-01-01T12:00-00:44:30.123456789[-00:44:30.123456789]", { offset });
assert.sameValue(result.epochNanoseconds, 45870_123_456_789n, `accepts the exact offset string (offset=${offset})`);
assert.sameValue(result.offset, "-00:44:30.123456789", "offset property is correct");
});
assert.throws(RangeError, () => Temporal.ZonedDateTime.from("1970-01-01T00:00-00:44:30[-00:44:30.123456789]", { offset: "reject" }), "offset=reject does not accept any other rounding than minutes");
const str = "1970-01-01T12:00-00:45[-00:44:30.123456789]";
["ignore", "prefer", "reject"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from(str, { offset });
assert.sameValue(result.epochNanoseconds, 45870_123_456_789n, `accepts the offset string rounded to minutes (offset=${offset})`);
assert.sameValue(result.offset, "-00:44:30.123456789", "offset property is still the full precision");
TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), 1970, 1, "M01", 1, 12, 0, 0, 0, 0, 0, "wall time is preserved");
});
const result = Temporal.ZonedDateTime.from(str, { offset: "use" });
assert.sameValue(result.epochNanoseconds, 45900_000_000_000n, "prioritizes the offset string with HH:MM precision when offset=use");
assert.sameValue(result.offset, "-00:44:30.123456789", "offset property is still the full precision");
TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), 1970, 1, "M01", 1, 12, 0, 29, 876, 543, 211, "wall time is shifted by the difference between exact and rounded offset");
const properties = { year: 1970, month: 1, day: 1, hour: 12, offset: "-00:45", timeZone: "-00:44:30.123456789" };
["ignore", "prefer"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from(properties, { offset });
assert.sameValue(result.epochNanoseconds, 45870_123_456_789n, `no fuzzy matching is done on offset in property bag (offset=${offset})`);
});
const result2 = Temporal.ZonedDateTime.from(properties, { offset: "use" });
assert.sameValue(result2.epochNanoseconds, 45900_000_000_000n, "no fuzzy matching is done on offset in property bag (offset=use)");
assert.throws(RangeError, () => Temporal.ZonedDateTime.from(properties, { offset: "reject" }), "no fuzzy matching is done on offset in property bag (offset=reject)");

View File

@ -24,13 +24,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.day step 6:
6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(86400_000_000_001n, tz);
assert.sameValue(datetime.day, 1);

View File

@ -3,17 +3,17 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.equals
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const expectedTimeZone = "+01:45:30.987654321";
const expectedTimeZone = "+01:46";
const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
// This operation should produce expectedTimeZone, so the following should
// be equal due to the time zones being different on the receiver and
// the argument.
const properties = { year: 1970, month: 1, day: 1, hour: 1, minute: 45, second: 30, millisecond: 987, microsecond: 654, nanosecond: 321 };
const properties = { year: 1970, month: 1, day: 1, hour: 1, minute: 46 };
assert(instance.equals({ ...properties, timeZone }), "time zone string should produce expected time zone");

View File

@ -1,19 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.equals
description: Fuzzy matching behaviour for UTC offset in ISO 8601 string
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const result = instance.equals("1970-01-01T00:44:30.123456789+00:45[+00:44:30.123456789]");
assert.sameValue(result, true, "UTC offset rounded to minutes is accepted");
assert.throws(RangeError, () => instance.equals("1970-01-01T00:44:30.123456789+00:44:30[+00:44:30.123456789]"), "no other rounding than minutes is accepted");
const properties = { offset: "+00:45", year: 1970, month: 1, day: 1, minute: 44, second: 30, millisecond: 123, microsecond: 456, nanosecond: 123, timeZone };
assert.throws(RangeError, () => instance.equals(properties), "no fuzzy matching is done on offset in property bag");

View File

@ -3,13 +3,20 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.equals
description: ZonedDateTime strings with UTC offset fractional part are not confused with time fractional part
description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("+00:01:30.987654321");
const timeZone = new Temporal.TimeZone("+01:35");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
const result = instance.equals(str);
assert.sameValue(result, false, "Time zone determined from bracket name");
str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
assert.throws(
RangeError,
() => instance.equals(str),
"Trailing zeroes not allowed for sub-minute time zone identifiers"
);

View File

@ -24,13 +24,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.getisofields step 7:
7. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// ZonedDateTime
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1001n, tz);
const fields = datetime.getISOFields();

View File

@ -17,4 +17,3 @@ function test(timeZoneIdentifier, expectedOffsetString, description) {
test("UTC", "+00:00", "offset of UTC is +00:00");
test("+01:00", "+01:00", "positive offset");
test("-05:00", "-05:00", "negative offset");
test("+00:44:59.123456789", "+00:44:59.123456789", "sub-minute offset is not rounded");

View File

@ -22,13 +22,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.hour step 6:
6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3600_000_000_001n, tz);
assert.sameValue(datetime.hour, 0);

View File

@ -14,13 +14,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.microsecond step 6:
6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1001n, tz);
assert.sameValue(datetime.microsecond, 0);

View File

@ -16,13 +16,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.millisecond step 6:
6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1_000_001n, tz);
assert.sameValue(datetime.millisecond, 0);

View File

@ -22,13 +22,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.minute step 6:
6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(60_000_000_001n, tz);
assert.sameValue(datetime.minute, 0);

View File

@ -16,4 +16,3 @@ function test(timeZoneIdentifier, expectedOffsetString, description) {
test("UTC", "+00:00", "offset of UTC is +00:00");
test("+01:00", "+01:00", "positive offset");
test("-05:00", "-05:00", "negative offset");
test("+00:44:59.123456789", "+00:44:59.123456789", "sub-minute offset is not rounded");

View File

@ -18,13 +18,14 @@ info: |
3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
sec-get-temporal.zoneddatetime.prototype.second step 6:
6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1_000_000_001n, tz);
assert.sameValue(datetime.second, 0);

View File

@ -3,13 +3,13 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const expectedTimeZone = "+01:45:30.987654321";
const expectedTimeZone = "+01:46";
const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
// This operation should produce expectedTimeZone, so the following operation
// should not throw due to the time zones being different on the receiver and

View File

@ -1,20 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: Fuzzy matching behaviour for UTC offset in ISO 8601 string
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const result = instance.since("1970-01-01T00:44:30.123456789+00:45[+00:44:30.123456789]");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC offset rounded to minutes is accepted");
assert.throws(RangeError, () => instance.since("1970-01-01T00:44:30.123456789+00:44:30[+00:44:30.123456789]"), "no other rounding than minutes is accepted");
const properties = { offset: "+00:45", year: 1970, month: 1, day: 1, minute: 44, second: 30, millisecond: 123, microsecond: 456, nanosecond: 123, timeZone };
assert.throws(RangeError, () => instance.since(properties), "no fuzzy matching is done on offset in property bag");

View File

@ -3,14 +3,21 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: ZonedDateTime strings with UTC offset fractional part are not confused with time fractional part
description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("+00:01:30.987654321");
const timeZone = new Temporal.TimeZone("+01:35");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
const result = instance.since(str);
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, -29, -12, -345, -679, "Time zone determined from bracket name");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, -30, 0, 0, 0, "ISO offset, sub-minute offset trailing-zeroes");
str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
assert.throws(
RangeError,
() => instance.since(str),
"Trailing zeroes not allowed for sub-minute time zone identifiers"
);

View File

@ -26,13 +26,14 @@ info: |
9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_).
sec-get-temporal.zoneddatetime.prototype.tojson step 3:
3. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, *"auto"*, *"auto"*, *"auto"*, *"auto"*).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// ZonedDateTime
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1001n, tz);
const jsonString = datetime.toJSON();

View File

@ -16,5 +16,3 @@ function test(timeZoneIdentifier, expected, description) {
test("UTC", "1970-01-01T00:00:00+00:00[UTC]", "offset of UTC is +00:00");
test("+01:00", "1970-01-01T01:00:00+01:00[+01:00]", "positive offset");
test("-05:00", "1969-12-31T19:00:00-05:00[-05:00]", "negative offset");
test("+00:44:59.123456789", "1970-01-01T00:44:59.123456789+00:45[+00:44:59.123456789]", "sub-minute offset");
test("-00:00:10.987654321", "1969-12-31T23:59:49.012345679+00:00[-00:00:10.987654321]", "sub-minute offset that rounds to zero");

View File

@ -31,7 +31,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1001n, tz);
const pdt = datetime.toPlainDateTime();

View File

@ -31,7 +31,7 @@ features: [Temporal]
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1001n, tz);
const time = datetime.toPlainTime();

View File

@ -26,13 +26,14 @@ info: |
9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_).
sec-get-temporal.zoneddatetime.prototype.tostring step 9:
9. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, _precision_.[[Precision]], _showCalendar_, _showTimeZone_, _showOffset_, _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// ZonedDateTime
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(1001n, tz);
const isoString = datetime.toString();

View File

@ -16,5 +16,3 @@ function test(timeZoneIdentifier, expected, description) {
test("UTC", "1970-01-01T00:00:00+00:00[UTC]", "offset of UTC is +00:00");
test("+01:00", "1970-01-01T01:00:00+01:00[+01:00]", "positive offset");
test("-05:00", "1969-12-31T19:00:00-05:00[-05:00]", "negative offset");
test("+00:44:59.123456789", "1970-01-01T00:44:59.123456789+00:45[+00:44:59.123456789]", "sub-minute offset");
test("-00:00:10.987654321", "1969-12-31T23:59:49.012345679+00:00[-00:00:10.987654321]", "sub-minute offset that rounds to zero");

View File

@ -3,13 +3,13 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const expectedTimeZone = "+01:45:30.987654321";
const expectedTimeZone = "+01:46";
const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone);
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
// This operation should produce expectedTimeZone, so the following operation
// should not throw due to the time zones being different on the receiver and

View File

@ -1,20 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: Fuzzy matching behaviour for UTC offset in ISO 8601 string
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("+00:44:30.123456789");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const result = instance.until("1970-01-01T00:44:30.123456789+00:45[+00:44:30.123456789]");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC offset rounded to minutes is accepted");
assert.throws(RangeError, () => instance.until("1970-01-01T00:44:30.123456789+00:44:30[+00:44:30.123456789]"), "no other rounding than minutes is accepted");
const properties = { offset: "+00:45", year: 1970, month: 1, day: 1, minute: 44, second: 30, millisecond: 123, microsecond: 456, nanosecond: 123, timeZone };
assert.throws(RangeError, () => instance.until(properties), "no fuzzy matching is done on offset in property bag");

View File

@ -3,14 +3,21 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: ZonedDateTime strings with UTC offset fractional part are not confused with time fractional part
description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("+00:01:30.987654321");
const timeZone = new Temporal.TimeZone("+01:35");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const str = "1970-01-01T00:02:00.000000000+00:02[+00:01:30.987654321]";
let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]";
const result = instance.until(str);
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 29, 12, 345, 679, "Time zone determined from bracket name");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, "ISO offset, sub-minute offset trailing-zeroes");
str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]";
assert.throws(
RangeError,
() => instance.until(str),
"Trailing zeroes not allowed for sub-minute time zone identifiers"
);

View File

@ -28,13 +28,14 @@ info: |
ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
sec-temporal.zoneddatetime.prototype.withplaintime step 4.a:
a. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
// This code path is encountered if the time zone offset is negative and its
// absolute value in nanoseconds is greater than the nanosecond field of the
// exact time's epoch parts
const tz = new Temporal.TimeZone("-00:00:00.000000002");
const tz = TemporalHelpers.specificOffsetTimeZone(-2);
const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
const otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime -> PlainTime

View File

@ -3,12 +3,12 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.withtimezone
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const instance = new Temporal.ZonedDateTime(0n, "UTC");
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = instance.withTimeZone(timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -3,11 +3,11 @@
/*---
esid: sec-temporal.zoneddatetime
description: Time zone strings with UTC offset fractional part are not confused with time fractional part
description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset
features: [Temporal]
---*/
const timeZone = "2021-08-19T17:30:45.123456789+01:46[+01:45:30.987654321]";
const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]";
const result = new Temporal.ZonedDateTime(0n, timeZone);
assert.sameValue(result.timeZoneId, "+01:45:30.987654321", "Time zone string determined from bracket name");
assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name");

View File

@ -0,0 +1,28 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.compare
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
features: [Temporal]
---*/
const duration1 = new Temporal.Duration(0, 0, 0, 31);
const duration2 = new Temporal.Duration(0, 1);
let result;
let relativeTo;
const action = (relativeTo) => Temporal.Duration.compare(duration1, duration2, { relativeTo });
relativeTo = "1970-01-01T00:00:00-00:45[Africa/Monrovia]";
result = action(relativeTo);
assert.sameValue(result, 0, "rounded HH:MM is accepted in string offset");
relativeTo = "1970-01-01T00:00:00-00:44:30[Africa/Monrovia]";
result = action(relativeTo);
assert.sameValue(result, 0, "unrounded HH:MM:SS is accepted in string offset");
const timeZone = Temporal.TimeZone.from("Africa/Monrovia");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => action(relativeTo), "rounded HH:MM not accepted as offset in property bag");

View File

@ -0,0 +1,25 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let result;
let relativeTo;
const action = (relativeTo) => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -0,0 +1,39 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.round
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
let result;
let relativeTo;
const action = (relativeTo) => instance.round({ largestUnit: "years", relativeTo });
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00:00-00:45[Africa/Monrovia]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "rounded HH:MM is accepted in string offset");
relativeTo = "1970-01-01T00:00:00-00:44:30[Africa/Monrovia]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 1, "unrounded HH:MM:SS is accepted in string offset");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
const timeZone = Temporal.TimeZone.from("Africa/Monrovia");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => action(relativeTo), "rounded HH:MM not accepted as offset in property bag");

View File

@ -0,0 +1,26 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let result;
let relativeTo;
const action = (relativeTo) => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -0,0 +1,38 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.total
description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
let result;
let relativeTo;
const action = (relativeTo) => instance.total({ unit: "days", relativeTo });
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
assert.sameValue(result, 366, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
assert.sameValue(result, 366, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00:00-00:45[Africa/Monrovia]";
result = action(relativeTo);
assert.sameValue(result, 366, "rounded HH:MM is accepted in string offset");
relativeTo = "1970-01-01T00:00:00-00:44:30[Africa/Monrovia]";
result = action(relativeTo);
assert.sameValue(result, 366, "unrounded HH:MM:SS is accepted in string offset");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");
const timeZone = Temporal.TimeZone.from("Africa/Monrovia");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45", timeZone };
assert.throws(RangeError, () => action(relativeTo), "rounded HH:MM not accepted as offset in property bag");

View File

@ -0,0 +1,103 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.from
description: Fuzzy matching behaviour with UTC offsets in ISO 8601 strings with named time zones and offset option
includes: [temporalHelpers.js]
features: [Temporal]
---*/
["use", "ignore", "prefer", "reject"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from("1970-01-01T12:00-00:44:30[Africa/Monrovia]", { offset });
assert.sameValue(result.epochNanoseconds, 45870_000_000_000n, `accepts the exact offset string (offset: ${offset})`);
assert.sameValue(result.offset, "-00:44:30", "offset property is correct");
});
["use", "ignore", "prefer", "reject"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from("1970-01-01T12:00-00:44:30.000000000[Africa/Monrovia]", { offset });
assert.sameValue(
result.epochNanoseconds,
45870_000_000_000n,
`accepts trailing zeroes after ISO string offset (offset: ${offset})`
);
assert.sameValue(result.offset, "-00:44:30", "offset property removes trailing zeroes from input");
});
assert.throws(
RangeError,
() => Temporal.ZonedDateTime.from("1970-01-01T00:00-00:44:30[-00:45]", { offset: "reject" }),
"minute rounding not supported for offset time zones"
);
const str = "1970-01-01T12:00-00:45[Africa/Monrovia]";
["ignore", "prefer", "reject"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from(str, { offset });
assert.sameValue(
result.epochNanoseconds,
45870_000_000_000n,
`accepts the offset string rounded to minutes (offset=${offset})`
);
assert.sameValue(result.offset, "-00:44:30", "offset property is still the full precision");
TemporalHelpers.assertPlainDateTime(
result.toPlainDateTime(),
1970,
1,
"M01",
1,
12,
0,
0,
0,
0,
0,
"wall time is preserved"
);
});
const result = Temporal.ZonedDateTime.from(str, { offset: "use" });
assert.sameValue(
result.epochNanoseconds,
45900_000_000_000n,
"prioritizes the offset string with HH:MM precision when offset=use"
);
assert.sameValue(result.offset, "-00:44:30", "offset property is still the full precision");
TemporalHelpers.assertPlainDateTime(
result.toPlainDateTime(),
1970,
1,
"M01",
1,
12,
0,
30,
0,
0,
0,
"wall time is shifted by the difference between exact and rounded offset"
);
const properties = { year: 1970, month: 1, day: 1, hour: 12, offset: "-00:45", timeZone: "Africa/Monrovia" };
["ignore", "prefer"].forEach((offset) => {
const result = Temporal.ZonedDateTime.from(properties, { offset });
assert.sameValue(
result.epochNanoseconds,
45870_000_000_000n,
`no fuzzy matching is done on offset in property bag (offset=${offset})`
);
});
const result2 = Temporal.ZonedDateTime.from(properties, { offset: "use" });
assert.sameValue(
result2.epochNanoseconds,
45900_000_000_000n,
"no fuzzy matching is done on offset in property bag (offset=use)"
);
assert.throws(
RangeError,
() => Temporal.ZonedDateTime.from(properties, { offset: "reject" }),
"no fuzzy matching is done on offset in property bag (offset=reject)"
);

View File

@ -0,0 +1,35 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.equals
description: Fuzzy matching behaviour for UTC offset in ISO 8601 string with named time zones
features: [Temporal]
---*/
const expectedNanoseconds = BigInt((44 * 60 + 30) * 1e9);
const timeZone = new Temporal.TimeZone("Africa/Monrovia");
const instance = new Temporal.ZonedDateTime(expectedNanoseconds, timeZone);
let result = instance.equals("1970-01-01T00:00:00-00:45[Africa/Monrovia]");
assert.sameValue(result, true, "UTC offset rounded to minutes is accepted");
result = instance.equals("1970-01-01T00:00:00-00:44:30[Africa/Monrovia]");
assert.sameValue(result, true, "Unrounded sub-minute UTC offset also accepted");
assert.throws(
RangeError,
() => instance.equals("1970-01-01T00:00:00-00:44:30[-00:45]"),
"minute rounding not supported for offset time zones"
);
const properties = {
offset: "-00:45",
year: 1970,
month: 1,
day: 1,
minute: 44,
second: 30,
timeZone
};
assert.throws(RangeError, () => instance.equals(properties), "no fuzzy matching is done on offset in property bag");

View File

@ -0,0 +1,48 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: Fuzzy matching behaviour for UTC offset in ISO 8601 string with named time zones
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("Africa/Monrovia");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
let result = instance.since("1970-01-01T00:44:30-00:44:30[Africa/Monrovia]");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, -1, -29, 0, 0, 0, 0, "UTC offset rounded to minutes is accepted");
result = instance.since("1970-01-01T00:44:30-00:44:30[Africa/Monrovia]");
TemporalHelpers.assertDuration(
result,
0,
0,
0,
0,
-1,
-29,
0,
0,
0,
0,
"Unrounded sub-minute UTC offset also accepted"
);
assert.throws(
RangeError,
() => instance.since("1970-01-01T00:44:30+00:44:30[+00:45"),
"minute rounding not supported for offset time zones"
);
const properties = {
offset: "-00:45",
year: 1970,
month: 1,
day: 1,
minute: 44,
second: 30,
timeZone
};
assert.throws(RangeError, () => instance.since(properties), "no fuzzy matching is done on offset in property bag");

View File

@ -0,0 +1,35 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: Fuzzy matching behaviour for UTC offset in ISO 8601 string with named time zones
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("Africa/Monrovia");
const instance = new Temporal.ZonedDateTime(0n, timeZone);
let result = instance.until("1970-01-01T00:44:30-00:44:30[Africa/Monrovia]");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 29, 0, 0, 0, 0, "UTC offset rounded to minutes is accepted");
result = instance.until("1970-01-01T00:44:30-00:44:30[Africa/Monrovia]");
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 29, 0, 0, 0, 0, "Unrounded sub-minute UTC offset also accepted");
assert.throws(
RangeError,
() => instance.until("1970-01-01T00:44:30+00:44:30[+00:45"),
"minute rounding not supported for offset time zones"
);
const properties = {
offset: "-00:45",
year: 1970,
month: 1,
day: 1,
minute: 44,
second: 30,
timeZone
};
assert.throws(RangeError, () => instance.until(properties), "no fuzzy matching is done on offset in property bag");

View File

@ -3,26 +3,52 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.with
description: Fuzzy matching behaviour with UTC offsets in ISO 8601 strings and offset option
description: Fuzzy matching behaviour with UTC offsets in ISO 8601 strings with named time zones and offset option
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("-00:44:30.123456789");
const timeZone = new Temporal.TimeZone("Africa/Monrovia");
const instance = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, hour: 12, timeZone });
assert.sameValue(instance.offset, "-00:44:30.123456789", "original offset");
assert.sameValue(instance.offset, "-00:44:30", "original offset");
const properties = { day: 2, offset: "-00:45" };
["ignore", "prefer"].forEach((offset) => {
const result = instance.with(properties, { offset });
assert.sameValue(result.epochNanoseconds, 132270_123_456_789n, `ignores new offset (offset=${offset})`);
assert.sameValue(result.epochNanoseconds, 132270_000_000_000n, `ignores new offset (offset=${offset})`);
assert.sameValue(result.offset, instance.offset, "offset property is unchanged");
TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), 1970, 1, "M01", 2, 12, 0, 0, 0, 0, 0, "wall time is not shifted");
TemporalHelpers.assertPlainDateTime(
result.toPlainDateTime(),
1970,
1,
"M01",
2,
12,
0,
0,
0,
0,
0,
"wall time is not shifted"
);
});
const result = instance.with(properties, { offset: "use" });
assert.sameValue(result.epochNanoseconds, 132300_000_000_000n, "accepts HH:MM rounded offset (offset=use)");
assert.sameValue(result.offset, instance.offset, "offset property is unchanged");
TemporalHelpers.assertPlainDateTime(result.toPlainDateTime(), 1970, 1, "M01", 2, 12, 0, 29, 876, 543, 211, "wall time is shifted by the difference between exact and rounded offset");
TemporalHelpers.assertPlainDateTime(
result.toPlainDateTime(),
1970,
1,
"M01",
2,
12,
0,
30,
0,
0,
0,
"wall time is shifted by the difference between exact and rounded offset"
);
assert.throws(RangeError, () => instance.with(properties, { offset: "reject" }), "no fuzzy matching is done in with()");

View File

@ -116,20 +116,6 @@ assert.sameValue(`${ oneDay.add(hours24, {
// throws on wrong offset for ZonedDateTime relativeTo string
assert.throws(RangeError, () => oneDay.add(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" }));
// does not throw on HH:MM rounded offset for ZonedDateTime relativeTo string
assert.sameValue(`${ oneDay.add(hours24, { relativeTo: "1971-01-01T00:00-00:45[-00:44:30]" }) }`, "P2D");
// throws on HH:MM rounded offset for ZonedDateTime relativeTo property bag
assert.throws(RangeError, () => oneDay.add(hours24, {
relativeTo: {
year: 1971,
month: 1,
day: 1,
offset: "-00:45",
timeZone: "-00:44:30"
}
}));
// at least the required properties must be present in relativeTo
assert.throws(TypeError, () => oneDay.add(hours24, {
relativeTo: {

View File

@ -229,24 +229,6 @@ assert.throws(RangeError, () => d.round({
relativeTo: "1971-01-01T00:00+02:00[-00:44:30]"
}));
// does not throw on HH:MM rounded offset for ZonedDateTime relativeTo string
assert.sameValue(`${ d.round({
smallestUnit: "seconds",
relativeTo: "1971-01-01T00:00-00:45[-00:44:30]"
}) }`, "P5Y5M5W5DT5H5M5S");
// throws on HH:MM rounded offset for ZonedDateTime relativeTo property bag
assert.throws(RangeError, () => d.round({
smallestUnit: "seconds",
relativeTo: {
year: 1971,
month: 1,
day: 1,
offset: "-00:45",
timeZone: "-00:44:30"
}
}));
// relativeTo object must contain at least the required correctly-spelled properties
assert.throws(TypeError, () => hours25.round({
largestUnit: "days",

View File

@ -81,20 +81,6 @@ assert.sameValue(`${ oneDay.subtract(hours24, {
// throws on wrong offset for ZonedDateTime relativeTo string
assert.throws(RangeError, () => oneDay.subtract(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" }));
// does not throw on HH:MM rounded offset for ZonedDateTime relativeTo string
assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: "1971-01-01T00:00-00:45[-00:44:30]" }) }`, "PT0S");
// throws on HH:MM rounded offset for ZonedDateTime relativeTo property bag
assert.throws(RangeError, () => oneDay.subtract(hours24, {
relativeTo: {
year: 1971,
month: 1,
day: 1,
offset: "-00:45",
timeZone: "-00:44:30"
}
}));
// at least the required properties must be present in relativeTo
assert.throws(TypeError, () => oneDay.subtract(hours24, {
relativeTo: {

View File

@ -76,25 +76,6 @@ assert.throws(RangeError, () => d.total({
relativeTo: "1971-01-01T00:00+02:00[-00:44:30]"
}));
// does not throw on HH:MM rounded offset for ZonedDateTime relativeTo string
var oneMonth = Temporal.Duration.from({ months: 1 });
assert.sameValue(oneMonth.total({
unit: "months",
relativeTo: "1971-01-01T00:00-00:45[-00:44:30]"
}), 1);
// throws on HH:MM rounded offset for ZonedDateTime relativeTo property bag
assert.throws(RangeError, () => d.total({
unit: "months",
relativeTo: {
year: 1971,
month: 1,
day: 1,
offset: "-00:45",
timeZone: "-00:44:30"
}
}));
// relativeTo object must contain at least the required correctly-spelled properties
assert.throws(TypeError, () => d.total({
unit: "months",

View File

@ -19,11 +19,7 @@ function generateTest(dateTimeString, zoneString, expectedName) {
[
"+00:00",
"+00",
"+0000",
"+00:00:00",
"+000000",
"+00:00:00.000000000",
"+000000.0"
"+0000"
].forEach(zoneString => {
generateTest("1976-11-18T15:23", `${ zoneString }[UTC]`, "UTC");
generateTest("1976-11-18T15:23", `+00:00[${ zoneString }]`, "+00:00");
@ -31,11 +27,7 @@ function generateTest(dateTimeString, zoneString, expectedName) {
[
"-04:00",
"-04",
"-0400",
"-04:00:00",
"-040000",
"-04:00:00.000000000",
"-040000.0"
"-0400"
].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, "-04:00"));
[
"1",
@ -48,7 +40,6 @@ function generateTest(dateTimeString, zoneString, expectedName) {
"12345678"
].forEach(decimals => {
test(`1976-11-18T15:23:30.${ decimals }Z`, "UTC");
test(`1976-11-18T15:23+01:00[+01:00:00.${ decimals }]`, `+01:00:00.${ decimals }`);
});
generateTest("1976-11-18T15:23", "z", "UTC");
test("1976-11-18T15:23:30,1234Z", "UTC");
@ -92,9 +83,6 @@ test("\u221200", "+00:00");
test("\u22120300", "-03:00");
test("\u221203:00", "-03:00");
test("\u221203", "-03:00");
test("+030000.0", "+03:00");
test("-03:00:00", "-03:00");
test("-03:00:00.000000000", "-03:00");
test("1976-11-18T15:23:30.123456789Z[u-ca=iso8601]", "UTC");
test("1976-11-18T15:23:30.123456789-04:00[u-ca=iso8601]", "-04:00");
test("1976-11-18T15:23:30.123456789[UTC][u-ca=iso8601]", "UTC");

View File

@ -31,10 +31,10 @@ assert.sameValue(`${ tz.getInstantFor(dt) }`, "-001000-10-29T04:46:38.271986102Z
// year 0 leap day
var dt = Temporal.PlainDateTime.from("0000-02-29T00:00");
var tz = Temporal.TimeZone.from("-00:01:15");
assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:15Z");
var tz = Temporal.TimeZone.from("-00:01");
assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z");
dt = Temporal.PlainDateTime.from("+000000-02-29T00:00");
assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:15Z");
assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z");
// outside of Instant range
var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999");

View File

@ -1,14 +0,0 @@
// Copyright (C) 2018 Bloomberg LP. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal-timezone-objects
description: sub-minute offset
features: [Temporal]
---*/
var zone = new Temporal.TimeZone("+00:19:32");
var inst = Temporal.Instant.from("2000-01-01T12:00Z");
var dtm = Temporal.PlainDateTime.from("2000-01-01T12:00");
assert.sameValue(zone.getOffsetNanosecondsFor(inst), 1172000000000);
assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-01-01T11:40:28Z");

View File

@ -9,7 +9,7 @@ features: [Temporal]
class SubminuteTimeZone extends Temporal.TimeZone {
constructor() {
super("-00:00:01.111111111");
super("UTC");
}
get id() {
return "Custom/Subminute";

View File

@ -288,6 +288,6 @@ var zdt = Temporal.ZonedDateTime.from({
month: 1,
day: 1,
hour: 12,
timeZone: "-00:44:30"
timeZone: TemporalHelpers.specificOffsetTimeZone(-2.67e12) // -00:44:30 in nanoseconds
});
assert.sameValue(zdt.offset, "-00:44:30");

View File

@ -27,7 +27,7 @@ zdt = Temporal.ZonedDateTime.from("-001000-10-29T10:46:38.271986102+00:00[UTC]")
assert.sameValue(`${ zdt.toInstant() }`, "-001000-10-29T10:46:38.271986102Z");
// year 0 leap day
var zdt = Temporal.ZonedDateTime.from("0000-02-29T00:00-00:01:15[-00:01:15]");
assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:15Z");
zdt = Temporal.ZonedDateTime.from("+000000-02-29T00:00-00:01:15[-00:01:15]");
assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:15Z");
var zdt = Temporal.ZonedDateTime.from("0000-02-29T00:00-00:01[-00:01]");
assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z");
zdt = Temporal.ZonedDateTime.from("+000000-02-29T00:00-00:01[-00:01]");
assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z");