Javascript - force new Date constructor to treat argument as UTC - javascript

Have an API end point that accepts a date and does some processing. I do give via postman the date as UTC (denoted by the Z at the end). Sample input sent from Postman.
{
"experimentDate":"2022-01-12T12:30:00.677Z",
}
In the code when I do
let startDate = new Date(experimentDate);
//other calculations e.g get midnight of the startDate
startDate.setHours(0,0,0,0);
The first assignment sets startDate corrected to the current timezone. The rest of my calculations go bad as a result of this. For instance when I use the setHours function setting time to 0, I expect it to be at midnight of the UTC time given but it goes to midnight of my current timezone.
Should new Date not keep the date in UTC given that there is a Z at the end of the date?
Should I reconvert it to UTC like below. Is this not redundant?
Date.UTC(startDate.getUTCFullYear(), startDate.getUTCMonth(),
startDate.getUTCDate(), startDate.getUTCHours(),
startDate.getUTCMinutes(), startDate.getUTCSeconds())
What is the right way to achieve this?

The Date object will be stored as a UTC date, however there are different methods on it that will set/get the date or time for both UTC and local timezones. Try using .setUTCHours(), rather than .setHours().

You can use the Date constructor to parse the timestamp provided.
Most of the methods will treat the date as a local time. For example, the getHours() method returns the hour for the specified date, according to local time.
However you can use the getUTCXXX() methods to get the UTC date components such as year, month, date, hour etc.
You can also use Date.toISOString() to get the date formatted as UTC.
You can use the Date.UTC method to get UTC midnight, passing in the relevant getUTCFullYear(), getUTCMonth(), getUTCDay() etc. from the experiment date.
This can then be passed to the Date constructor.
let timestamp = "2022-01-12T12:30:00.677Z";
const experimentDate = new Date(timestamp);
const midnightUTC = new Date(Date.UTC(experimentDate.getUTCFullYear(), experimentDate.getUTCMonth(), experimentDate.getUTCDate()))
console.log('Experiment date (UTC): ', experimentDate.toISOString());
console.log('Midnight (UTC): ', midnightUTC.toISOString());
You can also use Date.setUTCHours() to do the same thing.
let timestamp = "2022-01-12T12:30:00.677Z";
const experimentDate = new Date(timestamp);
const midnightUTC = new Date(experimentDate);
midnightUTC.setUTCHours(0,0,0,0);
console.log('Experiment date (UTC): ', experimentDate.toISOString());
console.log('Midnight (UTC): ', midnightUTC.toISOString());

Related

JS setDate() not working as expected when Date is created with new Date('YYYY-MM-DD')

let a = new Date()
a.setDate(1) <--- this set date to the first date of this month.
However
let b = new Date ('2020-02-15')
b.setDate(1)
b.toISOString() -> returns'2020-02-02T00:00:00.000Z'
What's going on?
Timezones. The difference comes from the fact that you're initializing date a with the current time, and b at midnight local time, which (depending on the time of day where you are) can be a different day when represented in UTC. setDate() sets the "day" part of the Date object based on your locale.
let a = new Date()
a.setDate(1);
let b = new Date('2022-02-14');
b.setDate(1);
console.log("UTC:")
console.log(a.toISOString());
console.log(b.toISOString());
console.log('Local timezone:')
console.log(a.toLocaleString())
console.log(b.toLocaleString())
The above code will stop proving its point in about a month, so for the benefit of People Of The Futureā„¢ here is the current output:
UTC:
2022-02-01T17:11:37.114Z
2022-02-02T00:00:00.000Z
Local timezone:
2/1/2022, 12:11:37 PM
2/1/2022, 7:00:00 PM
I think it have something to do with your TimeZone and Time Settings
date.toISOString() always displays the date as
YYYY-MM-DDTHH:mm:ss.sssZ
Where Z means UTC+0 TimeZone
try adding the the time and the TimeZone when constructing the date
something like
let date = new Date('2020-02-12T00:00:00.000+02:00');

Creating timezone independent dates in Javascript?

This question is related to this question.
So if we construct a date using an ISO string like this:
new Date("2000-01-01")
Depending on what timezone we are in, we might get a different year and day.
I need to be able to construct dates in Javascript that that always have the correct year, day, and month indicated in a string like 2000-01-01, and based on the answer in one of the questions if we use back slashes instead like this:
const d = new Date("2000/01/01")
Then we will always get the right year, day, and month when using the corresponding date API methods like this:
d2.getDate();
d2.getDay();
d2.getMonth();
d2.getFullYear();
So I just wanted to verify that my understanding is correct?
Ultimately I need to be able to create Date instances like this for example:
const d3 = new Date('2010/01/01');
d3.setHours(0, 0, 0, 0);
And the time components should always be zero, and the year, month, and day should be the numbers specified in the string.
Thoughts?
I just did a quick test with this:
https://stackblitz.com/edit/typescript-eztrai
const date = new Date('2000/01/01');
console.log(`The day is ${date.getDate()}`);
const date1 = new Date('2000-01-01');
console.log(`The day is ${date1.getDate()}`);
And it logs this:
The day is 1
The day is 31
So it seems like using backslashes should work ...
Or perhaps using the year, month (0 based index), and day constructor values like this:
const date3 = new Date(2000, 0, 1);
date3.setHours(0, 0, 0, 0);
console.log(`The day is ${date3.getDate()}`);
console.log(`The date string is ${date3.toDateString()}`);
console.log(`The ISO string is ${date3.toISOString()}`);
console.log(`Get month ${date3.getMonth()} `);
console.log(`Get year ${date3.getFullYear()} `);
console.log(`Get day ${date3.getDate()} `);
NOTE
Runar mentioned something really important in the accepted answer comments. To get consistent results when using the Javascript Date API use methods like getUTCDate(). Which will give us 1 if the date string is 2000-01-01. The getDate() method could give us a different number ...
From the ECMA standard of the Date.parse method:
When the UTC offset representation is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time.
What is happening is that New Date() implicitly calls Date.parse on the string. The "2000-01-01" version conforms to a Date Time String Format with a missing offset representation, so it is assumed you mean UTC.
When you use "2000/01/01" as input the standard has this to say:
If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
So in short the browser can do what they want. And in your case it assumes you mean the offset of the local time, so whichever offset you are located in gets added when you convert to UTC.
For consistent results, perhaps you want to take a look at Date.UTC
new Date(Date.UTC(2000, 0, 1))
If you need to pass in an ISO string make sure you include the time offset of +00:00 (is often abbreviated with z)
new Date("2000-01-01T00:00:00Z");
If you want to later set the date to something different, use an equivalent UTC setter method (e.g. setUTCHours).
When you retrieve the date, also make sure to use the UTC getter methods (e.g. getUTCMonth).
const date = new Date("2000-01-01T00:00:00Z");
console.log(date.getUTCDate());
console.log(date.getUTCMonth());
console.log(date.getUTCFullYear());
If you want to retrieve the date in a specific format you can take a look at Intl.DatTimeFormat, just remember to pass in timeZone: "UTC" to the options.
const date = new Date("2000-01-01T00:00:00Z");
const dateTimeFormat =
new Intl.DateTimeFormat("en-GB", { timeZone: "UTC" });
console.log(dateTimeFormat.format(date));

How to obtain a local timezone Date from a date string in javascript?

I am building an online store, most of my customers (basically all) are located in a given timezone, but my infrastructure is located in other timezone (we can assume it's UTC). I have the option for my clients to select a date for their orders, the problem is that my date component represents dates like this "YYYY-MM-DD". In am using the Date constructor like this:
let dateString = "2019-06-03"
let date = new Date(dateString)
console.log(date) //This will print the local time zone representation of my dateString
console.log(date.toISOString()) //This will print the utc equivalent of my dateString
The problem with this is that I want the UTC representation to be calculated from the local timezone, not the other way around. Let's suppose in am located in GMT-5, when I say let date = new Date("2019-06-06") I want to see "2019-06-03T00:00:00.000 GMT-5" , and the ISOString should be "2019-06-03T05:00:00.000Z". How can I do this ?
What you are trying to achieve can be done by appending the string T00:00:00 to the dateString before passing it to the Date() constructor.
But a word of caution, manipulating the timezone/offsets manually like this might result in incorrect data being presented.
If you are storing and retrieving all the order timestamps in UTC only, it will avoid timezone related issues and you might not need to process the timestamps like this.
let dateString = "2019-06-03"
let date = new Date(dateString + "T00:00:00")
console.log(date) //This will print the local time zone representation of my dateString
console.log(date.toISOString()) //This will print the utc equivalent of my dateString

offset time zone problem with toIsoString : does not convert well the date object

I'm trying to set a date that take the begin of year 2020 with javascript and convert it to isoString; the problem is that always I got a date like this : like "2019-12-31T23:00:00.000Z" !!
start: Date = new Date(new Date().getFullYear(), 0, 1);
how to set the date with this format : "2020-01-01T23:00:01.000Z"
The date produced by new Date() makes an allowance for the host timezone offset, the toISOString method is UTC so unless the host is set to UTC, there will be a difference equal to the host timezone offset. In the OP that's +01:00.
If you want the start of the year UTC, then you have to first generate a time value as UTC, then use that for the date:
// Get the current UTC year
let year = new Date().getUTCFullYear();
// Create a Date avoiding the local offset
let d = new Date(Date.UTC(year, 0, 1));
// Get a UTC timestamp
console.log(d.toISOString());
If, on the otherhand, you want an ISO 8601 timestamp with the local offset rather than UTC, something like 2020-01-01T00:00:00.000+02:00, you'll have to do that yourself or use a library (per this, thanks to Matt JP), see How to format a JavaScript date.

Displaying timezone-formatted date as UTC time

on my UI, I try to display a date based on a specific timezone. In this example, I will use Americas/New_York as the timezone. This is how I did it.
$scope.getStartTime = function(){
var date = new Date();
return moment(date).tz("Americas/New_York").format('YYYY-MM-DD HH:mm:ss');
};
Afterwards, I want to send this data and send it to my server. In my server however, I want it so that it is always serialized into UTC time instead of in the New York Timezone (EST).
For example, if the time was 12:00 P.M. in New York, then the time would be serialized to 4:00 P.M. in UTC time before it was sent to the backend. This was my attempt:
var date = getStartTime();
....
// Display the date in the UI
....
$scope.revertStartTime(date);
$scope.revertStartTime = function(startTime) {
console.log("Start time: ", startTime);
console.log("Moment: ", moment(startTime).format());
console.log("Converted to utc time: ", moment().utc(startTime).format());
return moment.utc(startTime).format("YYYY-MM-DD'T'HH:mm:ss.SSSZ");
}
I tried to revert the start time by using the moment().utc() function and hoped that the date would change to a UTC based date but unfortunately it keeps turning my date into the localized date instead of UTC date and I'm not sure why. Any help would be appreciated. Thanks!
Edit:
Tried to follow the below method and here is what I did:
$scope.getStartTime = function(){
var date = new Date();
var startTime = new moment(date).tz($rootScope.userinfo.timeZone).format('YYYY-MM-DD HH:mm:ss');
$rootScope.offset = moment().utcOffset(startTime);
console.log("offset: ", $rootScope.offset);
return startTime;
};
$scope.revertStartTime = function(startTime) {
console.log("User Selected Time: ", moment().utcOffset(startTime).format('YYYY-MM-DD HH:mm:ss'));
return moment().utcOffset(startTime).format('YYYY-MM-DD HH:mm:ss');
}
But all I get is an error saying that revertStartTime returns an Invalid Date.
A few things:
Hoping it's a typo, but just to point out, the zone ID is America/New_York, not Americas/New_York.
You can pass a value as moment.utc(foo), or moment(foo).utc(), but not moment().utc(foo). The difference is that one interprets the input as UTC and stays in UTC mode, while they other just switches to UTC mode. You can also think of this as "converting to UTC", but really the underlying timestamp value doesn't change.
Yes, you can switch to UTC mode and call format, but you can also just call .toISOString() regardless of what mode you're in. That's already in the ISO format you're looking for.
Note that if you start with a unique point in time, and you end with converting to UTC, no amount of switching time zones or offsets in the middle will change the result. In other words, these are all equivalent:
moment().toISOString()
moment.utc().toISOString()
moment(new Date()).toISOString()
moment.utc(new Date()).toISOString()
moment(new Date()).utc().toISOString()
moment().tz('America/New_York').toISOString()
moment.tz('America/New_York').toISOString()
moment().utcOffset(1234).toISOString()
moment.utc().format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]')
moment().utc().format('YYYY-MM-DD[T]HH:mm:ss.SSS[Z]')
Only the last two even need to be in UTC mode, because the format function would produce different output if in local mode or in a particular time zone.
In order to accomplish this you'd want to use .utcOffset(). It is the preferred method as of Moment 2.9.0. This function uses the real offset from UTC, not the reverse offset (e.g., -240 for New York during DST). Offset strings like "+0400" work the same as before:
// always "2013-05-23 00:55"
moment(1369266934311).utcOffset(60).format('YYYY-MM-DD HH:mm')
moment(1369266934311).utcOffset('+0100').format('YYYY-MM-DD HH:mm')
The older .zone() as a setter was deprecated in Moment.js 2.9.0. It accepted a string containing a timezone identifier (e.g., "-0400" or "-04:00" for -4 hours) or a number representing minutes behind UTC (e.g., 240 for New York during DST).
// always "2013-05-23 00:55"
moment(1369266934311).zone(-60).format('YYYY-MM-DD HH:mm')
moment(1369266934311).zone('+0100').format('YYYY-MM-DD HH:mm')
To work with named timezones instead of numeric offsets, include Moment Timezone and use .tz() instead:
// determines the correct offset for America/Phoenix at the given moment
// always "2013-05-22 16:55"
moment(1369266934311).tz('America/Phoenix').format('YYYY-MM-DD HH:mm')

Categories