Search code examples
javascriptdatetimeutcecmascript-temporal

How to create instance of `Temporal.Instant` at specific date and time?


I’m struggling with JavaScript’s proposed new Temporal API. What I am trying to do should be straight-forward, yet I fail to find a convincing solution. I must be missing something.

The task is as follows: instantiate an object representation of an UTC datetime from variables for year, month, day, hour and minute.

My thinking is as follows:

  • we are talking UTC so I need a Temporal.Instant;
  • new Temporal.Instant() requires the timestamp in nanoseconds so that doesn’t work;
  • Temporal.Instant.from() requires a ISO datetime string, which would require me to generate a properly formatted piece of text from the five variables I have — this is possible but a bit of a hack and kinda defeating the purpose of using a datetime library;
  • Temporal.PlainDateTime.from() has the right design, as it accepts an object like { year, month, day, hour, minute };
  • so then all we need to do is creating an Instant from this PlainDateTime. This does not seem to be possible though? Other than through — once again — a datetime string or a timestamp in ns…?

This is silly! The use case here is super basic, and yet it’s not obvious (to me) at all how to address it.

I was expecting to be able to simply do something like: Temporal.Instant.from({ year, month, day, hour, minute });

Now the best I can come up with is: Temporal.Instant.from(year + '-' + String(month).padStart(2, '0') + '-' + String(day).padStart(2, '0') + 'T' + String(hour).padStart(2, '0') + ':' + String(minute).padStart(2, '0') + 'Z'); // 😱

Please tell me I’m bigtime overlooking something.


Solution

  • Your PlainDateTime represents "a calendar date and wall-clock time that does not carry time zone information". To convert it to an exact time, you need to supply a timezone, using the toZonedDateTime method. By then ignoring calendar and timezone via the toInstant method, you can get the desired Instant instance.

    So there's a few ways to achieve this:

    • Create a PlainDateTime from an object, convert it to an instant by assuming UTC timezone:

      Temporal.PlainDateTime.from({year, month, day, hour, minute}).toZonedDateTime("UTC").toInstant()
      
    • Create a PlainDateTime using the constructor, convert it to an instant by assuming UTC timezone:

      new Temporal.PlainDateTime(year, month, day, hour, minute).toZonedDateTime("UTC").toInstant()
      
    • Create a ZonedDateTime directly from an object, providing the timezone in there, then convert it:

      Temporal.ZonedDateTime.from({timeZone: 'UTC', year, month, day, hour, minute}).toInstant()
      
    • Instead of going via a zoned datetime, you can also get the instant that a TimeZone instance ascribes to a PlainDateTime object:

      Temporal.TimeZone.from("UTC").getInstantFor(Temporal.PlainDateTime.from({year, month, day, hour, minute}))
      new Temporal.TimeZone("UTC").getInstantFor(new Temporal.PlainDateTime(year, month, day, hour, minute))
      
    • If you wanted to hardcode the instant in your code, you could also directly create it from an ISO string:

      Temporal.Instant.from("2022-10-23T02:50Z")
      
    • If you are open to including the old Date methods, you could also use Date.UTC to compute the millisecond value for the instant - beware zero-based months:

      Temporal.Instant.fromEpochMilliseconds(Date.UTC(year, month-1, day, hour, minute));
      

    Try them for yourselves with your particular example:

    const year = 2022;
    const month = 10;
    const day = 23;
    const hour = 2;
    const minute = 50;
    
    log(Temporal.PlainDateTime.from({year, month, day, hour, minute}).toZonedDateTime("UTC").toInstant());
    log(new Temporal.PlainDateTime(year, month, day, hour, minute).toZonedDateTime("UTC").toInstant());
    log(Temporal.ZonedDateTime.from({timeZone: 'UTC', year, month, day, hour, minute}).toInstant());
    log(Temporal.TimeZone.from("UTC").getInstantFor(Temporal.PlainDateTime.from({year, month, day, hour, minute})));
    log(new Temporal.TimeZone("UTC").getInstantFor(new Temporal.PlainDateTime(year, month, day, hour, minute)));
    log(Temporal.Instant.from("2022-10-23T02:50Z"));
    log(Temporal.Instant.fromEpochMilliseconds(Date.UTC(year, month-1, day, hour, minute)));
    <script src="https://tc39.es/proposal-temporal/docs/playground.js"></script>
    <script>function log(instant) { console.log(instant.epochSeconds); }</script>