js-temporal/temporal-polyfill

Should `(ZonedDateTime|PlainDateTime|PlainDate|PlainYearMonth).from` throw for object representing non-existent date?

lionel-rowe opened this issue · 2 comments

February 2020 has 29 days, so February 31 doesn't exist.

When parsing from a string, this throws as expected:

Temporal.ZonedDateTime.from('2020-02-31T00:00:00+00:00[UTC]')
// Uncaught RangeError: value out of range: 1 <= 31 <= 29

But when constructing the same date from a plain object, the polyfill simply "best-guesses" it to Feb 29, without throwing:

Temporal.ZonedDateTime.from({ year: 2020, month: 2, day: 31, hour: 0, minute: 0, second: 0, timeZone: 'UTC' })
// Temporal.ZonedDateTime <2020-02-29T00:00:00+00:00[UTC]>

The same issue also applies to PlainDate, PlainDateTime, and PlainYearMonth.

Is this correct?

This is handled by the overflow option in the from methods:

overflow (string): How to deal with out-of-range values if thing is an object. Allowed values are 'constrain' and 'reject'. The default is 'constrain'.

Temporal.ZonedDateTime.from(
  { year: 2020, month: 2, day: 31, hour: 0, minute: 0, second: 0, timeZone: 'UTC' }
);
// Temporal.ZonedDateTime <2020-02-29T00:00:00+00:00[UTC]>

Temporal.ZonedDateTime.from(
  { year: 2020, month: 2, day: 31, hour: 0, minute: 0, second: 0, timeZone: 'UTC' },
  { overflow: 'reject' }
);
// Uncaught RangeError: value out of range: 1 <= 31 <= 29

There's an issue in the tc39/proposal-temporal repo which I can't find at the moment, which discusses this — I admit it's slightly inconsistent that objects and strings are treated differently here. The thinking goes, "2020-02-31" is a syntactically invalid string according to ISO 8601, and will never be generated by any other application that outputs valid ISO 8601 strings, so it's always rejected. A property bag { year: 2020, month: 2, day: 31 } is not intrinsically invalid, so it can be subject to this sort of preprocessing given by the overflow option.