momentjs toDate - different output on different clients/browsers - javascript

I use momentjs to parse a Date String and convert it to a native JavaScript Date:
let dateString = '1980-04-06';
console.log(moment().utcOffset());
console.log(moment(dateString, 'YYYY-MM-DD').toDate());
<script src="https://cdn.jsdelivr.net/npm/moment#2.22.2/moment.min.js"></script>
The output on client 1(Firefox 62) is
120
Date 1980 - 04 - 05 T23: 00: 00.000 Z
and the output on client 2(Firefox 52 ESR) is
120
Date 1980 - 04 - 05 T22: 00: 00.000 Z
Can somebody explain me, why the utcOffset is the same (new Date().getTimezoneOffset() prints also -120 on both clients), but the Date (hour) is different?

You're checking the current UTC offset, not the offset of your 1980 moment instance. My guess is that if you took moment(dateString, 'YYYY-MM-DD') and called utcOffset on that, you'd get different offsets on the different browsers.
I bet what's happening is that the rules for your zone have changed since 1980 (for example, perhaps the timing of the DST has changed, or DST has been added or eliminated, or perhaps the standard offset has even changed). Browsers vary in the degree to which they get historical zone data right, which leads to errors interpreting date strings. I suspect that Firefox fixed their historical zone database for your zone, leading to different behavior in newer versions of the browser.

The offsets you're showing are for the current date and time, not for the date provided. If you change the middle line to log moment(dateString, 'YYYY-MM-DD').utcOffset(), you should see that the result in the older Firefox 52 is 60 instead of 120.
Contributing factors explaining this difference are:
The daylight saving time rules for your time zone are not the same in 1980 as they are today. Assuming Vienna (from your user profile), in 1980 the start of DST was at 00:00 on April 6th (reference here) which is the first Sunday in April. The current (2018) rule for Vienna is the last Sunday in March, which would be March 25th 2018 (reference here).
ECMAScript 5.1 (section 15.9.1.8) and earlier required browsers to always assume the current DST rule was in effect for all time - even if this was not actually what happened. This was corrected in ECMAScript 6 / 2015 (section 20.3.1.8) .
ECMAScript 2015 was implemented in Firefox starting with version 54. Since you are testing version 52, you are seeing the old behavior.
Since this particular DST change is right at the stroke of midnight, and it's a spring-forward transition, then, 1980-04-06T00:00 is not valid. The first moment of that day in that time zone is 1980-04-06T01:00. Moment takes care of this for you when you pass a date-only value. In the current browser (62, not 52), If you call .format() on the moment, you should see 1980-04-06T01:00:00+02:00. Note that this is time is already in DST, with a UTC+02:00 offset. Converted to UTC is 1980-04-05T23:00:00Z, thus aligning with the correct data as seen in your examples.
Long story short, there are many reasons to use up-to-date browsers. This is one of them.

Related

Incorrect timezone adjustment in moment.js and spacetime

My server is sending my Javascript client a timestamp in UTC. I'm currently in Mountain Time which is currently in daylight savings (GMT-7), but any timezone adjustment I do is only applying -6 offset.
To confirm that javascript is even aware of my timezone, I did the following:
console.log(Date().toString()); which outputs the following: Mon Nov 19 2018 12:13:28 GMT-0700 (Mountain Standard Time). It's clear that JS knows I am currently in GMT-7.
Now, my server is sending 2018-08-24T17:00:00. So I parse it with moment.js, convert to local timezone and then format the result.
moment.utc(this.props.value).local().format('h:mm A')
The resulting value is 11:00 AM. 17:00:00 - 7 offset is 10:00 which is 10:00 AM. Why is javascript converting into 11:00? I get the same result if I try the spacetime library:
const s = spacetime(this.props.value,'UTC')
s.goto(spacetime().timezone().name);
console.log(s.format('h:mm a')); // Also spits out 11:00 AM
If I manually adjust the moment object with the offset, it works correctly:
var m = moment.utc(this.props.value);
m.add(-(new Date()).getTimezoneOffset(), 'minutes');
console.log(m.format('h:mm A')) // Outputs the correct time: 10:00 AM
Why is moment and spacetime both not adjusting my timezone correctly when Javascript is clearly aware of what timezone I'm in? The same problem occurs in both Chrome and Microsoft Edge. For now I will use the hacky workaround above, but I'd prefer to use the native methods so I'm curious as to why this isn't working. Any ideas?
Mountain Standard Time is UTC-7
Mountain Daylight Time is UTC-6
It is currently November 19th, and DST is not in effect, so you currently get UTC-7
For the date you gave, 2018-08-24T17:00:00, DST is in effect, so you get UTC-6
Everything is working correctly.
In other words, it doesn't matter whether it is currently in effect or not, only whether it is/was/will be in effect for the date in question.
Regarding this part:
m.add(-(new Date()).getTimezoneOffset(), 'minutes');
Don't do that. You aren't adjusting the time zone, you're picking a different moment in time.

Java Date timezone printing different timezones for different years, Workaround needed

While testing my application I got a weird problem. When I put a date having the year before 1945, it changes the timezone.
I have got this simple program to show the problem.
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
Calendar calendar = Calendar.getInstance();
System.out.println("**********Before 1945");
calendar.set(1943, Calendar.APRIL, 12, 5, 34, 12);
System.out.println(format.format(calendar.getTime()));
System.out.println(calendar.getTime());
System.out.println("**********After 1945");
calendar.set(1946, Calendar.APRIL, 12, 5, 34, 12);
System.out.println(format.format(calendar.getTime()));
System.out.println(calendar.getTime());
}
The output I am getting is below:-
**********Before 1945
1943-04-12 05:34:12+0630
Mon Apr 12 05:34:12 IDT 1943
**********After 1945
1946-04-12 05:34:12+0530
Fri Apr 12 05:34:12 IST 1946
For the first one, I am getting it as +0630 and IDT, while for the second one, I am getting +0530 and IST which is expected.
Edit:-
After looking at #Elliott Frisch answer I tried a date before 1942:-
calendar.set(1915, Calendar.APRIL, 12, 5, 34, 12);
System.out.println(format.format(calendar.getTime()));
System.out.println(calendar.getTime());
output:-
1915-04-12 05:34:12+0553
Mon Apr 12 05:34:12 IST 1915
Here again, it says IST but shows +0553. Shouldn't it be +0530.
Just for a comparison, I tried same thing in javascript:-
new Date("1946-04-12 05:34:12") //prints Fri Apr 12 1946 05:34:12 GMT+0530 (IST)
new Date("1943-04-12 05:34:12") //prints Fri Apr 12 1943 05:34:12 GMT+0530 (IST)
new Date("1915-04-12 05:34:12") //prints Mon Apr 12 1915 05:34:12 GMT+0530 (IST)
Which works fine. I want to know why java is affected by it, and if it's a known problem, what is the possible workaround for it.
Thanks in advance.
This is likely the expected behaviour from Java (and not from JavaScript).
As implied by the comment by RobG above, programming languages may or may not support historical time rules (such as DST and timezone offsets). In your case, it appears that your Java runtime supports it, whereas your JavaScript runtime does not.
A list of historical timezones and DST rules for India can be found at timeanddate.com. The list confirms the timezone offsets of your Java dates:
Until 1941: UTC+5:53:20
1941: UTC+6:30
1942: UTC+5:30
1943-44: UTC+6:30
From 1945: UTC+5:30
Checking your dates against Wolfram|Alpha further confirms your Java date UTC offsets: 1915, 1943, 1946
Wikipedia provides more information about time in India:
Calcutta time was officially maintained as a separate time zone until 1948
Calcutta time could either be specified as UTC+5:54 or UTC+5:53:20. The latter is consistent with your code example.
The Wikipedia entry further informs that the current IST timezone with an offset of UTC+5:30 was not in full effect in all of India until 1955.
As pointed out by Elliott Frisch and confirmed by the link to timeanddate.com above, DST was in effect during WWII. In your comment to his answer, you state:
is this the way we are supposed to save in database and use it in applications, or we use some workaround for it
I guess it depends. If you really need to distinguish dates as points in time accurately, you would need a timezone-independent representation such as UTC or unix time (or milliseconds since the unix epoch). If you just work with local dates from the same timezone, a simple string representation (e.g. YYYY-MM-DD hh:mm:ss) could suffice.
There was a war. From the wikipedia link, India observed DST during World War 2, from 1942-1945.
java.time
Avoid using the troublesome old date-time classes bundled with the earliest versions of Java. Now legacy, supplanted by the java.time classes.
ZoneId z = ZoneId.of( "Asia/Kolkata" ); // "Asia/Calcutta"
LocalTime lt = LocalTime.of( 5 , 34 , 12 );
ZonedDateTime zdt1943 = ZonedDateTime.of( LocalDate.of( 1943 , Month.APRIL , 12 ) , lt , z );
ZonedDateTime zdt1945 = ZonedDateTime.of( LocalDate.of( 1945 , Month.APRIL , 12 ) , lt , z );
ZonedDateTime zdt1946 = ZonedDateTime.of( LocalDate.of( 1946 , Month.APRIL , 12 ) , lt , z );
ZonedDateTime zdt2016 = ZonedDateTime.of( LocalDate.of( 2016 , Month.APRIL , 12 ) , lt , z );
Dump to console.
System.out.println( "zdt1943: " + zdt1943 );
System.out.println( "zdt1945: " + zdt1945 );
System.out.println( "zdt1946: " + zdt1946 );
System.out.println( "zdt2016: " + zdt2016 );
See live code in IdeOne.com.
When run. We see the same behavior as described in your Question, with an offset-from-UTC of six and a half hours during the war and five and a half after. We get the same behavior whether using Asia/Kolkata or Asia/Calcutta. The java.time classes use tzdata, formerly known as Olson Database.
zdt1943: 1943-04-12T05:34:12+06:30[Asia/Kolkata]
zdt1945: 1945-04-12T05:34:12+06:30[Asia/Kolkata]
zdt1946: 1946-04-12T05:34:12+05:30[Asia/Kolkata]
zdt2016: 2016-04-12T05:34:12+05:30[Asia/Kolkata]
In the Question…
When I put a date having the year before 1945, it changes the timezone.
No it does not change the time zone. The results say that in the earlier years “5:34” was defined as six and a half hours ahead of UTC while in later years the definition became five and a half hours ahead of UTC. Just as “5:34” means eight hours behind UTC in Seattle in the summer but seven hours in the winter, because of Daylight Saving Time (DST) nonsense.
But I suspect these may wrong values; read on.
Time in Calcutta
The behavior we are seeing does not seem to match my reading of this Wikipedia page, Time in Calcutta. That page describes odd offsets other than on-the-hour or on-the-half-hour, such as UTC+05:54, which we are not seeing in any of our respective code samples.
So I suspect tzdata does not contain this historical data for India. But just this layperson’s guess; I am no historian.
Do not use date-time types for historical values
While I do not know the specifics of time in this historical period of India and its handling in tzdata, it seems that none of our date-time libraries are handling these historical nuances.
But we should not expect such handling! Know that tzdata does not promise complete coverage of time zones before 1970.
When referring to historical date-time values, I suggest you use simply text rather than any of the date-time data types. The purpose of the data types is for validation and calculation. You are likely doing neither for historical values. I cannot imagine you are determining the number of days an invoice is overdue from 1943.
Perhaps you should edit your Question to describe why you are storing these historical date-time values in a database so precisely. If you were merely experimenting and noticed these issues, know that you should not expect precise date-time handling in the far past (before 1970) nor into the far future (past the few weeks notice politicians sometimes give about sudden time zone definition changes).
Upshot: Attempting to precisely handle historical date-time values is fraught with various issues and seems pointless to me.
what is the possible workaround for it
I suggest using “local” date-time values as text in ISO 8601 format without any time zone.
I would recommend keeping the epoch equivalent of the dates in the database. I believe, irrespective of the day light savings, the time and date of a period represents the actual, be it IDT or IST. I would use the example https://stackoverflow.com/a/6687502 and convert all the dates to epoch and store into DB. I will reverse the logic to display the date time from the database along with IDT/IST indicator to avoid the confusion for the user.

Javascript epoch date incorrect

I am doing some javascript date stuff, and I executed the following:
console.log(new Date(0));
I expected to see the *nix Epoch, but I was oddly returned:
Wed Dec 31 1969 19:00:00 GMT-0500 (Eastern Standard Time)
What happened?
You are setting the internal time value, which is UTC, but seeing a string that is based on your system settings, which likely have an offset of UTC-05:00.
The ECMAScript specification explains how the Date constructor and instances work. Given:
new Date(0)
the Date constructor is called with one argument (§20.3.2.2 Date(value)) so it creates a Date instance with it's internal time value set depending on the type of argument. As the value is a number, the time value is set to that number.
The time value is an offset in milliseconds from 1970-01-01T00:00:00Z §20.3.1.1 Time Values and Time Range. Note that it's based on UTC.
The behaviour of console.log is entirely implementation dependent, so what you get from:
console.log(dateInstance);
depends on the host. However, most seem to call the object's toString method which returns an implementation dependent string based on the timezone setting for the host system (§20.3.4.41 Date.prototype.toString()). That is, a "local" time.
The timezone offset can be determined using getTimezoneOffset. It's in minutes and has the opposite sense to an ISO 8601 offset (e.g. UTC-05:00 will be +300). If you want to get a date string that represents the internal time value without an offset, use toUTCString.
I was unable to find any resources explaining it, but this 'error' is due to my timezone (as far as I can tell)
My timezone is GMT-0500, which is 5 hours behind UTC time. Add 5 hours to Wed Dec 31 1969 19:00:00 and you get the Epoch (Thurs Jan 1 1970 00:00:00)

Inconsistant date parsing with missing timezone

In doing some testing I've found inconsistant behavior between browsers with the following javascript
new Date("2013-09-10T08:00:00").toString()
In IE and Firefox the result is
"Tue Sep 10 2013 08:00:00 GMT-0400 (Eastern Daylight Time)"
In Chrome the result is
"Tue Sep 10 2013 04:00:00 GMT-0400 (Eastern Daylight Time)"
So according to my reading of the ECMA script of the format for Date strings it says...
All numbers must be base 10. If the MM or DD fields are absent "01" is
used as the value. If the HH, mm, or ss fields are absent "00" is
used as the value and the value of an absent sss field is "000".
The value of an absent time zone offset is "Z"
However in the documentation for the "new Date()" constructor it says
15.9.3.2 new Date (value)
Let v be ToPrimitive(value).
If Type(v) is String, then
a. Parse v as a date, in exactly the same manner as for the parse method (15.9.4.2); let V be the time
value for this date.
15.9.4.2 Date.parse (string)
The parse function applies the ToString operator to its argument and interprets the resulting String as a date
and time; it returns a Number, the UTC time value corresponding to the
date and time. The String may be interpreted as a local time, a UTC
time, or a time in some other time zone, depending on the contents of
the String.
Any ideas which implementation is correct?
Standards clash. ISO 8601 states that:
If no UTC relation information is given with a time representation, the time is assumed to be in local time.
ECMA says:
The value of an absent time zone offset is “Z”.
Mozilla devs think that ISO takes precedence, Chrome folks seem to disagree.
The current draft of ES6 says (under 20.3.1.15):
If the time zone offset is absent, the date-time is interpreted as a local time.
so Mozilla's implementation is (will be) correct.
There are several questions on stackoverflow.com that address this issue. I gave a rather thorough explanation here if anyone reading this is interested in the browser-to-browser details.
The bottom line though is, for now at least, you should either avoid the ISO 8601 format all together or ALWAYS include a timezone specifier when using it. And, never use the 'YYYY-MM-dd' format because it gets interpreted as a short version of ISO 8601 without a time zone specifier.

Why is 275481/69/100089 the max date "new Date()" will parse? [duplicate]

Assuming a 32-bit OS/Browser, could a Date object created in JavaScript rollover to 1970 if I set a date beyond 2038?
The Mozilla documentation says a year can be set to 9999, however I don't know if this is consistent across all JavaScript implementations, or if this is an accurate description of what the specification dictates.
I would think given the wording in the documentation, it seems like it's either using a 64-bit number for storing the time or storing the actual data in ISO date format.
Does anyone know how browsers implement this?
It shouldn't be - according to the ECMAScript specification seciont 15.9.1.1:
Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC. Leap seconds are ignored. It is assumed that there are exactly 86,400,000 milliseconds per day. ECMAScript number values can represent all integers from –9,007,199,254,740,991 to 9,007,199,254,740,991; this range suffices to measure times to millisecond precision for any instant that is within approximately 285,616 years, either forward or backward, from 01 January, 1970 UTC.
The actual range of times supported by ECMAScript Date objects is slightly smaller: exactly –100,000,000 days to 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC.
This gives a range of 8,640,000,000,000,000 milliseconds to either side of 01 January,
1970 UTC. The exact moment of midnight at the beginning of 01 January, 1970 UTC is represented by the value +0.
Only bitwise operators in JS are 32bit. There is no version that changes this, and there is no difference if your OS is 64bit. So if someone is using bitwise on timestamps, this could happen. For example, here I use bitwise or because I want the side-effect of all bitwise operators that they convert to int, just so I loose the milliseconds of the date.
new Date('2038-01-01T01:01:01.345') / 1000 | 0; // 2145913261.
new Date('2039-01-01T01:01:01.345') / 1000 | 0; // -2117518035. Wraps around...
I could be using anything else, such as Math.round, or parseInt and there will be no problem, but if I use bitwise, it's going to wrap around.

Categories

Resources