According to specifications, Node JS (ES5) should use current dts rules when working with Date objects. Current means "for now", not for a particular date. That's not perfect, but enough for me at this moment.
Currently that rules are wrong (due to law changes in Chile).
Simple probe:
console.log(new Date())
Mon Apr 08 2019 12:48:08 GMT-0300 (Chile Summer Time) {}
shows "(Chile Summer Time)" at the end of the default toString for a date. Actually we are not in Summer Time (it ended two days ago) and current offset should be -4 and not -3.
I tried upgrading Node to lates stable release and didn't work. I Don't want to change a lot of code (in production) to use moment.js or equivalent.
Is there any way to update DST rules in an existing Node JS installation?
Thanks in advance
-- Edit --
TZ environment variable is empty.
Tested on Mac OSX (Mojave 10.14.4) and Red Hat (4.8.5-36)
moment.js recognizes zone correctly ("America/Santiago" whith moment.tz.guess())
The change for Chile you mentioned was part of IANA TZ data version 2018f, released in October 2018.
Node exposes the tz version via process.versions.tz. The current shipping 10.15.3 LTS version returns 2018e, which explains why you aren't seeing the update. I assume this will be updated at some future date.
Node gets its IANA data via ICU. There are instructions for different ways to compile Node's ICU support (system-icu, small-icu, full-icu) documented here, which apply if you are building Node yourself from source code. Alas, I cannot find any mechanism for updating Node's ICU data directly, without building Node yourself.
ICU has documentation on how to update time zone data without updating all of ICU. however, I cannot see anywhere that Node is taking advantage of this capability.
Thus, when it comes to updating Node's time zone data - Either it cannot currently be done, or it is poorly documented. Sorry, I don't have a better answer for you than that.
You did say you were using moment-timezone already, which ships its time zone data independently. If you use only moment functions (rather than the Date object), then you should get the correct result.
Related
I have a list of deadlines for various projects. Once every 3 months, I download the next 3 months' worth of deadlines as ICS files and add them to my calendar. Because I only download them once every 3 months, I am experiencing an issue with regards to daylight saving time (DST). Here is an example of the problem:
In January, I download the upcoming deadlines for February, March, and April, setting the time as 9:00 AM on their respective dates.
Come March 13th (the start of DST), the deadlines start to appear as 8:00 AM because when I downloaded them, it was not DST, so when we "sprung forward," what used to be 9:00 AM is now 8:00 AM, and the files are not compensating for that.
I have had trouble determining where the best way to solve this problem would be:
In the configuration of the ICS file itself. I imagine that if there was a place to do this, it would be here. I have scoured the documentation of ICS files and have yet to find something that would auto-compensate for DST in the future.
In JavaScript on the front-end. I know that JavaScript has the option of setting the timezone to a region ("America/New_York") instead of a time zone, which helps it auto-compensate for DST, but I have not been able to get that feature to work on a date in the future, as it only references the current time.
In PostgreSQL on the back-end. I have tried various versions of AT TIME ZONE to no avail. I encounter the same problem I did in JavaScript: It sets the time in the specified time zone without taking into account whether or not the date would be affected by DST in the future.
Update to Post to Include More Information
The deadline information (PROJECT_NAME and DUE_DATE) is extracted from a PostgreSQL database. The due date is stored as a DATE value like this: 05-MAR-22.
To add the time, I have taken two approaches:
Adding the time in PostgreSQL like this:
select ('06-JUN-2022'::date + time '9:00') at time zone 'America/New_York'
Adding the time in JavaScript like this:
deadline.DUE_DATE = deadline.DUE_DATE.split("T"[0] += " 09:00:00";
But neither method has helped me solve the timezone issue.
The JavaScript microservice sets the following fields:
BEGIN:VEVENT
UID:[ UID ]
SUMMARY:[ PROJECT_NAME ]
DTSTAMP:[ DTSTAMP ]
DTSTART:[ DTSTART ]
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:[ TRIGGER ]
END:VALARM
DURATION:PT
END:VEVENT
which seems to be missing a few important details for configuring the timezone.
You have the option of embedding time zone information in the ICS file by utilizing the TZID parameter and VTIMEZONE component of ICS files. The VTIMEZONE component requires adding information about DST rules to the file, and each unique TZID must have a corresponding VTIMEZONE component. You can use tzurl.org which has generated VTIMEZONE definitions for every time zone. For more information about how to do this, you can refer to these related questions:
How to Specify Timezone in ics File which will work efficiently with google,outlook and apple?
What's VTIMEZONE used for in icalendar? Why not just UTC time?
I am lacking some knowledge of the specific implementation details of Microsoft Outlook and Apple calendar, but I'm pretty sure that they would take such relevant information from the ICS file into account.
Look at how you are generating the ICS files with Javascript. It sounds like the timezone is being set to the pre-daylight-savings timezone, which will cause problems when viewing the event after the daylight savings shift. According to the ics specification:
The use of local time in a DATE-TIME or TIME value without the "TZID" property parameter is to be interpreted as floating time, regardless of the existence of "VTIMEZONE" calendar components in the iCalendar object.
This may also point to the possibility that the timezone is being set incorrectly, because if the "TZID"/"VTIMEZONE" components were not defined, the events should be regarded as "floating", or "the same hour, minute, and second value regardless of which time zone is currently being observed."
Running a simple new Date().toString(). On Node 11, You get something like
'Fri May 10 2019 10:44:44 GMT-0700 (Pacific Daylight Time)'
While on Node 8 you get
'Fri May 10 2019 10:44:44 GMT-0700 (PDT)'
Note the different in timezone abbreviation. Why is that? And how can you force toString() to always return the zone in the abbreviation?
Stolen answer from #ssube who was too lazy to log in and post.
the whole Intl object and default formats were introduced between those two versions, which may have become the new default for Date as well.
After some digging on my own, and reading some of the Intl spec:
The ECMAScript 2015 Internationalization API Specification identifies time zones using the Zone and Link names of the IANA Time Zone Database. Their canonical form is the corresponding Zone name in the casing used in the IANA Time Zone Database.
As to how to revert back to an abbreviated timezone, I am seeing that there are several github repos that suggest using regex, others using an abbreviation Map, or even Ben Nadel who uses some regex to process the short timezone or long timezone, as seen in his blog here
Looks like JavaScript leaves this up to the implementer. Based on the below GitHub Issue for ECMA262, there are known differences between the ways UNIX & Windows handle the timezone value.
Across multiple JS implementations, it seems that Date.prototype.toString writes the timezone (in parens) in a long, locale-dependent form on Windows, but in a short form (2-4 letters) from the tz database on Unix-based OSes. More details in the V8 bug tracker.
The spec is really light on details for Date.prototype.toString:
Return an implementation-dependent String value that represents tv as a date and time in the current time zone using a convenient, human-readable form.
Does anyone have a good memory of why this is the definition? Looks like it goes all the way back to ES1.
Fortunately, it seems that, at this point, implementations have converged on something that's almost always the same, with the exception of the timezone string.
For the timezone string, would it be a good idea to pick one of the two alternatives and standardize it across all platforms? Does anyone have evidence one way or the other whether either of the two is likely to be more web-compatible, or whether we need to preserve the variation?
Additionally, it looks like there is still active discussion in the V8 Issues for Date.prototype.toString() normalization.
Going through the NodeJS there doesn't seem to be an explicit mention of this in their change logs for v10+.
Update
After digging through V8 commits, it looks like there is a new Timezone Names Cache implemented for performance in V8 when using Date.prototype.toString(). Based on the below excerpt from the message for this commit, it seems like this change is why there is a difference between Node v8 & Node v11
To speed up Date.prototype.toString(), this patch adds a cache in the
DateCache for the string short name representing the time zone.
Because time zones in a particular location just have two short names
(for DST and standard time), and the DateCache already understands
whether a time is in DST or not, it is possible to keep the result of
OS::LocalTimezone around and select between the two based on whether
the time is DST or not.
In local microbenchmarks (calling Date.prototype.toString() in a
loop), I observed a 6-10% speedup with this patch. In the browser, the
speedup may be even greater as the system call needs to do some extra
work to break out of the sandbox. I don't think the microbenchmark is
extremely unrealistic; in any real program which calls
Date.prototype.toString() multiple times, the cache should hit almost
all of the time, as time zone changes are rare.
The proximate motivation for this patch was to enable ICU as a backend
for timezone information, which is drafted at
https://codereview.chromium.org/2724373002/ The ICU implementation of
OS::LocalTimezone is even slower than the system call one, but this
patch makes their performance indistinguishable on the microbenchmark.
In the tz database, many timezones actually do have a number of
different historical names. For example, America/Anchorage went
through a number of changes, from AST to AHST to YST to AKST. However,
both ICU and the Linux OS interfaces just report the modern timezone
name in tests for the appropriate timezone name, even for historical
times. I can see why this would be:
- For ICU, CLDR only has two short names in the data file: the one for dst and non-dst
- For Linux, the timezone names do seem to make it into the /etc/localtime file. However, glibc assumes there are only two
relevant names and selects between them, as you can see in its
implementation of localtime_r:
http://bazaar.launchpad.net/~vcs-imports/glibc/master/view/head:/time/tzset.c#L573
So, this cache should be valid until we switch to a more accurate
source of short timezone names.
I am using JavaScript Intl API to detect the user's timezone in the browser:
Intl.DateTimeFormat().resolvedOptions().timeZone
While this API looks stable, I don't know what to do with the next. On my Windows 10 machine, the timezone is set to US Eastern Standard Time:
C:\> tzutil /g
US Eastern Standard Time
In Firefox and Edge the above JavaScript code results with "America/Indiana/Indianapolis", while Chrome returns just "America/Indianapolis".
Why this yields different results in different browsers? Is it a Chrome bug or should I transform the time zones somehow before processing them on the server?
On Ubuntu (which is my server's OS), if I list available time zones with timedatectl list-timezones, there is "America/Indiana/Indianapolis", but there is no "America/Indianapolis". Because of this issue I cannot accept Chrome's "America/Indianapolis" from the client, as I grab the "available time zones list" from the output of timedatectl list-timezones. Which behavior should I apply here?
Any help is appreciated. Thank you!
Chrome, like many others, gets its time zone information through ICU, which in turn sources from CLDR, which in part sources from IANA.
There is a subtle difference between IANA and CLDR with regard to canonicalization.
IANA treats the preferred form of the time zone identifier as canonical. If the preferred form changes, they make the new one primary (a Zone entry) and move the old one to an alias (a Link entry).
CLDR treates the first form of the time zone identifier as canonical. That is, the first time it appeared in CLDR, it is locked in forever. If a new form ever appears, it is added as an alias in the /common/bcp47/timezone.xml file.
Taking your case of Indianapolis:
IANA Zone entry (reference here)
Zone America/Indiana/Indianapolis ...
IANA Link entry (reference here)
Link America/Indiana/Indianapolis America/Indianapolis
In CLDR, the primary zone is listed first in the "aliases" attribute, followed by other aliases. (reference here)
<type name="usind" description="Indianapolis, United States" alias="America/Indianapolis America/Fort_Wayne America/Indiana/Indianapolis US/East-Indiana"/>
The same thing can be found with Asia/Calcutta vs. Asia/Kolkata and several other examples.
Additionally, be aware that the Windows time zone mappings also source from CLDR, in the /common/supplemental/windowsZones.xml file. You'll notice there that US Eastern Standard Time is mapped to America/Indianapolis. So the difference between browsers depends very much on which canonicalization rules are followed.
In the end, it doesn't really matter which alias is used. They point at the same data.
Also worth pointing out, that particular Windows zone should only be selected if you care about historical time changes in Indiana. If you are just in the US Eastern time zone, you should set your system to "Eastern Standard Time", rather than "US Eastern Standard Time". (Yes, those IDs are confusing...)
zone.tab is incomplete. Some time zones are represented as symbolic links:
$ find /usr/share/zoneinfo/America -name Indianapolis -exec file {} \;
/usr/share/zoneinfo/America/Indiana/Indianapolis: symbolic link to ../Indianapolis
/usr/share/zoneinfo/America/Indianapolis: timezone data, version 2, 7 gmt time flags, 7 std time flags, no leap seconds, 99 transition times, 7 abbreviation chars
I'm not sure how/why each browser returns a different result (probably they read from different sources and/or it can be due to how each implements Intl.DateTimeFormat). But it doesn't matter, because both are the same timezone.
If you check this list, you'll see that America/Indianapolis links to America/Indiana/Indianapolis. Those names in the format Region/City are maintained by IANA and it's probably one of the best sources we have about timezone information.
IANA also keeps a backward file that maps those 2 names, with America/Indiana/Indianapolis being the most recent one.
So one alternative is to get a copy of the backward file and use it to map whatever name you receive to the respective new name, and also validate against your server's timezone list.
var d = new Date();
// returns correct value "Fri Sep 26 2014 01:16:31 GMT+0300 (E. Europe Daylight Time)"
d.toString();
// returns incorrect value one hour behind "9/26/2014 12:16:31 AM"
d.toLocaleString();
It seems toLocaleString() is not taking DST into account for this test case.
How are local times calculated by Date (eg, does Chrome apply DST rules itself or via OS) and how/why does the algorithm differ between toString() and toLocaleString()?
Is this a bug or intended behavior?
Tested on Chrome/37.0.2062.120 with location Nicosia, Cyprus (http://www.timeanddate.com/time/zone/cyprus/nicosia)
On a hunch, I tried changing my Windows time zone to:
TZ Key / ID: "E. Europe Standard Time"
Display name: "(UTC+02:00) E. Europe"
After restarting Chrome, I was then able to get the same thing you showed:
Taking a closer look, at the localization properties:
So it would seem that there is no time zone understood by Chrome. This is because Chrome uses ICU, which uses the CLDR mappings for Windows time zones. That particular time zone is now considered "unmappable" - as there is no IANA time zone identifier that matches this Windows zone.
Note that this zone used to be mapped to Asia/Nicosia, but it was changed in the last revision of CLDR, as described in CLDR #6973
The reason for the discrepancy is that the toLocaleString function uses the ECMAScript Internationalization API, while toString does not. The prior is dependent on ICU and CLDR, while the latter just asks the OS for a time zone string.
The fix is for the user to switch their time zone to the correct setting. For Cyprus, it should be:
TZ Key / ID: "GTB Standard Time"
Display name: "(UTC+02:00) Athens, Bucharest"
When looking at the options in the Windows Time Zone Control Panel:
Be sure to close all Chrome windows completely after making the time zone change, as Chrome will not properly initialize time zones until it is restarted.
Now, the issue I mentioned earlier in comments is still a problem, as Intl.DateTimeFormat().resolvedOptions().timeZone now returns Europe/Kiev, and it should return either Asia/Nicosia, or Europe/Bucharest. But it is good enough to return the correct value for toLocaleString - at least most of the time.
This question already has answers here:
Create a Date with a set timezone without using a string representation
(29 answers)
How to initialize a JavaScript Date to a particular time zone
(20 answers)
Closed 8 years ago.
I have a timezone string, for example, "Pacific Time (US & Canada)". I need to get the equivalent of 3pm in that timezone in UTC for particular dates, including daylight savings time. How can I go about this?
For example, I need to find 3pm on December 3rd, PDT in UTC time.
That time zone identifier looks like it came from Rails, or from Twitter (which uses Rails). You first need to convert it to a standard IANA zone identifer such as "America/Los_Angeles". See the MAPPING declaration in the ActiveSupport::TimeZone docs.
Then you can use one of the libraries I mentioned here.
In particular, you may want to try moment-timezone. You'll need the latest develop version for this particular feature, as it was added in issue #25 which is not yet in the current release. Then you can do something like this:
moment.tz("2013-12-03T15:00:00","America/Los_Angeles").utc().format()
You can adjust the input/output format to whatever makes sense for you and is supported by moment.js.
The other time zone libraries I mentioned may also offer similar features without as much overhead, so they are worth a look.
This seems to work for me though it does feel a little fragile
new Date('2013-12-03 15:00:00 PDT').toUTCString()
-> "Tue, 03 Dec 2013 22:00:00 GMT"