Javascript -- Detect if user's locale are set to use 12-hour or 24-hour time format
Asked Answered
O

5

19

One way to do that is to parse new Date().toLocaleString(). But this doesn't work in chromium/webkit since the string it returns isn't dependent of the user's locale (see bug report at http://code.google.com/p/chromium/issues/detail?id=3607)

I emphasize that I'm looking for a solution that is client side only and that works in chromium.

Orvil answered 30/5, 2011 at 4:19 Comment(4)
I think this may help you [how-do-i-display-a-date-time-in-the-users-locale-format-and-time-offset][1] [1]: #85616Claymore
Just be a man, and force 24 hour time for everyone!Schmaltzy
@brillout.com Your accepted answer is outdated. Please update this question and check EliGreys answer (please note, somebody gave him 100+ points!)Girdle
The string generated by toLocaleString is governed by the associated language code (either default or specified), it has nothing to do with locale (i.e. geographic location).Karolinekaroly
S
12

It's been a few years since this was last answered and a few technologies have been introduced to solve the issue. One such technology is Intl.DateTimeFormat, which provides a wealth of information about date formats for various locales.

console.log(new Intl.DateTimeFormat().resolvedOptions());
console.log(new Intl.DateTimeFormat().resolvedOptions().hour12);
.as-console-wrapper { max-height: 100% !important; }

However, most locales don't define a default for the hour12 option. So, if this returns undefined, I'd look at the formatToParts function.

const hourParts = new Intl.DateTimeFormat(undefined, { hour: 'numeric' }).formatToParts(new Date(2020, 0, 1, 13));
console.log(hourParts);
.as-console-wrapper { max-height: 100% !important; }

The output from that should look like (for your browser's current language; in my case, "en-US"):

[
  {
    "type": "hour",
    "value": "1"
  },
  {
    "type": "literal",
    "value": " "
  },
  {
    "type": "dayPeriod",
    "value": "PM"
  }
]

Getting the length of the value of the part with type equal to "hour" will tell you whether it was formatted with twelve or twenty-four hour time.

For instance, I happen to know that in Japan, they use twenty four hour time, so I can check that:

const hourParts = new Intl.DateTimeFormat('ja-JP', {
  hour: 'numeric'
}).formatToParts(new Date(2020, 0, 1, 13));
console.log(hourParts.find(part => part.type === 'hour').value.length);
.as-console-wrapper { max-height: 100% !important; }

And I know the the US defaults to twelve hour time:

const hourParts = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric'
}).formatToParts(new Date(2020, 0, 1, 13));
console.log(hourParts.find(part => part.type === 'hour').value.length);
.as-console-wrapper { max-height: 100% !important; }

It would be easy enough to wrap this in a function:

function localeUses24HourTime(langCode) {
  return new Intl.DateTimeFormat(langCode, {
    hour: 'numeric'
  }).formatToParts(new Date(2020, 0, 1, 13)).find(part => part.type === 'hour').value.length === 2;
}

console.log(localeUses24HourTime()); // undefined means current user's language
console.log(localeUses24HourTime('en-US')); // a specific language known to be false
console.log(localeUses24HourTime('ja-JP')); // a specific language known to be true
.as-console-wrapper { max-height: 100% !important; }

You may find this more or less complicated than parsing the output of toLocaleString().

Note: I switched from using the term "locale" to "language" midway through my answer. This is due to how the specification is written and how information is specified. en-US and ja-JP are BCP language codes, passed to the constructor of Intl.DateTimeFormat to look up date and time formatting rules. The specification uses the term locale to refer to this piece of information but indeed it is a language identifier, which, while it may contain a region (the US and JP in the mentioned codes), does not require them, nor does that region necessarily indicate the user's locale (consider a person who speaks Spanish and learned it in Spain [and therefore would use the language code 'es-ES'], but lives in the United States, where the dates are formatted differently than in Spain).

Syneresis answered 27/2, 2020 at 16:21 Comment(4)
Thanks for this. It perfectly fit the very scenario I was interested in.Insectivorous
"en-US" is not a locale, it's a language code. This misnomer has been perpetuated by the authors of ECMA-402, it really should be addressed.Karolinekaroly
@Karolinekaroly To be sure, it should be addressed. However, I'm not sure every answer dealing with Intl and ECMA-402 needs to be amended. It really should be dealt with by ECMA, then answers can be modified to match.Syneresis
@HereticMonkey—yes, it's up to the authors to fix it. In the meantime, the error is perpetuated…Karolinekaroly
O
5

As long as Chromium doesn't fix toLocaleString() there is no way to do that in chromium.

For Firefox and IE parsing toLocaleString() will give that information.

EDIT
Apparently toLocalString() is now fixed in Chrome. The parsing of toLocaleString() is therefore a solution.

Orvil answered 30/9, 2011 at 15:34 Comment(4)
THIS ANSWER IS OUTDATED: It's fixed in Chrome (~ v32) !Girdle
Unfortunately, Chrome doesn't seem to respect the user's preference for 24h time - I am using Chrome and Safari side-by-side and Safari correctly reflects 24h time while Chrome is using 12h time.Westward
@Westward Are you sure? I set my time display in OSX settings to use a 24-hour clock, then tested in Safari, and I'm getting back 12h time. Apple probably fixed this bug, as calling toLocaleString() is asking for the time to be formatted to a certain locale, and obeying an override in the system settings for a locale format would be incorrect.Capreolate
@Westward I'm not sure if you're aware, but people who come across this answer are not aware that it's been fixed unless someone points it out, which I have done. It's only the 3rd result in Google when you search for it, so it's kinda important to point it out. If you're going to be snarky about it, do it for the right reasons, not because your jimmies were rustled. Also, I just tried it in Chrome and couldn't get the 24-hour format. Are you sure your Chrome's default locale is not en-US?Capreolate
H
3

You should never search for local pattern this way. toLocaleString() is clearly a mistake (derived from Java) and should not be used. As you mentioned, this method is not well supported in various browsers (Chrome is just one of them).
In fact the only web browser (from popular ones) which get it about right (but not 100% right) is IE.

To correctly format date depending on Locale, please use Globalize. It contains localized patterns dumped out of .Net.
You may alternatively want to use Dojo which also allows Locale-aware formatting, but based on CLDR.

Edit, new facts exist

There is a new standard for I18n in JavaScript - ECMA-402. This standard in fact allows for using JS Date's object. However, one should always pass a language tag:

var date = new Date();
var formatted = date.toLocaleString('de-DE');

The only problem with this is, the only web browser I am aware of that currently implements ECMA-402 is Google Chrome.

For now it seems that still the way to go is to use something along the lines of iLib.

Horrible answered 25/9, 2011 at 8:11 Comment(2)
thanks for the information about toLocaleString. Actually I only want to know if the user's locale are set to use 12-hour or 24-hour format. So I guess toLocaleString may do the job for Firefox and IE. For Globalize and CLDR it won't work since the 12-hour/24-hour format is independent to the language of the user's localeOrvil
@cept0: clearly, you must be a long time expert on I18n. I am so sorry, I promise not to write anything on I18n again. BTW. At the time of writing, ECMA-402 didn't exist yet.Decolorant
B
2

Parse (new Date).toLocaleString() for all browsers except Chrome, and check navigator.language against a map of locales and their time formats for Chrome.

Bermejo answered 30/9, 2011 at 19:52 Comment(8)
this doesn't work. As I commented at Pawel's answer the 12-hour/24-hour format is independent to the language of the user's locale. E.g. I used to use en-US but 24-hour formatOrvil
Sorry, but you're a minority. Just because someone can change the default doesn't mean most people do. There is a high enough correlation between en-US and 12-hour format that you should just ignore the very few edge cases. It's either that, or no solution at all, as JavaScript doesn't have access to OS-level settings otherwise.Bermejo
you say "high enough correlation". Do you have any statistics/source to support your saying? ThanksOrvil
Sorry, no empirical statistics, only my anecdotal evidence. It seems quite obvious to think that people with a en-US locale are more likely to use a 12-hour format.Bermejo
Hmm. Last I checked "more likely" and "few edge cases" are in different domains; if you're saying "ignore anyone who uses 24h time and English (US)" you're ignoring a large group of international users who use US English for business reasons and 24h time. Also not to mention you're ignoring the military ;-) If you're basically willing to say "only civilians living in the US who prefer am/pm matter", you really shouldn't be involved in localization/internationalization efforts ;-)Westward
@Westward And how do you propose to do so otherwise? You can only get the locale in a browser, and the locale determines the time format.Capreolate
@DanielT. I'm proposing that if a user has specified they want 24h time, they should get 24h time. Maybe we need a new method called .toUserPreferredTimeFormatString() to appease pedants who wouldn't want to sully the abstract purity of toLocaleString() with anything so prosaic as formatting according to what the user wants if it does not conform to a standardized locale. :-/Westward
@Westward It's up to the browser vendors then to implement something that tells us what the user's custom time settings are. Also, "formatting according to what the user wants if it does not conform to a standardized locale" That's the whole point of toLocaleString(), isn't it? To format the time using the standard format given a locale?Capreolate
P
1

I know it would be the least favoured way of doing it, but could you not just check the time?

If the time is before 12, set the time to 1pm and test if the output is 13 or 1.

I know its a shoehorn of an idea, but if placed into a nice Date.prototype.is24hour(), returns true; It could work nicely?

I use http://www.datejs.com/ with dates. tends to do everything I need! So you could use that alongside a custom prototype function, and that would give you what you need!

Purree answered 7/10, 2011 at 13:24 Comment(2)
this won't work in chrome because chrome will always return 13Orvil
Then maybe, chrome users just have to stick to 24hr? The implementation would still work for every other browser, but just show 13 on chrome!Purree

© 2022 - 2024 — McMap. All rights reserved.