js-temporal / proposal-temporal-v2

Future additions to Temporal
MIT License
24 stars 1 forks source link

Static string validation of PlainDate, PlainTime and PlainDateTime #22

Open dotnetCarpenter opened 2 years ago

dotnetCarpenter commented 2 years ago

String validation of date, time or date with time is currently error prone and/or memory hungry. With this proposal, String validation will have native performance and be correct.

String is very useful for serialization and communication between services but require validation when expressing date, time or date with time.

By adding a static .test method to Temporal.PlainDate, Temporal.PlainTime and Temporal.PlainDateTime, validation would be performant, easy and correct.

API:

YYYY refers to a year between 0001 and 9999, both included.

MM refers to a a zero-padded month between 01 and 12, both included.

DD refers to a a zero-padded month day between 01 and 31, both included, in the Gregorian calendar.

hh refers to a zero-padded hour between 00 and 23, both included.

mm refers to a zero-padded minute between 00 and 59, both included.

ss refers to a zero-padded second between 00 and 60 (where 60 is only used to denote an added leap second), both included.

Ref: _https://en.wikipedia.org/wiki/ISO_8601_

Advantages:

One use-case is to send a valid date as a String to a database and while the database will validate the date, we would want to check the date before opening a database connection and wait for the response. This can obviously be done both client-side and server-side before communicating with the database.

With Temporal proposal 1, it can be implemented like so:

const validateDate = x => {
    try {
        Temporal.PlainDate.from (x)
    } catch (e) {
        return false
    }
    return true
}

validateDate ('2022-02-29') // <- false, because there is only 28 days in February 2022.

An optimization would be to have Temporal.PlainDate.test so that the static .test method can bypass the instantiation process and return a Boolean. Perhaps bringing it on par with the performance of the regex solution.

const validateDate = x => /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2]\d|3[0-1])$/.test (x)

validateDate ('2022-02-29') // <- true, because regex has no concept of dates

Concerns:

Communicating between services sometimes also means different formats. This is covered in https://github.com/js-temporal/proposal-temporal-v2/issues/2 which in contrast to this suggestion, creates an object instance. Similarly to #2, it might be useful to be able to validate other serializable formats than the ones Temporal's from method accept.

For outputting different serializable formats from Temporal, there is https://github.com/js-temporal/proposal-temporal-v2/issues/5.

I have not gone much into Temporal.PlainTime.test and Temporal.PlainDateTime.test. Further examination of use-cases might reveal that it's more complicated than stated here.

Prior art:

There does not seem to be any static date parsing libraries in the current JS ecosystem (as of 2021). All four libraries below will create instance of Date.

Lib Name Static Syntax URL
Moment.js No moment("2022-02-29").isValid() https://momentjs.com/docs/#/parsing/is-valid/
luxon No DateTime.fromISO("2022-02-29").isValid https://moment.github.io/luxon/#/validity
date-fns No isValid (parseISO('2022-02-29')) https://date-fns.org/v2.27.0/docs/isValid#examples
date-fp No isValid (parse ('YYYY-MM-DD', '2022-02-29')) https://cullophid.github.io/date-fp/docs/functions/is-valid.html

Constraints / corner cases:

ptomato commented 2 years ago

Thanks! If I understand correctly, the main advantage of this API would be validating the string without constructing a Temporal.(Whatever) instance, and the reason it's not ideal to do in user code is because you either have to construct the instance, or you write your own regex and then you have no guarantee that it corresponds 100% to what from() would accept. Is that accurate?

dotnetCarpenter commented 2 years ago

Yes, that is correct. The main points are to put it simply, for Temporal.PlainDate,

  1. Static test function that can validate an ISO date string is valid, without relaying on try...catch and creating Temporal JS instances.
  2. Regex does not work since there is no guarantee that the ISO date string is a valid date.

AFAIK the two points are identical for Temporal.PlainTime and Temporal.PlainDateTime.

dotnetCarpenter commented 2 years ago

On second thought, Temporal.PlainTime.test (String) can easily be coded in regex. But omitting that, would break polymorphism for Temporal objects.