12

I am trying to determine users locale date format so that I can use it later to show date in specific format.

I know I can use toLocaleDateString() to get the date format.

Let's say I got 1/2/2017. How to determine whether this is in dd/mm format or mm/dd format?

One thing I have tried is I can get current date and month from new Date() and check based on that (manually). When the date is 2/2/2016 or 3/3/2016 how to determine which one is date and which one is month?

Is there any solution anybody has for this issue?

Also I have looked into moment.js. If some solution available there also I will be happy to use that.

Shrabanee
  • 2,706
  • 1
  • 18
  • 30
  • Have you considered using moment [`localeData`](http://momentjs.com/docs/#/i18n/locale-data/)? Is your question similar to [this one](http://stackoverflow.com/q/42437261/4131048)? – VincenzoC Apr 12 '17 at 12:33
  • The default format returned by the browser's *toLocaleString* and the format that a user typically uses for dates are two entirely different things. Nearly all browsers use US format by default. They don't reliably respect user preferences for *toLocaleString*. The best solution is to use an unambiguous format, e.g. use a name or abbreviation for the month. – RobG Apr 12 '17 at 22:44
  • There are already [*many questions*](http://stackoverflow.com/search?q=[javascript]+get+local+date+format) on this, surely at least one is a duplicate? E.g. [*how to get local date/time format*](http://stackoverflow.com/questions/17647644/how-to-get-local-date-time-format?s=1|3.3068), [*How to get client system local date and time format*](http://stackoverflow.com/questions/31679141/how-to-get-client-system-local-date-and-time-format?s=2|1.6050). – RobG Apr 12 '17 at 22:47
  • @RobG I have checked above questions and some other questions also before asking this one. – Shrabanee Apr 13 '17 at 05:59
  • `The best solution is to use an unambiguous format, e.g. use a name or abbreviation for the month` I have tried doing this but this is not my requirement. I want to use `dd/mm/yyyy` or `mm/dd/yyyy` based on user machine's date format. – Shrabanee Apr 13 '17 at 06:00
  • 1
    @Shrabanee—cool, so you should have come to the conclusion that you can't **reliably** determine the user's preferred format from their browser. ;-) – RobG Apr 14 '17 at 05:47
  • @RobG Yes. Seems like that. – Shrabanee Apr 14 '17 at 05:49
  • @RobG on browsers where Intl.DateTimeFormat is supported, toLocaleString() generates the same result given the same options, and this result by definition respects the system or specified locale settings. This includes all modern browsers; i.e. for almost everyone's intents and purposes it can be relied on as of 2019. – Adam Leggett Jan 09 '19 at 16:54
  • @VincenzoC moment with all locale data compiled in is insanely large. – Adam Leggett Jan 09 '19 at 16:56
  • @AdamLeggett I agree with you, but note that there was no reference to file size problems in the original question. Maybe you can use `Intl.DateTimeFormat` (or [luxon](http://moment.github.io/luxon/), or [date-fns](https://date-fns.org/)) depending on your needs. – VincenzoC Jan 09 '19 at 17:11
  • @VincenzoC I produced a solution that uses `Intl` in an answer I just added below. Hope it helps someone. – Adam Leggett Jan 09 '19 at 17:25
  • @AdamLeggett—that is not true and can be disproved with minimal testing. E.g. `new Date().toLocaleString(undefined, {hour12: false});` produces different results in Firefox and Chrome depending on system settings for the default language (which is what the OP is trying to do). – RobG Jan 09 '19 at 20:46
  • @RobG I just tested this in Chrome 71 and Firefox 64 with my system locale set to en-US and for both cases got 12/31/1970 00:00:00. What am I missing? – Adam Leggett Jan 09 '19 at 22:24
  • @AdamLeggett— **depending on system settings**. Move away from en-US. The accepted answer produces contradictory results for me in Firefox, claiming one format but producing something different. – RobG Jan 09 '19 at 22:31
  • @RobG can you please share what your system locale is? This could be useful information for the rest of us. – Adam Leggett Jan 09 '19 at 22:38
  • @AdamLeggett—the results were achieved with system language settings for en-AU and browser defaults. When the browser produces contradictory results (like claiming m/d/y but presenting d/m/y) it's self evident that things are awry. The difficulty is that it's a pain to have to change system settings to do testing (which means also restarting browsers like Chrome). – RobG Jan 09 '19 at 23:35

4 Answers4

21

You can do this without using moment also

function getDateFormatString(lang = 'default') {
  const formatObj = new Intl.DateTimeFormat(lang).formatToParts(new Date());

  return formatObj
    .map(obj => {
      switch (obj.type) {
        case "day":
          return "DD";
        case "month":
          return "MM";
        case "year":
          return "YYYY";
        default:
          return obj.value;
      }
    })
    .join("");
}

// Browser default
console.log(getDateFormatString());
// locale="en-US" 
console.log(getDateFormatString('en-us')); // MM/DD/YYYY
// locale="en-GB"
console.log(getDateFormatString('en-gb')); // DD/MM/YYYY
RobG
  • 142,382
  • 31
  • 172
  • 209
Praveen Kumar
  • 332
  • 4
  • 11
11

Using moment localeData you can get localized longDateFormat. This will give you localized format for year, month and day. You can use this value to parse your input string locale-aware.

Here a live example:

// Get user locale
var locale = window.navigator.userLanguage || window.navigator.language;
// Set locale to moment
moment.locale(locale);

// Get locale data
var localeData = moment.localeData();
var format = localeData.longDateFormat('L');

var m1 = moment('2/2/2016', format);
console.log(m1.format()); // February 2nd 2016
console.log(m1.format(format) + ' using format: ' + format);

var m2 = moment('5/1/2017', format);
console.log(m2.format());
console.log(m2.format(format) + ' using format: ' + format);
// January 5th 2017 for locales that use DD/MM/YYYY
// May 1st 2017 for locales that use MM/DD/YYYY
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment-with-locales.min.js"></script>

This code will not work for locales that uses format that starts with year (e.g. YYYY.MM.DD. for Hungarian locale)

VincenzoC
  • 30,117
  • 12
  • 90
  • 112
  • Your example should use a date that is confusable, such as 5/1/2017. 2/2/2016 is 2 February regardless of which value is the month and which is the day. ;-) – RobG Apr 12 '17 at 22:45
  • @VincenzoC this is not helping me to achieve what I want. :( – Shrabanee Apr 13 '17 at 06:33
  • I've edited my answer adding another example as suggested by @RobG. I hope this way my answer is more clear. Shrabanee I'm sorry you didn't find my answer useful, please try to explain why. I think that linked questions could help, can you detail further what is your requirement? – VincenzoC Apr 13 '17 at 08:07
  • Will it work across browsers of the same system? Or it will behave differently? Do you have any idea? I will try this out anyways ;). – Shrabanee Apr 13 '17 at 10:16
  • It is not showing the format same as the system's date format :( . Will it not determine based on the system's date format? – Shrabanee Apr 13 '17 at 10:29
  • The code above will give the same result on each browser, the results depends only on the locale. What do you mean with _system_? – VincenzoC Apr 13 '17 at 14:51
  • 1
    This does not work in Safari or Firefox at least. For me, 5/1/2017 should be 5 February but is returned as 1 May: `05/01/2017 using format: MM/DD/YYYY`. There is no way to reliably determine the user's preferred date format from the language setting (particularly as nearly all default to en-us regardless of system settings or the user's actual preference). – RobG Apr 14 '17 at 05:42
5

Answering this for 2019. If you really want to be thorough, you can try to handle low market share legacy browsers, non-Latin numbering systems or non-Gregorian calendars.

This could all be reduced to a regex replace, but if you're going to be parsing dates, you're going to want to keep the field indices.

function dateFormat(language) {
  const sample = window.Intl ? new Intl.DateTimeFormat(language, {
    numberingSystem: 'latn',
    calendar: 'gregory'
  }).format(new Date(1970, 11, 31)) : '';

  let mm = 0,
      mi = sample.indexOf(12);
  let dd = 1,
      di = sample.indexOf(31);
  let yy = 2,
      yi = sample.indexOf(1970);

  // IE 10 or earlier, iOS 9 or earlier, non-Latin numbering system
  // or non-Gregorian calendar; fall back to mm/dd/yyyy
  if (yi >= 0 && mi >= 0 && di >= 0) {
    mm = (mi > yi) + (mi > di);
    dd = (di > yi) + (di > mi);
    yy = (yi > mi) + (yi > di);
  }

  let r = [];
  r[yy] = 'yyyy';
  r[mm] = 'mm';
  r[dd] = 'dd';

  return r.join(sample.match(/[-.]/) || '/');
}

console.log(dateFormat());        // 'mm/dd/yyyy' if in US
console.log(dateFormat('de'));    // 'dd.mm.yyyy'
console.log(dateFormat('en-AU')); // 'dd/mm/yyyy'
Adam Leggett
  • 3,714
  • 30
  • 24
  • Note that in Safari, '1970-12-31T00:00' is (incorrectly) treated as UTC. Irrelevant in this case, but `new Date(1970, 11, 31)` (for example) is less to type and not problematic. ;-) The enduring issue though is how reliably the browser reports the "*users locale date format*" as there is rarely one format used exclusively anywhere. – RobG Jan 09 '19 at 23:44
  • @RobG even though this is true, I personally prefer to let the browser do what it wants here. If you are providing something like a date entry field, you should be giving it a placeholder or some such with the expected format, so the worst case will be that the user is aware he needs to enter a format that's not the one he's accustomed to. The alternative is to bring in an entire huge library like Moment, which I have no interest in, since it's more important to be displaying and parsing the same format than it is to be consistent with format across browsers. – Adam Leggett Feb 05 '19 at 16:12
  • @RobG of course, it appears you deal with dates and times a lot, which suggests you work on calendar software or some such. In that case, the incentive for you leans more toward bringing in the huge library. – Adam Leggett Feb 05 '19 at 16:17
  • Not at all. I strongly advocate for unambiguous formats, that way it doesn't matter if you use 5 May 2019 or May 5 2019 or whatever other ordering of values you want. I also think expecting a user to know the language setting of the browser they're using is not reasonable, particularly given the IOT and huge variety of user agents. – RobG Oct 01 '19 at 02:50
  • @RobG of course, it is always best to provide a language/country selector on a site. If you do, you can feed the setting to the function in my code snippet above. By default, it will use the browser's locale setting, which by default, uses the system or device locale setting, which for a majority of users will be the correct one. As of late 2019, I am seeing all browsers and devices return dd/mm/yyyy for en-AU, which I added to the snippet above. Providing a format selector independent of the country selector seems like overkill for most applications. – Adam Leggett Oct 01 '19 at 15:26
  • Consider an American visiting New Zealand using a PC in an internet cafe or library. What settings will the host PC have? What format date will be presented to them using the host settings? 2/10/2019 is likely to confuse, 02-Oct-2019 might be less "colloquial" but leaves no doubt about the intended date. I find the need to transmit information precisely and unambiguously trumps attempting to appease user whims every time (please don't get me started on "user friendly" dates like "about an hour ago"). ;-) – RobG Oct 02 '19 at 00:28
  • @RobG I can't ask users to enter "02-Oct-2019" (well, maybe I could with some complex auto fill code, but what about non-English languages?). I can ask them to select it on a calendar, but my date entry boxes drop down a calendar picker on focus anyway and many users do not want to have to navigate that. I would be interested to see what date entry widget you have done. I haven't gotten complaints about mine, but I am keenly aware there is always a better way. – Adam Leggett Oct 02 '19 at 15:16
  • It depends on the use case. Sites with a very wide audience and little data entry like airline booking sites tend to force users to use a picker. Data entry focussed apps like accounting usually provide user date entry but also display the parsed result (usually by replacing the entered text). Offering a picker and data entry also is common. The key is usability and clarity. I've rarely been responsible for final design (i.e. the client), but try to influence it as much as I can along the above principles. Sometimes I win, sometimes not. – RobG Oct 03 '19 at 09:14
-2

You can pass an options argument to the toLocaleDateString to get the date as you wish.

var date = new Date();
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
console.log(date.toLocaleDateString('en',options));
Ozan
  • 1,623
  • 3
  • 13
  • 25
  • 2
    Please read the question properly. This output is not what I want to determine. – Shrabanee Apr 12 '17 at 12:22
  • Sure I did read it properly. However you will have to validate the date based on the output you get. The index of the month in the given string will determine the date format. Otherwise using regex to validate only one of type of format will facilitate the hassle. – Ozan Apr 12 '17 at 12:28
  • As I have asked in my question, say I have `2/2/2016`. Now as per your answer I will get it as February 2, 2016. How I will check whether it is in 'dd/mm' format or 'mm/dd' format? For dated like `3/2/2016` I can determine the format. – Shrabanee Apr 13 '17 at 06:13