Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 714
brillout Avatar asked May 30 '11 04:05

brillout


People also ask

How do I get 12 hour time in Javascript?

var now = new Date(); var hours = now. getHours() % 12 || 12; // 12h instead of 24h, with 12 instead of 0.

How do I get AM PM in node JS?

const formatAMPM = (date) => { let hours = date. getHours(); let minutes = date. getMinutes(); const ampm = hours >= 12 ? 'pm' : 'am'; hours %= 12; hours = hours || 12; minutes = minutes < 10 ? `0${minutes}` : minutes; const strTime = `${hours}:${minutes} ${ampm}`; return strTime; }; console.


5 Answers

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).

like image 109
Heretic Monkey Avatar answered Nov 18 '22 08:11

Heretic Monkey


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.

like image 35
brillout Avatar answered Nov 18 '22 08:11

brillout


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.

like image 36
Paweł Dyda Avatar answered Nov 18 '22 06:11

Paweł Dyda


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

like image 2
Eli Grey Avatar answered Nov 18 '22 07:11

Eli Grey


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!

like image 1
jamesmhaley Avatar answered Nov 18 '22 08:11

jamesmhaley