Javascript date toLocaleString ignores locale - javascript

I have en_GB as locale on both my system and my browser.
browser
system
When I run (new Date()).toLocaleString() in the browser console I get "12/1/2021, 1:56:32 PM" aka the en_US format. However, if I set the locale via Chromium Dev Tools Sensors tab to en_GB and reload the page, the function returns "01/12/2021, 14:05:02" aka the proper en_GB formatting.
Overriding locale with Dev Tools
So my questions is whether or not this is an issue with my system/browser. If it's not an issue with my configuraiton, then how why does that dev tools feature fix the issue? How exactly does it change the global javascript values and why doesn't the browser set those automatically according to my default language?
I am aware of workarounds such as passing the navigator.language manually as an argument to toLocaleString function or using a library like moment or dayjs to manage my dates and formatting, but this questions just about the unpredictable behaviour of toLocaleString as I have described above.
Thanks.

Turns out I have had export LANG=en_US.utf8 in my .bashrc. Since it is apparently sourced before starting any program on my system, the browsers used en_US instead of my actual system language.

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.

How/Where does JavaScript detect the default locale?

Today, the 11th of September, 2017, JavaScript's toLocaleDateString() method is outputting 9/11/2017 for me. But I am in the UK, so the formatting is wrong in this case. The MDN Web Docs tell me that this method returns "a formatted string in the default locale".
So, where/how is JavaScript detecting my default locale? Where is this set, or what does it depend on, and (how) can I change it?
Edited to add: I'm using Firefox 55.0.3 on Windows 10 if that makes any difference.
To summarize shortly, detecting the current locale is implementation dependent and may differ from environment to environment. Your default language may also depend on the installer you've used to install your browser.
The not so short version:
Following the ECMAScript spec, conforming browsers (and other environments, such as Node.js) should implement localization following the ECMAScript Internationalization API (ECMA-402), which only outlines the following for getting the default locale:
The DefaultLocale abstract operation returns a String value representing the [...] language tag for the host environment’s current locale.
This means that getting the default locale is implementation dependent and can differ from browser to browser. This is intentional, as it allows browser vendors to keep their current, differing implementations to stay conforming without much fuss.
While it's true that it would be nice to have this standardized as well, it's more beneficial to get everyone on board for a broad spec first and then work out the little kinks later.
Most modern browsers allow you to change your current default locale in their preferences (Chrome shown):
I checked JavaScript date and time formatting in Chrome, Firefox, and Edge on my Windows 10 PC, with interesting results. My language tag should be "en-NZ', and I thought the locale behavior would follow accordingly.
Of the three browsers, only Edge had picked up the correct language tag by default, presumably from the OS, while Chrome and Firefox both defaulted to en-US. Edge also defaults to using the correct locale settings.
It seems I can influence the locale in Chrome by changing language in Advanced Settings to en-NZ, but the default locale behavior I get is as for en-GB. Firefox doesn't seem to care what my preferred language is for locale behavior, which depends only on the language "used to display menus, messages, and notifications from Firefox". The only English language options for that are US, Canada, or UK.
In Chrome and Firefox the only way to ensure the correct locale behavior seems to be to explicitly specify it in the JavaScript code.
You can pass locale as parameter. See the example below:
var date = new Date().toLocaleDateString("en-GB");

Retrieving browser language in chrome

I've been trying to get momentjs to correctly detect the browser language and localise the time displays. (as per Locale detection with Moment.js)
But I have been having a bit of trouble getting the correct locale out of chrome.
It seems like chrome has the correct list of languages configured for my browser in navigator.languages but navigator.language is returning something completely different.
I'm guessing either I have chrome setup wrong (which doesn't seem likely given the correct languages are in navigator.languages) or the method for selecting the browser language isn't quite right?
Is there a different way I should be using to get the language other than window.navigator.userLanguage || window.navigator.language ?
The W3C draft states that:
navigator.language is the user's "preferred language" or a "plausible language". This is not necessarily the same as navigator.languages[0].
navigator.languages lists the "user's preferred languages, with the most preferred language first". It is expected to have the same value as the Accept-Language header.
By the looks of quickly experimenting in a VM, navigator.languages[0] leads to the most accurate results, navigator.language being the language of the OS.
It looks as if the "en-GB" you are getting refers to the language of the user, whereas the "en-NZ" refers to the user locale (number & date/time formatting).
Since this is still a draft, the results you get may change with implementation.

Chrome timeZone option to Date.toLocaleString()

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

Categories

Resources