Locale and specific date format with Moment.js
Asked Answered
A

4

46

I am using Moment.js in my project and formatting dates as follows:

var locale = window.navigator.userLanguage || window.navigator.language;
moment.locale(locale);
someDate.format("L");

It works well but sometimes I need show a date without a year. I can't use something like someDate.format("MM/DD") because in some languages it should be someDate.format("DD/MM"). I need something like L,LL,LLL but without the year.

What can I do?

LTS : 'h:mm:ss A',
LT : 'h:mm A',
L : 'MM/DD/YYYY',
LL : 'MMMM D, YYYY',
LLL : 'MMMM D, YYYY LT',
LLLL : 'dddd, MMMM D, YYYY LT'
Azotic answered 8/12, 2014 at 14:24 Comment(0)
G
32

Okay. This is a little awful, but you knew it was going to be.

First, you can access the actual format string for (for instance) 'L':

var formatL = moment.localeData().longDateFormat('L');

Next, you can perform some surgery on it with judicious regex replacement:

var formatYearlessL = formatL.replace(/Y/g,'').replace(/^\W|\W$|\W\W/,'');

(Which is to say: Remove YYYY, plus the orphaned separator left by its removal)

Then you can use your new format string in a moment format call:

someDate.format(formatYearlessL);

This necessarily makes some assumptions:

  • The order of the month + day numeric format for a locale matches the order for the year + month + day format for that locale, with the year removed.
  • The short form uses separators only between month and day (no leading / trailing separators).
  • The separator for a short numeric date format is always non-alphanumeric.
  • The format consists of numeric elements and separators, rather than a sentence-form format with articles (see RGPT's comment below about Spanish and Portugese, which will also apply to long formats in some other languages).

On a quick review of locale/*.js, these assumptions hold true for every locale file I examined, but there may be some locales that violate them. (ETA: a comment below points out that a German short date format violates the second assumption)

As an additional important caveat, this is likely to be fragile. It is entirely possible that a future version of moment.js will change the location of the data currently in longDateFormat...

Gauffer answered 15/4, 2015 at 4:1 Comment(8)
I'm agree with you, looks like it's a single way for now. I hope they will add more formats in future.Azotic
This won't work in several cultures. Lets take Portuguese as an example: The full date will be: "14 de Fevereiro de 2016". If you just remove the year, in this case "2016", you will get: "14 de Fevereiro de" that is wrong. The correct result should be "14 de Fevereiro". At least Spanish shares the same problem (so with these two we are already talking about more then 650m possible users affected by this bug). I see that other languages will have the same issue.Psychodrama
RGPT - good point, edited the post to add that to the caveats. In general, this approach is only going to be appropriate when you're altering a short, numeric date format with a predictable separator.Gauffer
This procedure doesn’t work for German dates. The long form is DD.MM.YYYY but the short form still requires the trailing dot (DD.MM.) —which gets stripped by the second replace(…).Goodwill
Edited to make explicit the assumption that the short form has no leading/trailing separator, and pointed out this case where it fails. Thanks!Gauffer
Considering the super irregular use of separators around the world this answer is far from sufficient. I'm surprised MomentJs does not cover this.Mccrae
I found this regex more robust to remove the Y in the format. It also works for the above quoted Portuguese, german and both L and l formats. var formatYearLessL = formatL.replace(/[^MD]*YYYY[^MD]*?(?=M|$| [Hh])/, '');Scratchboard
Thank you. Was able to convert the date according to locale and with two digits for the year by using var formatShortYearL = formatL.replace(/Y+/g,'YY') , someDate.format(formatShortYearL); Hope this will help some one.Darelldarelle
R
7

As far as I understand, you can change the date format(without year) for specific languages using MomentJS properties https://momentjs.com/docs/#/customization/long-date-formats/

Example:

moment.updateLocale('en', {
  longDateFormat: {
    LLL: "MMMM Do, LT", // Oct 6th, 4:27 PM
  }
});

moment.updateLocale('ru', {
  longDateFormat: {
    LLL : 'D MMMM, HH:mm', // 6 окт., 16:27
  }
});
Rouault answered 9/10, 2017 at 17:4 Comment(0)
R
1

The library doesn't make it easy to add new formats because they are validated against a regex that we cannot override (var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;).

However, we can override the format function in order to transform our new format into tokens that Moment recognizes.

For example, let's add a new custom format called "LMD". First we need to define it for every locale we want to use:

moment.updateLocale('en', {
   longDateFormat: {
       LMD: 'MMMM D'
   }
});

moment.updateLocale('fr', {
    longDateFormat: {
        LMD: 'D MMMM'
    }
});

Then we override the original format function and transform the inputString ("LMD") into the real tokens we previously defined. After that we just call the original function and let Moment do its job as usual:

var originalMomentFormat = moment.prototype.format;
moment.prototype.format = function (inputString) {
    if (inputString === 'LMD') { // check for your custom types here. maybe use constants or whatever
        inputString = moment.localeData().longDateFormat(inputString);
    }
    return originalMomentFormat.apply(this, [inputString]);
};

Example usage:

moment(someDate).format('LMD');
Responsibility answered 6/12, 2018 at 21:43 Comment(0)
P
0

One option might be to use the CLDR data and get a hold of the Gregorian data and look up main.{locale}.dates.calendars.gregorian.dateTimeFormats.availableFormats. The one you probably want is Md.

I might suggest that you use the Globalize library to make this all easier. You'll note that if you set the date format you want as a skeleton, the library can do all the localization for you.

For example:

.dateFormatter({ skeleton: "GyMMMd" })( new Date() )
// > "Nov 30, 2010 AD"
Pointless answered 28/12, 2019 at 22:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.