Simpledateformat and Locale Based Format String

SimpleDateFormat and locale based format string

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

SimpleDateFormat pattern based on locale, but forcing a 4-digit year

I would do it like this:

    StringBuffer buffer = new StringBuffer();

Calendar date = Calendar.getInstance();
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
FieldPosition yearPosition = new FieldPosition(DateFormat.YEAR_FIELD);

StringBuffer format = dateFormat.format(date.getTime(), buffer, yearPosition);
format.replace(yearPosition.getBeginIndex(), yearPosition.getEndIndex(), String.valueOf(date.get(Calendar.YEAR)));

System.out.println(format);

Using a FieldPosition you don't really have to care about wheter the format of the date includes the year as "yy" or "yyyy", where the year ends up or even which kind of separators are used.

You just use the begin and end index of the year field and always replace it with the 4 digit year value and that's it.

How to get the date-time format string from current Locale, as a parameter for SimpleDateFormat

You can do it as follows:

SimpleDateFormat getTheCurrentLocaleDateTimeFormatString() {
return (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault());
}

A test program:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = getTheCurrentLocaleDateTimeFormatString();
String localeTimeString = dateFormat.format(calendar.getTimeInMillis());
System.out.println(localeTimeString);
}

static SimpleDateFormat getTheCurrentLocaleDateTimeFormatString() {
return (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault());
}
}

Output:

Sunday, 12 January 2020

[Update]

Posting the following code based on your comment:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

public class Main {
public static void main(String[] args) {
Calendar calendar=Calendar.getInstance();
String formatString=getTheCurrentLocaleDateTimeFormatString();
System.out.println(formatString);
SimpleDateFormat dateFormat = new SimpleDateFormat(formatString);
String localeTimeString= dateFormat.format(calendar.getTimeInMillis());
System.out.println(localeTimeString);
}

static String getTheCurrentLocaleDateTimeFormatString() {
return ((SimpleDateFormat) DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault())).toLocalizedPattern();
}
}

Output:

EEEE, d MMMM y
Sunday, 12 January 2020

[Another update]

Posting the following code based on your another comment:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
String formatString = getTheCurrentLocaleDateTimeFormatString();
System.out.println(formatString);
SimpleDateFormat dateFormat = new SimpleDateFormat(formatString);
String localeTimeString = dateFormat.format(calendar.getTimeInMillis());
System.out.println(localeTimeString);
}

static String getTheCurrentLocaleDateTimeFormatString() {
return ((SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL,
Locale.getDefault())).toLocalizedPattern();
}
}

Output:

EEEE, d MMMM y 'at' HH:mm:ss zzzz
Sunday, 12 January 2020 at 20:28:05 Greenwich Mean Time

How locale dependent is SimpleDateFormat?

Just found the getAvailableLocales static method on Locale, and it turns out that all the fields of a calendar can be locale dependent:

public static void main(String[] args) {
String pattern = "yyyy-MM-dd HH:mm:ss";
Date date = new Date();
String defaultFmt = new SimpleDateFormat(pattern).format(date);

for (Locale locale : Locale.getAvailableLocales()) {
String localeFmt = new SimpleDateFormat(pattern, locale).format(date);
if (!localeFmt.equals(defaultFmt)) {
System.out.println(locale + " " + localeFmt);
}
}
}

On my system (in germany running an english version of ubuntu) this outputs the following list, lets hope the unicode character come through intact:

ja_JP_JP 23-03-03 16:53:09
hi_IN २०११-०३-०३ १६:५३:०९
th_TH 2554-03-03 16:53:09
th_TH_TH ๒๕๕๔-๐๓-๐๓ ๑๖:๕๓:๐๙

So Japan and Thailand use a different epoch but are otherwise based on the gregorian calendar, which explains why month and day are the same.

Other locales also use different scripts for writing numbers, for example Hindi spoken in India and a variant of Thai in Thailand.

To answer the question, the locale should alway be specified to a known value when a locale independant String is needed.

Edit: Java 1.6 added a constant Locale.ROOT to specify a language/country neutral locale. This would be preferred to specifying the English locale for output targeted at a computer.

The root locale is the locale whose language, country, and variant are empty ("") strings. This is regarded as the base locale of all locales, and is used as the language/country neutral locale for the locale sensitive operations.

different results with same locale using SimpleDateFormat on same machine with Windows zulu8

I have not understood how the implementation by Talend ETL can be any of your business. If they have not yet found the opportunity for upgrading to java.time, the modern Java date and time API, it’s their problem, not yours. You should not use SimpleDateFormat nor Calendar in your own code.

Java has got 3 default locales

Java hasn’t just got one, it’s got three default locales, partly for historical reasons. They can be set individually. To demonstrate:

    Locale.setDefault(Locale.FRANCE);
Locale.setDefault(Locale.Category.DISPLAY, Locale.JAPAN);
Locale.setDefault(Locale.Category.FORMAT, Locale.GERMANY);

System.out.println(Locale.getDefault());
System.out.println(Locale.getDefault(Locale.Category.DISPLAY));
System.out.println(Locale.getDefault(Locale.Category.FORMAT));

Output from this snippet is:

fr_FR
ja_JP
de_DE

The output reflects in order France, Japan and Germany (deutsch/Deutschland).

Your comment states that the code of SimpleDateFormat uses the default FORMAT locale as its default locale (so Germany in my example). That is, the locale that it uses when you don’t specify one (you should’t use SimpleDateFormat, if you do nevertheless, you should always specify locale explicitly).

As I said, the three can be set individually. The one-arg Locale.setDefault() sets all three, though.

Does this observation explain? On my Java 11 it seems that setting the locale on the command line sets all three default locales (until altered by Locale.setDefault()). I tried just

    System.out.println(Locale.getDefault());
System.out.println(Locale.getDefault(Locale.Category.DISPLAY));
System.out.println(Locale.getDefault(Locale.Category.FORMAT));

I ran this snippet with -Duser.language=en -Duser.country=US on the command line, and the output was:

en_US
en_US
en_US

Also other language and country setting came through in all three locales. So no, this doesn’t alone explain why your SimpleDateFormat in one case did not seem to pick up the locale from the command line.

Does this observation provide a solution?

I still haven’t understood what your real end goal is. The first recommendation is: Your code should not rely on the default locale of the JVM. Use explicit locale in your locale sensitive operations.

If you do need to set the default FORMAT locale for Talend ETL to work the way you require it to, Locale.setDefault(Locale.Category.FORMAT, Locale.US); should do it.

Link

Related question: Which "default Locale" is which?

Detect locale of a date which is in string format using Java

I suggest you iterate over the possible locales (either Locale.getAvailableLocales() or your own list of locales you want to be able to detect), for each use DateTimeFormatter.ofLocalizedDate() with all possible styles (full, long, medium, short, or just those that you consider relevant) and try parsing your string into a date. If it succeeds, you have got a possible locale, but just one of several possible. The following method returns an array of all possible locales for a given date string.

public static Locale[] possibleLocalesForDateString(String dateString) {
Locale[] availableLocales = Locale.getAvailableLocales();
return Arrays.stream(availableLocales)
.filter(loc -> {
for (FormatStyle style : FormatStyle.values()) {
DateTimeFormatter formatter
= DateTimeFormatter.ofLocalizedDate(style).withLocale(loc);
try {
LocalDate.parse(dateString, formatter);
// succeeded; this is a possible locale
return true;
} catch (DateTimeParseException dtpe) {
// failed; ignore this locale/format style combination
}
}
return false;
})
.toArray(Locale[]::new);
}

On my Java 8 possibleLocalesForDateString("12/11/2017") returns an array of 50 locales. Starting out from 160 available locales the method has reduced to a little less than a third of the possibilities. The array does not include US locale because the short US format uses two-digit year, while the medium format is like Dec 11, 2017.

Other results:

  • 12/11/17 gives as many as 67 possible locales including US (en_US) and France (fr_FR). The date is understood as Dec 11 in the US and as 12 Nov in France.
  • 30/11/17 gives “just” 60 locales. US is no longer included, France still is. Using a day-of-month greater than 12 helps narrow down the possibilities.
  • Conversely 12/31/17 gives just the remaining 7 locales including US; but it’s still ambiguous.
  • Trying a German date string: 11.12.17. I got 30 locales back including Germany (de_DE).

Avoid SimpleDateFormat. Since you tagged your question with simpledateformat, I wanted to mention that the SimpleDateFormat class is long outdated and notoriously troublesome. java.time, the modern Java date and time API is so much nicer to work with. So I am using classes and enums from that API in my method.

Link: Oracle Tutorial on Date Time, where the use of java.time is explained. You can find many other resources out there.

How can I format day and month in the locale-correct order in Java?

No, sorry. I know of no Java library that will automatically turn "MMM d" around into 20 sep. for a locale that prefers the day of month before the month abbreviation.

You may try modifying the answer by Rowi in this way:

DateTimeFormatter ft = 
DateTimeFormatter
.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.forLanguageTag("sv-SE"))
;

However the result is:

20 sep. 2019

It includes the year, which you didn’t ask for.

An advanced solution would use the DateTimeFormatterBuilder class to build DateTimeFormatter objects.

DateTimeFormatterBuilder
.getLocalizedDateTimePattern(
FormatStyle.MEDIUM,
null,
IsoChronology.INSTANCE,
Locale.forLanguageTag("sv-SE")
)

This returns d MMM y. Modify this string to delete the y and the space before it. Note that in other languages the y may be yy, yyyy or u and may not come last in the string. Pass your modified format pattern string to DateTimeFormatter.ofPattern.

It may be shaky. Even if you look through the format pattern strings for all available locales, the next version of CLDR (where the strings come from) might still contain a surprise. But I think it’s the best we can do. If it were me, I’d consider throwing an exception in case I can detect that the string from getLocalizedDateTimePattern doesn’t look like one I know how to modify.

SimpleDateFormat(String template, Locale locale) with for example Locale.US for ASCII dates

To remove the warning just add Locale.getDefault() as the second argument while instantiating the date format object.
Eg.

 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
java.util.Locale.getDefault());


Related Topics



Leave a reply



Submit