101

I'm trying to format a date in Java in different ways based on the given locale. For instance I want English users to see "Nov 1, 2009" (formatted by "MMM d, yyyy") and Norwegian users to see "1. nov. 2009" ("d. MMM. yyyy").

The month part works OK if I add the locale to the SimpleDateFormat constructor, but what about the rest?

I was hoping I could add format strings paired with locales to SimpleDateFormat, but I can't find any way to do this. Is it possible or do I need to let my code check the locale and add the corresponding format string?

1

10 Answers 10

97

Use DateFormat.getDateInstance(int style, Locale locale) instead of creating your own patterns with SimpleDateFormat.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, this works OK. I went for DateFormat.LONG which suits my needs the best. BTW, Norwegian locale and DateFormat.MEDIUM is crap(!)
You are right, the strange MEDIUM pattern for Norwegian never occured to me. The weekday is also missing in the FULL format, as opposed to most other locales.
FYI, the troublesome classes of java.util.Date and SimpleDateFormat are now legacy, supplanted by the java.time classes.
96

SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE dd MMM yyyy", Locale.ENGLISH);
String formatted = dateFormat.format(the_date_you_want_here);

Comments

35

Use the style + locale: DateFormat.getDateInstance(int style, Locale locale)

Check http://java.sun.com/j2se/1.5.0/docs/api/java/text/DateFormat.html

Run the following example to see the differences:

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

public class DateFormatDemoSO {
  public static void main(String args[]) {
    int style = DateFormat.MEDIUM;
    //Also try with style = DateFormat.FULL and DateFormat.SHORT
    Date date = new Date();
    DateFormat df;
    df = DateFormat.getDateInstance(style, Locale.UK);
    System.out.println("United Kingdom: " + df.format(date));
    df = DateFormat.getDateInstance(style, Locale.US);
    System.out.println("USA: " + df.format(date));   
    df = DateFormat.getDateInstance(style, Locale.FRANCE);
    System.out.println("France: " + df.format(date));
    df = DateFormat.getDateInstance(style, Locale.ITALY);
    System.out.println("Italy: " + df.format(date));
    df = DateFormat.getDateInstance(style, Locale.JAPAN);
    System.out.println("Japan: " + df.format(date));
  }
}

Output:

United Kingdom: 25-Sep-2017
USA: Sep 25, 2017
France: 25 sept. 2017
Italy: 25-set-2017
Japan: 2017/09/25

2 Comments

Don't forget the DateFormat.LONG and DateFormat.DEFAULT styles!
Right idea to specify style & locale. But the classes used are now outmoded. The troublesome old date-time classes such as java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat are now legacy, supplanted by the java.time classes built into Java 8 & Java 9. See Tutorial by Oracle.
21

tl;dr

LocalDate
.now()
.format(
    DateTimeFormatter
        .ofLocalizedDate( FormatStyle.MEDIUM ) )
        .localizedBy( Locale.of( "no" , "NO" ) );
);

The troublesome classes of java.util.Date and SimpleDateFormat are now legacy, supplanted by the java.time classes.

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

DateTimeFormatter

Use DateTimeFormatter to generate strings representing only the date-portion or the time-portion.

The DateTimeFormatter class can automatically localize.

To localize, specify:

  • FormatStyle to determine how long or abbreviated should the string be.
  • Locale to determine (a) the human language for translation of name of day, name of month, and such, and (b) the cultural norms deciding issues of abbreviation, capitalization, punctuation, and such.

Example:

Locale l = Locale.of( "fr" , "CA" ) ;  // French language, Canada culture. 
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.FULL ).localizedBy( l );
String output = ld.format( f );

Going the other direction, you can parse a localized string.

LocalDate ld = LocalDate.parse( input , f );

Note that the locale and time zone are completely orthogonal issues. You can have a Montréal moment presented in Japanese language or an Auckland New Zealand moment presented in Hindi language.

Another example: Change 6 junio 2012 (Spanish) to 2012-06-06 (standard ISO 8601 format). The java.time classes use ISO 8601 formats by default for parsing/generating strings.

String input = "6 junio 2012";
Locale l = Locale.of( "es" , "ES" );  // In earlier Java: Locale.forLanguageTag( "es-ES" )
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "d MMMM uuuu" , l );
LocalDate ld = LocalDate.parse ( input , f );
String output = ld.toString();  // 2012-06-06. 

Peruse formats

Here is some example code for perusing the results of multiple formats in multiple locales, automatically localized.

An EnumSet is an implementation of Set, highly optimized for both low memory usage and fast execution speed when collecting Enum objects. So, EnumSet.allOf( FormatStyle.class ) gives us a collection of all four of the FormatStyle enum objects to loop. For more info, see Oracle Tutorial on enum types.

LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 23 );

List < Locale > locales = new ArrayList <>( 3 );
locales.add( Locale.of( "fr" , "CA" ) );  // French language, Canada culture.
locales.add( Locale.of( "no" , "NO" ) );  // Norwegian language, Norway culture.
locales.add( Locale.of( "en" , "US" ) );  // English language, United States culture.

// Or use all locales (almost 800 of them, for about 120K text results).
// Locale[] locales = Locale.getAvailableLocales(); // All known locales. Almost 800 of them.

for ( Locale locale : locales )
{
    System.out.println( "------|  LOCALE: " + locale + " — " + locale.getDisplayName() + "  |----------------------------------" + System.lineSeparator() );

    for ( FormatStyle style : EnumSet.allOf( FormatStyle.class ) )
    {
        DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( style ).localizedBy( locale );
        String output = ld.format( f );
        System.out.println( output );
    }
    System.out.println( "" );
}
System.out.println( "« fin »" + System.lineSeparator() );

Output.

------|  LOCALE: fr_CA — French (Canada)  |----------------------------------

mardi 23 janvier 2018
23 janvier 2018
23 janv. 2018
18-01-23

------|  LOCALE: no_NO — Norwegian (Norway)  |----------------------------------

tirsdag 23. januar 2018
23. januar 2018
23. jan. 2018
23.01.2018

------|  LOCALE: en_US — English (United States)  |----------------------------------

Tuesday, January 23, 2018
January 23, 2018
Jan 23, 2018
1/23/18

« fin »

withLocale versus localizedBy

By the way, let's talk about localizing DateTimeFormatter.

The original java.time classes in Java 8 came with the method DateTimeFormatter#withLocale. This lets you specify a a Locale to use during localization instead of implicitly using the JVM’s current default Locale.

Later, in Java 10, the DateTimeFormatter class gained the localizedBy method. This method may override some aspects of your specified formatting pattern, for deeper effects of localization. For example, rather than use Western Arabic numerals (0-9), the formatter employs the digits of that locale’s script. You may or may not want these effects.

While I have not yet given this much consideration, and I am no expert on localization, my initial thoughts are:

  • If you are specifying a formatting pattern, you probably want this exact pattern. If so, call withLocale rather than localizedBy.
  • If you want deeper localization, do not specify a formatting pattern. Instead of an explicit pattern, let DateTimeFormatter determine the appropriate format by calling one of the ofLocalized… methods. Then call localizedBy to get the deeper localization effects. With this approach in Java 25 we see in code below that of the 1,158 locales available, exactly 100 locales show a difference when using localizedBy versus withLocale for ZonedDateTime with a soft-coded formatter.

That would be my general suggestion. Of course, I am sure that in particular circumstances, depending on the needs and expectations of your users and stakeholders, you may want to otherwise combine soft-coding/hard-coding formats with localizedBy/withLocale.

Here is some example code showing these differences of localizedBy versus withLocale across all locales. Have fun playing around with this code.

package work.basil.example.time;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.*;

public class ExLocalizedBy
{
    static void main ( )
    {
        IO.println( "Runtime.version: " + Runtime.version() );

        ZoneId z = ZoneId.systemDefault();
        ZonedDateTime zdt = ZonedDateTime.now( z );
        //List < Locale > locales =
        //        List.of(
        //                Locale.of( "fr" , "CA" ) ,  // French language, Canada culture.
        //                Locale.of( "no" , "NO" ) ,  // Norwegian language, Norway culture.
        //                Locale.of( "en" , "US" )    // English language, United States culture.
        //        );
        List < Locale > locales = List.of( Locale.getAvailableLocales() ); // All known locales. Almost 800 of them.
        IO.println( "locales.length = " + locales.size() );

        int countDifferences = 0;
        Set < Locale > localesWithAnyDifferences = new HashSet <>( locales.size() );
        for ( Locale locale : locales )
        {
            //IO.println( "------|  LOCALE: " + locale + " — " + locale.getDisplayName() + "  |----------------------------------" + System.lineSeparator() );

            for ( FormatStyle style : EnumSet.allOf( FormatStyle.class ) )
            {
                DateTimeFormatter f = DateTimeFormatter.ofPattern( "…" );

                // Old-way: DateTimeFormatter#withLocale
                DateTimeFormatter fWithLocale =
                        DateTimeFormatter
                                .ofLocalizedDate( style )
                                .withLocale( locale );  // ⬅️ Critical line.
                String outputWithLocale = zdt.format( fWithLocale );

                // New-way: DateTimeFormatter#localizedBy
                DateTimeFormatter fLocalizedBy =
                        DateTimeFormatter
                                .ofLocalizedDate( style )
                                .localizedBy( locale );  // ⬅️ Critical line.
                String outputLocalizedBy = zdt.format( fLocalizedBy );

                if ( ! outputWithLocale.contentEquals( outputLocalizedBy ) )
                {
                    countDifferences++;
                    localesWithAnyDifferences.add( locale ); // `Set` eliminates duplicates.
                    IO.println( "DIFFERENCE DETECTED for style " + style + ": locale = " + locale + " `withLocale`: " + outputWithLocale + " versus `localizedBy`: " + outputLocalizedBy );
                }
            }
        }
        IO.println( "countDifferences = " + countDifferences );
        IO.println( "localesWithAnyDifferences.size() = " + localesWithAnyDifferences.size() );
        localesWithAnyDifferences.forEach( locale -> IO.println( locale.toString() + " |  " + locale.getDisplayName() ) );
        IO.println( "« fin »" + System.lineSeparator() );
    }
}

Here is an excerpt of output.

Runtime.version: 25+35-3488
locales.length = 1158
DIFFERENCE DETECTED for style FULL: locale = ar_BH `withLocale`: السبت، 23 أغسطس 2025 versus `localizedBy`: السبت، ٢٣ أغسطس ٢٠٢٥
DIFFERENCE DETECTED for style LONG: locale = ar_BH `withLocale`: 23 أغسطس 2025 versus `localizedBy`: ٢٣ أغسطس ٢٠٢٥
DIFFERENCE DETECTED for style MEDIUM: locale = ar_BH `withLocale`: 23‏/08‏/2025 versus `localizedBy`: ٢٣‏/٠٨‏/٢٠٢٥
DIFFERENCE DETECTED for style SHORT: locale = ar_BH `withLocale`: 23‏/8‏/2025 versus `localizedBy`: ٢٣‏/٨‏/٢٠٢٥
DIFFERENCE DETECTED for style FULL: locale = raj `withLocale`: 2025 अगस्त 23, शनिवार versus `localizedBy`: २०२५ अगस्त २३, शनिवार
DIFFERENCE DETECTED for style LONG: locale = raj `withLocale`: 2025 अगस्त 23 versus `localizedBy`: २०२५ अगस्त २३
DIFFERENCE DETECTED for style MEDIUM: locale = raj `withLocale`: 2025 अगस्त 23 versus `localizedBy`: २०२५ अगस्त २३
DIFFERENCE DETECTED for style SHORT: locale = raj `withLocale`: 2025-08-23 versus `localizedBy`: २०२५-०८-२३
DIFFERENCE DETECTED for style FULL: locale = ne_NP_#Deva `withLocale`: 2025 अगस्ट 23, शनिबार versus `localizedBy`: २०२५ अगस्ट २३, शनिबार
DIFFERENCE DETECTED for style LONG: locale = ne_NP_#Deva `withLocale`: 2025 अगस्ट 23 versus `localizedBy`: २०२५ अगस्ट २३
…
DIFFERENCE DETECTED for style LONG: locale = bgc_IN `withLocale`: 2025 अगस्त 23 versus `localizedBy`: २०२५ अगस्त २३
DIFFERENCE DETECTED for style MEDIUM: locale = bgc_IN `withLocale`: 2025 अगस्त 23 versus `localizedBy`: २०२५ अगस्त २३
DIFFERENCE DETECTED for style SHORT: locale = bgc_IN `withLocale`: 2025-08-23 versus `localizedBy`: २०२५-०८-२३
countDifferences = 400
localesWithAnyDifferences.size() = 100
ar_BH | Arabic (Bahrain)
raj | Rajasthani
ne_NP_#Deva | Nepali (Devanagari, Nepal)
ar_DJ | Arabic (Djibouti)
mzn | Mazanderani
sat__#Olck | Santali (Ol Chiki)
my_MM | Burmese (Myanmar (Burma))
ar_ER | Arabic (Eritrea)
ur_IN | Urdu (India)
ar_EG | Arabic (Egypt)
ks_IN | Kashmiri (India)
ckb | Central Kurdish
sat_IN | Santali (India)
sa_IN_#Deva | Sanskrit (Devanagari, India)
ar_JO | Arabic (Jordan)
pa__#Arab | Punjabi (Arabic)
ne_IN | Nepali (India)
ar_IQ | Arabic (Iraq)
ar_IL | Arabic (Israel)
ar_LB | Arabic (Lebanon)
ar_KW | Arabic (Kuwait)
ar_KM | Arabic (Comoros)
ne_NP | Nepali (Nepal)
dz_BT_#Tibt | Dzongkha (Tibetan, Bhutan)
ar_MR | Arabic (Mauritania)
bn_BD_#Beng | Bangla (Bangla, Bangladesh)
ar_PS | Arabic (Palestinian Territories)
ar_OM | Arabic (Oman)
ckb_IR | Central Kurdish (Iran)
ckb_IQ | Central Kurdish (Iraq)
ar_SA | Arabic (Saudi Arabia)
ar_QA | Arabic (Qatar)
ar_TD | Arabic (Chad)
ar_SO | Arabic (Somalia)
raj_IN_#Deva | Rajasthani (Devanagari, India)
ar_SD | Arabic (Sudan)
ar_SY | Arabic (Syria)
ar_SS | Arabic (South Sudan)
ks_IN_#Arab | Kashmiri (Arabic, India)
nqo_GN | N’Ko (Guinea)
uz__#Arab | Uzbek (Arabic)
lrc_IR_#Arab | Northern Luri (Arabic, Iran)
ar_YE | Arabic (Yemen)
sd_IN | Sindhi (India)
nqo | N’Ko
dz_BT | Dzongkha (Bhutan)
ckb_IQ_#Arab | Central Kurdish (Arabic, Iraq)
lrc | Northern Luri
lrc_IR | Northern Luri (Iran)
lrc_IQ | Northern Luri (Iraq)
sat | Santali
sd__#Arab | Sindhi (Arabic)
th_TH_TH_#u-nu-thai | Thai (Thailand, TH, Thai Digits)
as | Assamese
sd_PK | Sindhi (Pakistan)
mni_IN | Manipuri (India)
bn | Bangla
ks__#Arab | Kashmiri (Arabic)
bgc_IN_#Deva | Haryanvi (Devanagari, India)
dz | Dzongkha
fa | Persian
sat_IN_#Olck | Santali (Ol Chiki, India)
bgc | Haryanvi
bho | Bhojpuri
raj_IN | Rajasthani (India)
as_IN | Assamese (India)
mzn_IR_#Arab | Mazanderani (Arabic, Iran)
uz_AF_#Arab | Uzbek (Arabic, Afghanistan)
ps_AF | Pashto (Afghanistan)
ks | Kashmiri
ar_EG_#Arab | Arabic (Arabic, Egypt)
nqo_GN_#Nkoo | N’Ko (N’Ko, Guinea)
bn_BD | Bangla (Bangladesh)
mr | Marathi
my | Burmese
ne | Nepali
mr_IN | Marathi (India)
ja_JP_JP_#u-ca-japanese | Japanese (Japan, JP, Japanese Calendar)
ps | Pashto
fa_AF | Persian (Afghanistan)
sa | Sanskrit
sd | Sindhi
bho_IN | Bhojpuri (India)
bn_IN | Bangla (India)
ps_AF_#Arab | Pashto (Arabic, Afghanistan)
as_IN_#Beng | Assamese (Bangla, India)
bho_IN_#Deva | Bhojpuri (Devanagari, India)
my_MM_#Mymr | Burmese (Myanmar, Myanmar (Burma))
mni | Manipuri
pa_PK_#Arab | Punjabi (Arabic, Pakistan)
ps_PK | Pashto (Pakistan)
fa_IR | Persian (Iran)
sd_PK_#Arab | Sindhi (Arabic, Pakistan)
mni_IN_#Beng | Manipuri (Bangla, India)
mr_IN_#Deva | Marathi (Devanagari, India)
mzn_IR | Mazanderani (Iran)
mni__#Beng | Manipuri (Bangla)
sa_IN | Sanskrit (India)
fa_IR_#Arab | Persian (Arabic, Iran)
bgc_IN | Haryanvi (India)
« fin »

For more discussion, see this Question by Mahozad.

Comments

9

Localization of date string:

Based on redsonic's post:

private String localizeDate(String inputdate, Locale locale) { 

    Date date = new Date();
    SimpleDateFormat dateFormatCN = new SimpleDateFormat("dd-MMM-yyyy", locale);       
    SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");


    try {
        date = dateFormat.parse(inputdate);
    } catch (ParseException e) {
        log.warn("Input date was not correct. Can not localize it.");
        return inputdate;
    }
    return dateFormatCN.format(date);
}

String localizedDate = localizeDate("05-Sep-2013", new Locale("zh","CN"));

will be like 05-九月-2013

1 Comment

Thanks for the answer!! Here I added a suggestion actually confused with the date format name ("dateFormatCN" and "dateFormat") will took almost 2 hrs, so i just added it for reference this is for "dateFormat" is input date format & "dateFormatCN" is output date format.
5

This will display the date according to the user's current locale:

To return date and time:

import java.text.DateFormat;    
import java.util.Date;

Date date = new Date();
DateFormat df = DateFormat.getDateTimeInstance();
String myDate = df.format(date);

Output:

Dec 31, 1969 7:00:02 PM

To return the date only, use:

DateFormat.getDateInstance() 

Output:

Dec 31, 1969

Comments

2

Java 8 Style for a given date

LocalDate today = LocalDate.of(1982, Month.AUGUST, 31);
System.out.println(today.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).localizedBy(Locale.ENGLISH)));
System.out.println(today.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).localizedBy(Locale.FRENCH)));
System.out.println(today.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).localizedBy(Locale.JAPANESE)));

2 Comments

I believe I covered this kind of solution a year ago.
Instead of .withLocale(locale), use .localizedBy(locale) to get localized numbers and digits. See How to localize numbers with DateTimeFormatter?.
1

Please find in the below code which accepts a Locale instance and returns the locale specific date format/pattern.

public static String getLocaleDatePattern(Locale locale) {
    // Validating if Locale instance is null
    if (locale == null || locale.getLanguage() == null) {
        return "MM/dd/yyyy";
    }

    // Fetching the locale specific date pattern
    String localeDatePattern = ((SimpleDateFormat) DateFormat.getDateInstance(
            DateFormat.SHORT, locale)).toPattern();

    // Validating if locale type is having language code for Chinese and country
    // code for (Hong Kong) with Date Format as - yy'?'M'?'d'?'
    if (locale.toString().equalsIgnoreCase("zh_hk")) {
        // Expected application Date Format for Chinese (Hong Kong) locale type
        return "yyyy'MM'dd";
    }

    // Replacing all d|m|y OR Gy with dd|MM|yyyy as per the locale date pattern
    localeDatePattern = localeDatePattern.replaceAll("d{1,2}", "dd")
            .replaceAll("M{1,2}", "MM")
            .replaceAll("y{1,4}|Gy", "yyyy");

    // Replacing all blank spaces in the locale date pattern
    localeDatePattern = localeDatePattern.replace(" ", "");

    // Validating the date pattern length to remove any extract characters
    if (localeDatePattern.length() > 10) {
        // Keeping the standard length as expected by the application
        localeDatePattern = localeDatePattern.substring(0, 10);
    }
    return localeDatePattern;
}

Comments

-1
String text = new SimpleDateFormat("E, MMM d, yyyy").format(date);

Comments

-3

Java8

 import java.time.format.DateTimeFormatter;         
 myDate.format(DateTimeFormatter.ofPattern("dd-MMM-YYYY",new Locale("ar")))

2 Comments

Using ofPattern with a locale argument will give you month name in the desired language, but not different formats as requested in the question. Using uppercase YYYY will give you incorrect and very surprising results in corner cases. Using Java 8 is a good idea.
Please fix or delete this Answer. It is blatantly incorrect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.