Reputation: 1046
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:
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 }
;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.
Upvotes: 6
Views: 2516
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
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