Chrome timeZone option to Date.toLocaleString() - javascript

I have recently discovered that there is a new extension to JavaScript. This adds several features to the Date object in the toLocaleString, toLocaleDateString and toLocaleTimeString functions. Reference here.
I am particularly interested in the timeZone option, that supports IANA/Olson time zones, such as America/New_York or Europe/London. This is currently only supported in Google Chrome.
Previous advice was that to work in JavaScript with any other time zone than UTC or your own local time zone, one had to use a library. But now, it appears that this is starting to be incorporated directly into the browser. So now you can do this:
new Date().toLocaleString("en-US", {timeZone: "America/New_York"})
// output: "7/4/2013 5:15:45 PM"
Or:
new Date().toLocaleString("en-NZ", {timeZone: "Pacific/Chatham",
timeZoneName: "long"})
// output: "7/5/2013 9:59:52 AM GMT+12:45"
Or:
new Date().toLocaleString("en-GB", {timeZone: "Europe/London",
timeZoneName: "short"})
// output: "4/7/2013 22:18:57 United Kingdom Time"
// (strange time zone name, but ok)
This is very cool, but I have a few questions:
Is this part of a new standard? Perhaps buried somewhere in ECMAScript 6? Or is it just something custom to Chrome?
Why just Google Chrome? Is it supported anywhere else? Are there plans to supported it anywhere else?
I checked node.js, which uses Chrome's JavaScript runtime, but it doesn't work there. Why not?
Is the time zone data accessible in any other way than the functions I listed? If only available when formatting strings, then doing any calculations based on the results may be difficult.
This is focused on output, but how would I use it for input? Is there a way to pass the time zone in the constructor to the Date object? I tried the following:
// parsing it with a date and time
new Date("2013-01-01 12:34:56 America/New_York")
// passing it as a named option
new Date(2013,0,1,12,34,56,{timeZone:"America/New_York"})
Neither worked. I couldn't find anything in the specs, so I don't think this exists (yet), but please tell me if I am wrong.
The issue described in this post, created by a flaw in the ECMAScript 5 spec, still affects the output, even when the correct data is in the TZDB. How is it that both old and new implementations are coexisting? One would think it would be all the old way, or all the new way. For example, with my computer's time zone set to US Eastern Time:
new Date(2004,10,7,0,0).toLocaleString("en-US",{timeZone:"America/New_York"})
returns "11/6/2004 11:00:00 PM". It should return midnight, since I started at midnight and my local time zone matches the output time zone. But it places the provided input date at the wrong UTC point due to the ES5 issue.
Can I expect that as IANA releases updates to the TZDB that Google will push Chrome updates that contain the changes?

update
There is pretty extensive write-up about the API here
Is this part of a new standard? Perhaps buried somewhere in ECMAScript
6? Or is it just something custom to Chrome?
Yes, these are part of the ECMAScript Internationalization API. It is implemented separate from ECMAScript, but the requirement of implementing ECMAScript Internationalization API is to first have correct implementation of ECMAScript 5.1
Why just Google Chrome? Is it supported anywhere else? Are there plans
to supported it anywhere else?
For the recent years, Google Chrome has mostly been first to implement new features. Mozilla is more conservative, still for example discussing whether to implement the download attribute of a elements. It is now available in IE11 Beta and Opera too. It will be available in Firefox 25.
I checked node.js, which uses Chrome's JavaScript runtime, but it
doesn't work there. Why not?
node.js just uses the same engine, which is a separate project from the Google Chrome browser. The engine just implements Ecmascript 5.1. This is an extension node.js would have to implement separately right now. It will become available in V8 in Q3 so probably a bit after that you can use it in node.js.
This is focused on output, but how would I use it for input? Is there
a way to pass the time zone in the constructor to the Date object? I
tried the following:
There is nothing about inputting dates in the specification. I personally cannot see how this would be useful, you are doing it wrong if you are not transmitting UTC timestamps because something like "2013-01-01 12:34:56 America/New_York" is ambiguous during transitions from DST to standard time.
The issue described in this post, created by a flaw in the ECMAScript
5 spec, still affects the output, even when the correct data is in the
TZDB.
This is input issue, not output. Again, constructing a date with local timezone that you cannot influence or detect is doing it wrong. Use the timestamp constructor overload or Date.UTC.
Can I expect that as IANA releases updates to the TZDB that Google
will push Chrome updates that contain the changes?
Nothing in the spec but I think it will be reasonable to expect that the rules are not too much behind.

Is this part of a new standard? Perhaps buried somewhere in ECMAScript
6? Or is it just something custom to Chrome?
Indeed it is part of the new ECMA-402 standard. The standard is very difficult to read, but there is this friendly introduction.
Why just Google Chrome? Is it supported anywhere else? Are there plans
to supported it anywhere else?
MDN has a list of supporting browsers. According to Bug 853301 it will be available in Firefox 25.
I checked node.js, which uses Chrome's JavaScript runtime, but it doesn't work there. Why not?
Possible reasons are many; it is not up to the current code base, or it would make the node.js bigger and slower (the previous bug tracker entry from Mozilla indicates that the timezone data increased the download size of Firefox by 10 %, and caused the I/O to increase substantially during browser start up.
Is the time zone data accessible in any other way than the functions I listed? If only
available when formatting strings, then doing any calculations based on the results may be
difficult.
Seems that it is not available. Furthermore, the Intl API primer talks that only UTC and local time zone are absolutely required to be supported.
This is focused on output, but how would I use it for input? Is there a way to pass
the time zone in the constructor to the Date object? I tried the following:
The Intl API only speaks about date/time formatting, string collations and number formatting. The datetime formatting not only supports the Gregorian calendar but also many kinds of other calendars, lunar, lunisolar and so forth.
The issue described in this post, created by a flaw in the ECMAScript 5 spec, still affects
the output, even when the correct data is in the TZDB. How is it that both old and new
implementations are coexisting? One would think it would be all the old way, or all the new
way. For example, with my computer's time zone set to US Eastern Time:
new Date(2004,10,7,0,0).toLocaleString("en-US",{timeZone:"America/New_York"})
returns "11/6/2004 11:00:00 PM". It should return midnight, since I started at midnight and > my local time zone matches the output time zone. But it places the provided input date at the > wrong UTC point due to the ES5 issue.
The reason for that is that ES5 mandates that the input to new Date be calculated using the current DST and offset, that is it is America/New York but with EDT timezone, even though Nov 6 is not in EDT. Obviously as this is so specified, then it cannot be changed. However, as Chrome is using the TZDB to do the conversion from the bare UTC point-in-time value to the America/New York tz, it then does consider the time as being in EST.
Can I expect that as IANA releases updates to the TZDB that Google will push Chrome updates
that contain the changes?
I'd believe so

Related

JS offset of a date on specific browser/configuration

I've an API that returns birthdate as 2020-11-24T00:00:00 to a React app. That react app uses that portion to display it:
new Date("2020-11-24T00:00:00").toLocaleDateString();
The issue is that on my browser and all browsers I have seems to give no issue: date is correclty shown. One or two customer complains about ir because they see the date 23/11/2020 (one dat before). I cannot reproduce the bug.
As I understood, Dates can be interpreted by browser as Zulu date so browser can translate the date from GMT+0 to browser's region. Right. Now I have to try to reproduce the bug in order to fix it and And I simply cannot because of misunderstanding.
First postulate: Date("2020-11-24T00:00:00") is going wrong, let's try that: fiddle => no, I cannot reproduce with my browser
Second postulate: .toLocaleDateString() is going wrong, let's try that: [fiddle][2] => no, I cannot reproduce with my browser when changing Location in Chrome.
How can I reproduce the issue in order to fix it?
The current ECMAScript standard obliges your example date string to be interpreted as a local date/time, because the time part is included and the timezone part is not.
Through time the rules for parsing strings have become a bit more standardized, but older JavaScript engines may behave differently. See for instance a post from 2013 or 2017 where a difference was reported (at that time). It is likely that some of your users run that JavaScript on much older engines which interpret this type of date string as UTC (as if suffixed by "Z").
Mozilla Contributors write about using Date.parse (which uses the same parser as when the string is passed to the constructor):
Note: Parsing of strings with Date.parse is strongly discouraged due to browser differences and inconsistencies.
To remove all ambiguity also for older browsers, parse the string yourself:
var dateStr = "2020-11-24T00:00:00";
var dateArr = dateStr.match(/\d+/g);
var date = new Date(dateArr[0], dateArr[1]-1, dateArr[2]); // Add time parts if needed
// Guaranteed to be reporting on the 24th of November 2020 (in locale format)
console.log(date.toLocaleDateString());

Does Intl.DateTimeFormat()'s `dateStyle` and `timeStyle` use the format defined in the OS preferences?

In the past I've always said it's not possible to display the date/time in the format as defined by the user in their OS preferences. For example, I may have EN-US as my language/region but prefer to use a 24-hour time format. Or, I may have changed the short date format from mm/dd/yyyy to mm/dd/yy.
Has this changed now that Intl.DateTimeFormat can use dateStyle and timeStyle values? Note: these options are not supported in all browsers at the time of writing this.
The values for dateStyle and timeStyle are:
full
long
medium
short
These predefined formats (full, long, etc) can be defined by the user in their system preferences, like this.
Do the values of dateStyle and timeStyle use the same format as defined in the OS preferences for the values of full, long, etc?
Here's an example. In the snippet below I have used toLocaleString to display the current date/time. For the options I have passed dateStyle: 'long' and timeStyle: 'long'. Will this use the same format as defined in my OS preferences even if I change the 'long' format to a custom format?
const event = new Date();
console.log(event.toLocaleString(undefined, { dateStyle: 'long', timeStyle: 'long', timeZone: 'UTC' }));
Sometimes.
dateStyle and timeStyle addition is a stage 3 proposal, so they are not yet fully finalized. That said, the question is not really about these specific options, but rather about how the engine decides to retrieve the default locale settings and whether it includes the customizations the user has made.
The ECMAscript specification describes the default locale as implementation-specific, so it’s up to the engine to make this decision. See more details in another answer.
It makes a lot of good sense to use the user’s OS preferences, and Edge does so. However, Chrome and Firefox allow the user to choose the browser language independently, and this causes inconsistencies with how the rest of the application behaves. See a more thorough discussion in Firefox bug 1366136.
So to summarize, the answer is dateStyle and timeStyle will sometimes take user’s OS preferences into account, and sometimes they will not.

Creating a promotion that starts at 9am for all stores worldwide

Let's say, for example, that a store promotion starts at 9am for all stores worldwide. This means that it starts at 9am CST for stores in Chicago, 9am PST for stores in Seattle, and 9am GMT for stores in the UK.
In our promotions table on Postgres, we would set the start time for this promotion as 09:00:00.
Each store has a computer with a web browser that looks up available promotions. It needs to pass its local time to the server so that the server can return all promotions for that local time. Thus we need to find a way to capture the local time in JavaScript, encode it, send it to a Java backend, reconstruct it, and then compare that with the start time in the promotions table.
The local time, of course, depends on the time zone. If it's 9am in Chicago then a store in Chicago should tell the server that it's 9am. It's futile to send the UTC time without some indication of the time zone.
Question: What's a good way to capture the local time (based on time zone) in JavaScript, encode that, send it to a Java backend, reconstruct it as a Java Date, and then compare that Java Date with the 9am promotion start time in the Postgres database?
My (unsatisfactory) approach: The best I can think of is to send the UTC time in milliseconds using JavaScript's Date.getTime method, along with the time zone offset, which can be calculated in minutes using JavaScript's Date.getTimezoneOffset method and converted to milliseconds. Subtracting the time zone offset from the UTC time in milliseconds, we can then create a Java Date object from the resulting difference. If it's 9am in Chicago, then, hopefully, the Java Date will store 9am. What's a little odd about this approach, however, is that the Java Date will actually be storing 9am UTC, even though it's representing 9am CST. This is just one of the reasons why I am not satisfied with this approach. Can you think of something better?
Postgres
In Postgres, when you mean 9 AM everywhere, or 9 AM anywhere, on a certain date, use the column data type TIMESTAMP WITHOUT TIME ZONE. Any time zone or offset-from-UTC included with an input is ignored, with the date and time-of-day taken as-is (no adjustment) and stored. This data type purposely lacks any concept of time zone or offset-from-UTC.
For time-of-day without a date, use TIME WITHOUT TIME ZONE data type. Postgres also offers a TIME WITH TIME ZONE only because it is required by the SQL spec; this WITH type is nonsensical and should never be used.
Postgres is an excellent choice for such a project, as it offers excellent date-time support in both its data types and in its functions. Databases vary widely in their date-time features.
Java
On the Java backend, use only the modern java.time classes. These years ago supplanted the terrible old date-time classes bundled with the earliest versions of Java.
If not yet using Java 8 or later, find nearly all the same functionality in a back-port to Java 6 & 7 in the ThreeTen-Backport project. Well worth the minor effort of adding this library to your project. From the same fine folks who brought you the java.time classes and the Joda-Time project, all led by the same man Stephen Colebourne.
LocalDateTime
In java.time, use LocalDateTime class for when you mean 9 AM anywhere/everywhere on a certain date. Like TIMESTAMP WITHOUT TIME ZONE in Postgres, this class purposely lacks any concept of zone or offset.
LocalDateTime ldt = LocalDateTime.of( 2018 , 1 , 23 , 15 , 0 , 0 , 0 ) ; // 3 PM on 23rd of January this year.
LocalTime
If you mean the time-of-day only, without a date, use the class LocalTime.
LocalTime lt = LocalTime.of( 15 , 0 ) ; // 3 PM.
JDBC
As of JDBC 4.2 and later you can exchange java.time objects with the database via getObject and setObject methods.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
If your JDBC drivers are not yet updated to 4.2, then fall back to the awful old legacy classes, but convert immediately to the java.time classes.
Given that the legacy classes lack a class for a date plus time-of-day without time zone, we have to fake it. Use java.sql.Timestamp which represents a moment in UTC with a resolution of nanoseconds, and just ignore the fact that it is in UTC.
java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;
For Java 8 and later, convert using new methods added to the old classes. Convert first to java.time.Instant, which also represents a moment in UTC with a resolution of nanoseconds. Then convert to a LocalDateTime by effectively removing the concept of UTC.
Instant instant = ts.toInstant() ; // Convert from legacy class to modern one.
LocalDateTime ldt = LocalDateTime.ofInstant( instant , ZoneOffset.UTC ) ; // Remove the concept of UTC (or any other offset or zone) from our data.
For Java 6 & 7 using the ThreeTen-Backport library, use the conversion methods in their utility DateTimeUtils class.
org.threeten.bp.Instant instant = org.threeten.bp.DateTimeUtils.toInstant( ts ) ; // Convert from legacy class to modern.
org.threeten.bp.LocalDateTime ldt = LocalDateTime.ofInstant( instant , ZoneOffset.UTC ) ; // Remove the concept of UTC (or any other offset or zone) from our data.
ZonedDateTime
The Local… classes by definition have no real meaning until you place them in the context of a time zone. A LocalDateTime is not a moment, does not represent a point on the timeline.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as PST or BST or EST or IST as they are not true time zones, not standardized, and not even unique(!).
LocalDateTime ldt =
LocalDateTime.of(
LocalDate.of( 2018 , Month.January , 23 ) ,
LocalTime.of( 9 , 0 )
)
;
ZoneId zLosAngeles = ZoneId.of( "America/Los_Angeles" ) ; // Seattle time zone.
ZonedDateTime zdtSeattle = ldt.atZone( zLosAngeles ) ;
ZoneId zChicago = ZoneId.of( "America/Chicago" ) ;
ZonedDateTime zdtChicago = ldt.atZone( zChicago ) ;
ZoneId zLondon = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdtLondon = ldt.atZone( zLondon ) ;
There we have three ZonedDateTime objects: zdtSeattle, zdtChicago, and zdtLondon. These are all 9 AM on the 23rd of January earlier this year. Understand that these are three very different moments, each being several hours earlier as you go eastward. They all have the same wall-clock time (9 AM on 23rd) but are three different points on the timeline.
JavaScript
While I do not know JavaScript well enough to say for certain, I doubt you have any library there as rich for date-time handling. The java.time framework is industry-leading.
As for web client user-interface development, I use Vaadin, so it is a non-issue: pure Java on back-end auto-generates the HTML/CSS/DOM/JavaScript needed by the web browser.
find a way to capture the local time in JavaScript
As for detecting the current default time zone in the client machine, I’m no expert, but as I recall the browsers do not return a named time zone, only an offset-from-UTC. See the Answer by Matt Johnson for a possible solution. In any app (desktop or web), ultimately, if the correct time zone is vital, then you must ask or confirm the desired/expected time zone with the user. And it may be wise to always indicate somewhere on your user interface what time zone is being used by your app.
If you need to exchange date-time values between your Java backend and JavaScript code in the front-end, you have two choices primarily:
ISO 8601
Count-from-epoch
ISO 8601
The ISO 8601 standard defines a variety of textual formats for exchanging date-time values. These are wisely designed to avoid ambiguity. They are easy to parse by machine, and easy to read by humans across cultures.
The java.time classes use these formats by default when generating/parsing strings.
Count-from-Epoch
I do not recommend this approach, as it is confusing and error-prone, subject to ambiguity and incorrect assumptions between the people and libraries who are sending or receiving.
An epoch reference date is a point in time used as baseline. Then some count forward or backward is made of some granularity.
One big problem is that there are dozens of epoch references used by various systems. The java.time classes by default use the Unix time epoch of first moment of 1970 in UTC, 1970-01-01T00:00Z.
Another problem is that there are many granularities such as whole seconds, milliseconds, microseconds, and nanoseconds. Programmers must document/communicate clearly what granularity is in play.
If you were to be sending your three opening moments for your three stores to JavaScript as a count-from-epoch, you would be sending three different numbers.
long millisecondsSeattle = zdtSeattle.toInstant().toEpochMilli() ;
long millisecondsChicago = zdtChicago.toInstant().toEpochMilli() ;
long millisecondsLondon = zdtLondon.toInstant().toEpochMilli() ;
Results in three different numbers for three different moments.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You do not need to capture the user's local time, but merely their IANA time zone identifier, such as "America/Los_Angeles". This can then be used in your Java back-end code in APIs that accept a time zone.
In most modern browsers, you can capture the the time zone id like this:
Intl.DateTimeFormat().resolvedOptions().timeZone
If you require supporting older browsers, there are several libraries that will use this Intl API when available, but will fall back to an educated guess when not. More on this here.

javascript seems to be using time zones backwards with Firefox

I've run the following in the console on Firefox (version 21) and I'm getting results I don't expect.
new Date(1362891600000);
var date = new Date(1362891600000);
var time = date.getHours();
new Date(date.setHours(date.getHours() + 24));
The result really throws me for a loop.
The first date shows up as Eastern Daylight Time, while the second one shows up with Eastern Standard Time. It's totally backwards. This does not happen with IE or with Chrome.
What's going on here?
This is definitely a bug in Firefox. You should probably report it to them.
However, be aware that anything after the offset is non-standard and support varies wildly across browsers and operating systems.
For example, some browsers display a time zone name, while others display an abbreviation or internal id. Also, some keep their own strings, and some use the values returned by the operating system. And on Windows, there is a different time zone database than on Linux or Mac. Also, some browsers may localize this string using language, locale, or culture settings.
You can display it to a user, if you know the value is in their own local time zone. But don't rely on it for anything critical.

Change timezone in Arshaw FullCalendar

From my understanding, Arshaw FullCalendar displays events according to the timezone of the local computer's operating system. I assume this is done by the javascript Date() object, which also is based on the local computer's operating system. I have an application that has group calendars and a group timezone, I want to be able to force every local Arshaw Calendar to display according to that group's time-zone, no matter what timezone that computer is. How do you do this?
Note: I've looked through the documentation fairly thoroughly, and found no such option. I'm hoping that javascript has something equivalent to php's date_default_timezone_set(), which seems to me the way this could be solved.
*Edit 1/31/2013 12:23pm CST:
I am sending everything to the calendar as unix timestamps, so I assume the option ignoreTimezone would not apply here, as described in this stackoverflow thread:
jQuery FullCalendar timezone synchronization
You should probably try to set the option "ignoreTimezone" to "false" and give to Arshaw FullCalendar date in ISO8601.
You can get more information about that here: http://arshaw.com/fullcalendar/docs/event_data/ignoreTimezone/
To convert unix timestamps to ISO8601, you can use this in javascript:
var d = new Date(1360412434000).toISOString();
This is ECMAScript 5.
See here for compatibility and fallback code: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toISOString

Categories

Resources