Javascript Date, setFullYear changes timezone - javascript

Javascript Dates work with timezones. If I create a date, it sets the timezone. When I update the year of that date, I don't expect the timezone to change. It does, however. The worst thing is that it changes the timezone but not the time, causing the actual time to shift by one hour!
This causes the issue that if I have the person's birth date, and want to know his birthday this year, I cannot simply set the year to the current year using birthdate.setFullYear(2018), because it will return the birthday minus one hour. That means that it occurs one day before the actual birthday, at eleven 'o clock.
let test = new Date('1990-10-20');
console.log(test);
console.log(test.toISOString().substring(0, 10));
// 1990-10-20 ( Sat Oct 20 1990 01:00:00 GMT+0100 (Central European Standard Time) )
test.setFullYear(2000);
console.log(test);
console.log(test.toISOString().substring(0, 10));
// 2000-10-19 ( Fri Oct 20 2000 01:00:00 GMT+0200 (Central European Summer Time) === one hour too soon!! )
It might be that your timezone does not reproduce, here is my output:
"1990-10-20T00:00:00.000Z"
1990-10-20
"2000-10-19T23:00:00.000Z"
2000-10-19
The only workaround I found is substring the date and replace it as string values. How can I do it better using the Date object?

Hm, maybe it would be better to use setUTCFullYear(...) (MDN Docs) instead? At least in that case, it won't mess up the time.
let test = new Date('1990-10-20');
console.log(test);
console.log(test.toISOString().substring(0, 10));
// "1990-10-20T00:00:00.000Z")
test.setUTCFullYear(2000);
console.log(test);
console.log(test.toISOString().substring(0, 10));
// "2000-10-20T00:00:00.000Z"

BEWARE THE TIMEZONE If you want to work with just-a-date using the date object (and you more or less have to) your date objects should represent UTC midnight at the start of the date in question. In your case, to indicate a year, use UTC midnight at the start of the 1st January. This is a common and necessary convention, but it requires a lot of attention to make sure that the timezone doesn't creep back in, as you've discovered.
When you say "javascript dates work with timezones", a javascript date is a moment in time (ticks since the epoch) with handy static functions for converting that moment into a meaningful string in the local timezone (or a specified timezone). The date object itself does not have a timezone property.
So, you can create your UTC midnight year with something like...
var myDate = new Date(Date.UTC(2018,0,1)); // months are zero-indexed !
Then serialize it specifying UTC...
myDate.toISOString();
myDate.toLocaleDateString("en",{timezone:"UTC"});

Related

Creating Date by iso ''0001-01-01T01:00:00' adding 16 second

new Date("0001-01-01T01:00:00Z") --> Mon Jan 01 0001 02:50:16 GMT+0150 (Moscow Standard Time)
Incorrect GMT: my timezone GMT+3000, but date creates GMT+0150
For dates, you can (and should, in my opinion) define them in UTC ISO 8601 "Z" format ("YYYY-MM-DDTHH:MM:SSZ"), just as you did.
However, to get a user-friendly string representation of those dates, it will depend on your client and on the Javascript engine used. You can constrain the output if you explicitly specify a reference timezone with toLocaleString().
var date = new Date("1990-01-01T01:00:00Z");
console.log(date.toLocaleString("en-US", {timeZone: "Asia/Jerusalem"}));
console.log(date.toLocaleString("en-US", {timeZone: "Europe/Moscow"}));
console.log(date.toLocaleString("en-US", {timeZone: "Africa/Djibouti"}));
// output on my machine, should be the same on yours :
// 1/1/1990, 3:00:00 AM
// 1/1/1990, 4:00:00 AM
// 1/1/1990, 4:00:00 AM
console.log(date.toString());
// output **on my machine**, should **not** be the same on yours
// Mon Jan 01 1990 02:00:00 GMT+0100 (Central European Standard Time)
For the 16 seconds issue, this is linked to the way offset are defined by the rules for those dates before the notion of IANA timezone existed.
They probably don't make sense in your application, and I discourage you to use dates like 1st January of year 0001 for your examples.
Examples :
var date = new Date("0001-01-01T01:00:00Z");
console.log(date.toLocaleString("en-US", {timeZone: "Asia/Jerusalem"}));
console.log(date.toLocaleString("en-US", {timeZone: "Europe/Moscow"}));
console.log(date.toLocaleString("en-US", {timeZone: "Africa/Djibouti"}));
// output on my machine, should be the same on yours :
// 1/1/1, 3:20:54 AM
// 1/1/1, 3:30:17 AM
// 1/1/1, 3:27:16 AM
console.log(date.toString());
// output **on my machine**, should **not** be the same on yours
// Mon Jan 01 0001 01:09:21 GMT+0009 (Central European Standard Time)
More information here (thanks Johan Karlsson for the link) :
https://bugs.chromium.org/p/chromium/issues/detail?id=849404
The most relevant comment from this link is, I think :
This is working as intended and working per spec. The spec says that
we have to follow the IANA timezone database.
In 1880, there's no standard timezone and America/Los_Angeles timezone
offset was based on its longitude. The same is true of other
timezones.
Also note that there are many timezone around the world the zone
offset (and whether or not to have DST or when to start DST) have
changed multiple times even since 2000 (e.g. Europe/Moscow). The
change to make them work correctly also brought in what's reported
here.
Pac0's answer is correct (and you should accept that answer since it came first, not this one). But just to provide a detailed explanation:
Dates from before recorded history of time zones are marked in the time zone database as LMT - which stands for Local Mean Time. The offsets are derived from the latitude and longitude of the city, not by any current political determination.
Since the offset shown is 1:50:16 ahead of UTC, I can derive that your system time zone is Europe/Minsk. This is seen in the tzdb sources here:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Minsk 1:50:16 - LMT 1880
This is just the first line of the zone entry for Europe/Minsk, which says that until 1880, use the LMT entry of UTC+1:50:16.
As to why it says "Moscow Standard Time" - that string comes from the Unicode CLDR data, which in the /common/supplemental/metaZones.xml file we can see:
<timezone type="Europe/Minsk">
<usesMetazone to="1991-03-30 23:00" mzone="Moscow"/>
<usesMetazone to="2011-03-27 00:00" from="1991-03-30 23:00" mzone="Europe_Eastern"/>
<usesMetazone to="2014-10-26 22:00" from="2011-03-27 00:00" mzone="Europe_Further_Eastern"/>
<usesMetazone from="2014-10-26 22:00" mzone="Moscow"/>
</timezone>
So Europe/Minsk uses the Moscow metazone up until 1991. Then, using one of the language files such as /common/main/en.xml for English, we can see the actual text assigned to this metazone:
<metazone type="Moscow">
<long>
<generic>Moscow Time</generic>
<standard>Moscow Standard Time</standard>
<daylight>Moscow Summer Time</daylight>
</long>
</metazone>
And now you have a complete picture for how the string Mon Jan 01 0001 02:50:16 GMT+0150 (Moscow Standard Time) was derived from 0001-01-01T01:00:00Z.
A timezone is an offset plus a date range. To format your date, javascript wants to know what the timezone offset was for Moscow in the year zero. This is hard information to come by, and might not be accurate ! You think you're asking something simple, but it's actually pretty extreme. If you want to use the date object to represent durations, you should take the epoch as your starting point.

HTML5: hour from "Date()" object in <input type='time'> [duplicate]

I send this date from my controller in java (Spring-MVC) the type in mysql is datetime
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "..") public Date getYy() {
return this.yy;
}
as : [2015-09-30 00:00:00.0]
When i get this dates with ajax as 1443567600000 :
new Date(1443567600000) convert to Tue Sep 29 2015 23:00:00 GMT+0000 (Maroc)
So why i get wrong date off by one hour?
SOLUTION
We resolve it by
d = new Date(value) ;
d.setTime( d.getTime() - new Date().getTimezoneOffset()*60*1000 );
because it was Daylight saving time (DST) or summer time problem. good article
This JS handling of Date is a quite a head-flip.
I'm in the UK... "easy" because we're on GMT (UTC)... except during the summer months, when there's DST (British Summer Time, BST). Clocks go forward in summer and back in winter (stupidly by the way, but that's another issue!) by one hour. One day in March what is 4pm GMT is now called 5pm (BST).
summer month:
If you do new Date( '2017-08-08' ) this will give you (toString) 'Date 2017-08-08T00:00:00.000Z'.
If you do new Date( '2017-08-08 00:00' ), however, this will give you 'Date 2017-08-07T23:00:00.000Z'!
In the second case it appears JS is trying to be "helpful" by assuming that because you stipulated the hour you were specifying BST time. So it adjusts to GMT/UTC. Otherwise it doesn't... though (of course) it still produces a Date object which is specific right down to the milliseconds. Quite a gotcha!
Confirmation: a winter month... when BST is not applied:
new Date( '2018-01-01 00:00' )/ new Date( '2018-01-01' ): both give 'Date 2018-01-01T00:00:00.000Z'
As for adjusting, it appears that to undo the automatic adjustment you just go
jsDate.setTime( jsDate.getTime() + jsDate.getTimezoneOffset() * 60 * 1000 );
... this is a bit like what Youssef has written... except you have to obtain the offset from the specific Date in question... and my experiments seem to prove that you add this, not subtract it.
Really it would be much more helpful if the default string representation of JS Date gave the UTC time followed by the TZ (time zone) offset information, as with some of the ISO date formats: clearly, this information is included.
I think maybe this is a Daylight Saving Time problem. You can check your client's timezone, and your server's timezone. (web server or SQL Server)
We should probably need more data about it, but it could be that nothing is wrong here, it depends how you set and get back your date.
Basically 1443567600000 doesn't contains timezone. It represent Tue Sep 29 2015 23:00:00 from Greenwich. It's a moment in time that, of course, it different from any location that has a different timezone. The same moment, happens at different time (the midnight of GMT+1 is the 11pm of GMT).
You have to store both the time and the timezone in your DB, and possibly send back to JS, otherwise it will be always interpreted differently depends by the local time of the client.
To make an example:
var d = new Date(2015, 8, 30);
console.log(d.toJSON()); // In my case I got "2015-09-29T22:00:00.000Z"
console.log(d.toDateString()); // "Wed Sep 30 2015"
To be more specific
time = new Date("2018-06-01 " + time);
var offset = time.getTimezoneOffset();
offset = Math.abs(offset / 60);
time.setHours(time.getHours() + offset);
in this case time is equal to hours with leading zeros:minutes with leading zeros.
This will add the hours difference to the UTC.
If you pass a string to date it is treated as UTC.

Javascript new Date decreases date by a day

I have a scenario where I am using AngularJS to read date. Interestingly it decreases my date value by one.
Why is this happening?
new Date("2016-01-06T00:00:00")
give me result as
Tue Jan 05 2016 16:00:00 GMT-0800 (Pacific Standard Time)
This is because when you use the new Date() in the JavaScript, it converts and prints the date in browsers timezone.
So if you print:
new Date("2016-01-06T00:00:00-0800")
You will get the actual output you want, because of the -0800 difference between your time zone (determined by the browser) and the UTC time.
The UTC time zone is used to interpret arguments in ISO 8601 format that do not contain time zone information (note that ECMAScript 2015 specifies that date time strings without a time zone are to be treated as local, not UTC).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
Since your date string appears to lack one, JS assumed it's UTC time. The result you see is the same moment in time, offset to your timezone. All you need to do is provide timezone data to the string you're parsing.
It is because the date is taking your browser's timezone in to account, which in your case is in PST. It does the same to me, in EST:
test = new Date("2016-01-06T00:00:00")
Tue Jan 05 2016 19:00:00 GMT-0500 (EST)
You can still obtain the time in UTC by using any of the .getUTC* functions, like so:
test.getUTCDate();
6

Javascript Date() give wrong date off by one hour

I send this date from my controller in java (Spring-MVC) the type in mysql is datetime
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "..") public Date getYy() {
return this.yy;
}
as : [2015-09-30 00:00:00.0]
When i get this dates with ajax as 1443567600000 :
new Date(1443567600000) convert to Tue Sep 29 2015 23:00:00 GMT+0000 (Maroc)
So why i get wrong date off by one hour?
SOLUTION
We resolve it by
d = new Date(value) ;
d.setTime( d.getTime() - new Date().getTimezoneOffset()*60*1000 );
because it was Daylight saving time (DST) or summer time problem. good article
This JS handling of Date is a quite a head-flip.
I'm in the UK... "easy" because we're on GMT (UTC)... except during the summer months, when there's DST (British Summer Time, BST). Clocks go forward in summer and back in winter (stupidly by the way, but that's another issue!) by one hour. One day in March what is 4pm GMT is now called 5pm (BST).
summer month:
If you do new Date( '2017-08-08' ) this will give you (toString) 'Date 2017-08-08T00:00:00.000Z'.
If you do new Date( '2017-08-08 00:00' ), however, this will give you 'Date 2017-08-07T23:00:00.000Z'!
In the second case it appears JS is trying to be "helpful" by assuming that because you stipulated the hour you were specifying BST time. So it adjusts to GMT/UTC. Otherwise it doesn't... though (of course) it still produces a Date object which is specific right down to the milliseconds. Quite a gotcha!
Confirmation: a winter month... when BST is not applied:
new Date( '2018-01-01 00:00' )/ new Date( '2018-01-01' ): both give 'Date 2018-01-01T00:00:00.000Z'
As for adjusting, it appears that to undo the automatic adjustment you just go
jsDate.setTime( jsDate.getTime() + jsDate.getTimezoneOffset() * 60 * 1000 );
... this is a bit like what Youssef has written... except you have to obtain the offset from the specific Date in question... and my experiments seem to prove that you add this, not subtract it.
Really it would be much more helpful if the default string representation of JS Date gave the UTC time followed by the TZ (time zone) offset information, as with some of the ISO date formats: clearly, this information is included.
I think maybe this is a Daylight Saving Time problem. You can check your client's timezone, and your server's timezone. (web server or SQL Server)
We should probably need more data about it, but it could be that nothing is wrong here, it depends how you set and get back your date.
Basically 1443567600000 doesn't contains timezone. It represent Tue Sep 29 2015 23:00:00 from Greenwich. It's a moment in time that, of course, it different from any location that has a different timezone. The same moment, happens at different time (the midnight of GMT+1 is the 11pm of GMT).
You have to store both the time and the timezone in your DB, and possibly send back to JS, otherwise it will be always interpreted differently depends by the local time of the client.
To make an example:
var d = new Date(2015, 8, 30);
console.log(d.toJSON()); // In my case I got "2015-09-29T22:00:00.000Z"
console.log(d.toDateString()); // "Wed Sep 30 2015"
To be more specific
time = new Date("2018-06-01 " + time);
var offset = time.getTimezoneOffset();
offset = Math.abs(offset / 60);
time.setHours(time.getHours() + offset);
in this case time is equal to hours with leading zeros:minutes with leading zeros.
This will add the hours difference to the UTC.
If you pass a string to date it is treated as UTC.

toDateString() decrements my date

For the following code:
var d = new Date("2013-07-01");
console.log(d.toDateString());
It outputs Sun Jun 30 2013, which is one day less of what was input. What happened to the object? What date is actually stored?
The date is parsed as UTC date, but the toDateString() outputs what that time is in the local timezone.
Try this instead
var d = new Date(2013, 6, 1); //6 instead of 7 because the mont argument is zero-based
console.log(d.toDateString());
What date is actually stored?
2013-07-01 00:00:00 UTC
Parsing a Date from a string can yield different results depending on what browser/runtime you are using. There is a chart here that lists some of the browsers and what they support.
But in general, a string like "2013-07-01" is treated as if it were at midnight in UTC.
If you wanted to parse it in local time, you could use "2013/07/01". But that's a bit strange. You might be tempted to use "07/01/2013", but that might be interpreted as either Jan 7th or July 1st. Any of these are still implementation specific.
If you want a general purpose, "works everywhere" solution, you have a few different options:
Option 1
Don't parse it. Use the constructor that accept integers.
var d = new Date(2013,6,1); // watch out for the zero-based month
console.log(d.toDateString());
// output: "Mon Jul 01 2013"
With this code, the actual UTC time stored is going to vary depending on your own time zone. But since .toDateString() also takes that time into account, then you should be ok.
Option 2
Parse it as UTC, and display it as UTC/GMT:
var d = new Date("2013-07-01");
console.log(d.toUTCString());
// output: "Mon, 01 Jul 2013 00:00:00 GMT"
But this output probably has more than you were looking for, and it may still look different in various browsers.
Option 3
Use the moment.js library, where you can control the formatting exactly however you desire.
var m = moment("2013-07-01", "YYYY-MM-DD"); // explicit input format
console.log(m.format("ddd MMM DD YYYY")); // explicit output format
// output: "Mon Jul 01 2013"
IMHO, this is the best solution. But it does require you use a library.
Option 4
This approach works pretty good and doesn't require 3rd party libraries.
var parts ='2013-07-01'.split('-');
var mydate = new Date(Date.UTC(parts[0],parts[1]-1,parts[2])); //please put attention to the month
You can simply append your timezone before parsing as well.
var d = new Date("2013-07-01" + "T00:00:00.000-05:00");
console.log(d.toDateString());

Categories