22

Is it possible to detect if user's machine is using 12 hour clock (am/pm) or 24 hour clock (military time)?

One way would be to check users locales, but then it is just massive list of locale comparison and someone from U.S who wants 12 hour clock can send me just en locale, not US_en and I have no way of knowing her preferences. At the same time someone from U.S might be set her machine to use 12 hour time format and doesn't want 12 hour clock.

EDIT:

date.toLocaleTimeString();

Would work it theory, as user Mouser suggested below, but unfortunately it's bugged on WebKit browsers (tested on Chrome and new Opera on Windows) and for some reason always returns am/pm time.

Example: http://jsfiddle.net/sjuaL3p4/

So I guess I have to rephrase my question if anyone has an idea how to accomplish it on webkit browsers also.

Badr Hari
  • 8,114
  • 18
  • 67
  • 100

6 Answers6

11

You can utilize the Internationalization API with resolvedOptions to determine the hourCycle:

const locale = navigator.language
Intl.DateTimeFormat(locale,  { hour: 'numeric' }).resolvedOptions().hourCycle // ?
Intl.DateTimeFormat('en-US', { hour: 'numeric' }).resolvedOptions().hourCycle // h12
Intl.DateTimeFormat('en-GB', { hour: 'numeric' }).resolvedOptions().hourCycle // h23
kgreen
  • 518
  • 6
  • 13
  • This doesn't say what the system is actually set to, it just makes a guess based on the browser language setting (which might be different to the system language and does not necessarily reflect time format settings). Also, *hourCycle* is the wrong option, it should be *hour12* (true/false) or *dayPeriod* (am/pm). – RobG Aug 09 '21 at 10:02
  • Your browser should consume the locale from OS settings, but to your point that can get complicated. Say for example the OS locale is set to `en-US`, but you also prefer `h23` time. I don't believe browsers have a way to detect this at the moment. I haven't researched it in a few years, so I would be curious if anything has changed. Using `hour12` works too, but I wouldn't recommend `dayPeriod` as only Safari seems to return that property. – kgreen Aug 12 '21 at 09:30
  • The formats produced by *toLocaleString* are based on the language code ("locale"), options provided and nominally data from the [CLDR project](http://cldr.unicode.org). They aren't (can't be) based on system settings because a particular implementation should produce the same format for the same language and options regardless of the host system. Otherwise, a developer specifying a particular language and options would have no idea what format might be produced if they were more–or–less randomly overridden by system settings. – RobG Aug 12 '21 at 10:27
  • Sadly `navigator.language` is complicated - Safari will consume the OS's language settings when determining its `navigator.language` value. While Chrome and Firefox both have their own internal language configurations which they pull from. – kgreen Aug 16 '21 at 13:14
9

This solution works in most browsers, but bugs in Chrome.

    var date = new Date(Date.UTC(2012, 11, 12, 3, 0, 0));
    var dateString = date.toLocaleTimeString();

    //apparently toLocaleTimeString() has a bug in Chrome. toString() however returns 12/24 hour formats. If one of two contains AM/PM execute 12 hour coding.
    if (dateString.match(/am|pm/i) || date.toString().match(/am|pm/i) )
    {
        //12 hour clock
        console.log("12 hour");
    }
    else
    {
        //24 hour clock
        console.log("24 hour");
    }

Workaround Chrome; A proof of concept

This is an ugly work-around for Chrome. It sniffs out the users country_code via reverse geolocation. That code is checked against an array with countries using the 12 hour system. This solution is ugly because you need user permission to get geolocation data and if the users locale is different it will give the wrong information, but will provide you with the country of the user. I strongly advise against using this example. It's purely for inspiration purposes. I made it up as a proof of concept.

 function getClockChrome() {

  navigator.geolocation.getCurrentPosition(function(pos) {
   var url = "http://nominatim.openstreetmap.org/reverse?format=json&lat="+pos.coords.latitude+"&lon="+pos.coords.longitude+"&addressdetails=1&accept-language=en_US&json_callback=chromeClockCallBack";
   var script = document.createElement('script');
   script.src = url;
   document.body.appendChild(script);
  },
  
  function()
  {
   //no access to location
  },
  

  {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 0
  }
  );

 }

 getClockChrome();

 var dateCountryCode = ['US', 'GB', 'PH', 'CA', 'AU', 'NZ', 'IN', 'EG', 'SA', 'CO', 'PK', 'MY'];
 function chromeClockCallBack(data)
 {
        //Request succeeded
  if (dateCountryCode.indexOf(data.address.country_code.toUpperCase()) > -1)
  {
   alert("12 hour clock");
  }
  else
  {
   alert("24 hour clock");
  }
 }
Mouser
  • 13,132
  • 3
  • 28
  • 54
  • 2
    http://jsfiddle.net/sjuaL3p4/ - I'm using 24 hour clock, but the toLocaleTimeString return AM/PM time nevertheless... I think you need to specify the locale you want to use but that defeats the purpose... – Badr Hari Dec 25 '14 at 14:36
  • Tested this on my machine with 24 hour clock settings and 12 hour clock settings, showed the correct value. – Mouser Dec 25 '14 at 14:41
  • https://code.google.com/p/chromium/issues/detail?id=3607 Looks like a chromium bug. Otherwise your way of doing it would be perfect... – Badr Hari Dec 25 '14 at 14:47
  • @BadrHari I didn't know professionals boxers could code, but I've made an edit, should maybe do the trick for all browsers. – Mouser Dec 27 '14 at 01:29
  • We can but unfortunately we can't overcome this bug... toString() returns for all the americans 24-hour clock... – Badr Hari Dec 27 '14 at 19:13
  • While geolocation was something I wanted to avoid, it looks like because of webkit bug I have to go to that direction. Thank you for thinking about this problem with me. – Badr Hari Jan 02 '15 at 19:36
  • 2
    This doesn't work reliably as *toLocaleString* is implementation dependent so may or may not reflect the system settings. On MacOS and Safari, it doesn't matter what I set the system time format to, the output is always "24 hour", Firefox is always "12 hour". – RobG Aug 09 '21 at 09:58
  • Wouldn't this fail if the user's timezone had the hard coded time in the first 12 hours of the day (AM not PM) – Justin Nov 09 '21 at 19:30
2

toLocaleTimeString should give the time in a format that reflects the user's preferences but it is unreliable.

I'm on a Debian system. The output of locale is:

LANG=en_US.utf8
LANGUAGE=
LC_CTYPE="en_US.utf8"
LC_NUMERIC="en_US.utf8"
LC_TIME="en_US.utf8"
LC_COLLATE="en_US.utf8"
LC_MONETARY="en_US.utf8"
LC_MESSAGES="en_US.utf8"
LC_PAPER="en_US.utf8"
LC_NAME="en_US.utf8"
LC_ADDRESS="en_US.utf8"
LC_TELEPHONE="en_US.utf8"
LC_MEASUREMENT="en_US.utf8"
LC_IDENTIFICATION="en_US.utf8"
LC_ALL=

Here are a few experiments with date:

$ date +%X
05:23:32 PM
$ LANG=en_GB date +%X
17:24:06
$ LC_TIME=en_GB date +%X
17:24:22

The %X format tells date to output the time according to the locale. The results above are exactly as expected. Setting LC_TIME is a way to change only the time format but keep everything else in the locale intact. So someone in the US could use a 24h time format even if the default for en_US is 12.

I've tried Mouser's script on my system:

$ firefox --no-remote
[Shows a time in the 12 hour format, as expected.]
$ LANG=en_GB firefox --no-remote
[Shows a time in the 24 hour format, as expected.]

So far so good. However,

$ LC_TIME=en_GB firefox --no-remote
[Shows a time in the 12 hour format, this is wrong!]

I get the same results with Chrome. It seems that both Firefox and Chrome ignore LC_TIME.

Community
  • 1
  • 1
Louis
  • 146,715
  • 28
  • 274
  • 320
1

Besides the fact that this should never be done, .toLocaleTimeString() wouldn't be a good solution even if it did work across browsers. It returns a time string following the format set by the user's computer, but it's possible to have a 24-hour clock that still shows AM/PM, as well as a 12-hour clock that doesn't. It's not feasible to accurately or reliably detect this.

Hydrothermal
  • 4,851
  • 7
  • 26
  • 45
0

This simple line seems to be working for me.

var is24 = ((new Date(2014, 01, 01, 15, 0, 0, 0)).toLocaleTimeString().indexOf("15") > -1);

Though it still doesn't work in Chrome (works in IE and Firefox)

David
  • 3,653
  • 2
  • 24
  • 26
0

This works for me in Chrome. I used the selected answer from @Mouser which was not working in Chrome.

    let result = false;
    const date = new Date();
    const dateString = date.toLocaleTimeString(language, {
        timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
        timeStyle: 'short'
    });
    
    if (dateString.match(/am|pm/i) || date.toString().match(/am|pm/i)) {
        result = true;
    }
    else {
        result = false;
    }
Andre RB
  • 306
  • 4
  • 7