Tim Molendijk
Tim Molendijk

Reputation: 1046

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:

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.

Upvotes: 6

Views: 2516

Answers (2)

RobG
RobG

Reputation: 147383

The Date API will continue to exist forever and is pretty handy so it seems sensible to use it if it helps.

Date.UTC returns the number of milliseconds since the ECMAScript epoch. Temporal.instant requires nanoseconds, so given an input object with {year, month, day, etc.} the only hassle is to deal with the nanoseconds using BigInt, which is not hard:

// Object for 7 December 2022
let d = {
  year: 2022,
  month: 12,
  day: 7,
  hour: 3,
  minute: 24,
  second: 30,
  millisecond: 10,
  microsecond: 3,
  nanosecond: 500
}

// Convert to time value - nanoseconds since ECMAScript eopch
let timeValue = BigInt(Date.UTC(d.year, d.month-1, d.day, 
  d.hour, d.minute, d.second, d.millisecond)) * BigInt(1e6) +
  BigInt(d.microsecond * 1e3) + BigInt(d.nanosecond);

// 1670383470010003500
console.log(timeValue.toString());

// 2023-01-07T03:24:30.010Z
console.log(new Date(Number(timeValue/BigInt(1e6))).toISOString());

An instant can then be created as:

let instant = new Temporal.Instant(timeValue);

Upvotes: 0

Bergi
Bergi

Reputation: 664494

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>

Upvotes: 7

Related Questions