3

I have a multicultural application, the project uses Java 7. I need to show the date, but the day in month and month only. So for example in Locale.UK today's date looks like: 24/01, but in Locale.US like: 1/24. How to achieve this in Java 7? I tried to use DateFormat.getDateInstance(dateFormat, locale), but in this case I can use just predefined date formats, for example DateFormat.SHORT, DateFormat.DEFAULT etc. there is no predefined format just with the day in month and month only. Next I tried to use SimpleDateFormat with locale, but this is not working as I wonder, it just translates some text according to the locale. Here is my sample code:

DateFormat dfuk = DateFormat.getDateInstance(DateFormat.SHORT, Locale.UK);
DateFormat dfus = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
System.out.println(dfuk.format(new Date()));  // 24/01/17
System.out.println(dfus.format(new Date()));  // 1/24/17
SimpleDateFormat sdfuk = new SimpleDateFormat("dd/MM", Locale.UK);
SimpleDateFormat sdfus = new SimpleDateFormat("dd/MM", Locale.US);
System.out.println(sdfuk.format(new Date()));  // 24/01
System.out.println(sdfus.format(new Date()));  // 24/01

I expected last line to print 01/24 (or 1/24). How to achieve this?

Floern
  • 33,559
  • 24
  • 104
  • 119
bladekp
  • 1,529
  • 22
  • 29
  • You third last line about `sdfus` says `SimpleDateFormat sdfus = new SimpleDateFormat("dd/MM", Locale.US);` -- Did you mean to say `MM/dd` instead? – leeyuiwah Jan 24 '17 at 20:05
  • Yes it can be, but in case you said I have to write additional condition which will verify actual locale, and choose appropriate format (dd/MM or MM/dd) I wish to find Java library built-in solution for my problem. – bladekp Jan 24 '17 at 20:09
  • The `getDateInstance()` method takes an argument for `style`. The shortest style seems to be `DateFormat.SHORT`. You can argue that perhaps they should provide one that is even shorter (`DateFormat.SHORTER` perhaps?) c.f. http://docs.oracle.com/javase/6/docs/api/java/text/DateFormat.html#getDateInstance(int) – leeyuiwah Jan 24 '17 at 20:16
  • 1
    My lib [Time4J](https://github.com/MenoData/Time4J) offers the class [net.time4j.xml.AnnualDate](http://time4j.net/javadoc-en/net/time4j/xml/AnnualDate.html) with support for four format styles in about ~ 80 locales. The version line v3.x would be runnable on Java-6 or 7, the newer versions v4.x on Java-8. If you still miss any locale then just open an issue on the tracker of Time4J. The current i18n-data are based on unicode-cldr-version v30. – Meno Hochschild Jan 24 '17 at 20:40

4 Answers4

2

Your code was throwing the same format because both DateFormat were constructed with the same pattern and that is not correct..

SimpleDateFormat sdfuk = new SimpleDateFormat("dd/MM", Locale.UK);
SimpleDateFormat sdfus = new SimpleDateFormat("dd/MM", Locale.US);

You will need the local if you have a String that is going to be converted to date... for the parsing you are trying to do (string -> Date) only the pattern is ok...

Example

DateFormat sdfuk = new SimpleDateFormat("dd/MM");
DateFormat sdfus = new SimpleDateFormat("MM/d");
System.out.println(sdfuk.format(new Date())); // 24/01
System.out.println(sdfus.format(new Date())); // 01/24
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • 1
    This is okay, but we have like ~80 locales, in case you said I have to write additional condition which will verify actual locale, and choose appropriate format (dd/MM or MM/dd) I wish to find Java library built-in solution for my problem. – bladekp Jan 24 '17 at 20:15
1

Change your third last line to

SimpleDateFormat sdfus = new SimpleDateFormat("MM/dd", Locale.US);

Overcoming the shortcoming of what standard Java offers:

The getDateInstance() method takes an argument for style. The shortest style seems to be DateFormat.SHORT. You can argue that perhaps they should provide one that is even shorter (DateFormat.SHORTER perhaps?) c.f.

http://docs.oracle.com/javase/6/docs/api/java/text/DateFormat.html#getDateInstance(int)

Before that happen, you can build an enum of pattern for the shorter style. Below is an example:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

enum PatternShorter { // or PatternMonthDayOnly
    MM_SLASH_DD     ("MM/dd")
    , DD_SLASH_MM   ("dd/MM")
    ;
    private String pattern;

    public String getPattern() {
        return pattern;
    }

    private PatternShorter(String pattern) {
        this.pattern = pattern;
    }

    public static PatternShorter getDefault() { return DD_SLASH_MM; }
}

public class DateFormatEx {
    private static Map<Locale, PatternShorter> patternShorter = new HashMap<>();

    static {
        patternShorter.put(Locale.UK, PatternShorter.DD_SLASH_MM);
        patternShorter.put(Locale.UK, PatternShorter.MM_SLASH_DD);
        // any locale not listed here will get the default pattern
    }

    private static String getPattern (Locale locale) {      
        if (patternShorter.get(locale)!=null) {
            return patternShorter.get(locale).getPattern();
        } else {
            return PatternShorter.getDefault().getPattern();
        }
    }

    public static void main(String[] args) {
        List<Locale> listOfLocale = Arrays.asList(Locale.UK, Locale.US, Locale.FRENCH);
        for (Locale locale : listOfLocale) {
            SimpleDateFormat fmt 
            = new SimpleDateFormat(getPattern(locale), locale);
            System.out.format("for locale %s the shorter date/month display is: %s%n"
                    , locale.toString()
                    , fmt.format(new Date()));  
        }
    }

}

The output would be:

for locale en_GB the shorter date/month display is: 01/24
for locale en_US the shorter date/month display is: 24/01
for locale fr the shorter date/month display is: 24/01
leeyuiwah
  • 6,562
  • 8
  • 41
  • 71
  • 1
    This is okay, but we have like ~80 locales, in case you said I have to write additional condition which will verify actual locale, and choose appropriate format (dd/MM or MM/dd) I wish to find Java library built-in solution for my problem. – bladekp Jan 24 '17 at 20:14
  • @bladekp -- I have updated answer and added explanation on how to overcome the shortcoming of what standard Java offers – leeyuiwah Jan 24 '17 at 20:37
0

tl;dr

MonthDay.from( 
    LocalDate.now( ZoneId.of( "America/Montreal" ) ) 
).format( DateTimeFormatter.ofPattern( "MM/dd" ) )

01/24

MonthDay

There's a class for that: MonthDay.

The MonthDay represents the combination of a month and day-of-month.

MonthDay md = MonthDay.of( Month.JANUARY , 24 );

Avoid legacy date-time classes.

Avoid the troublesome old date-time classes such as SimpleDateFormat, now legacy, supplanted by the java.time classes.

The MonthDay class is part of the java.time framework built into Java 8 and later. Much of java.time is back-ported to Java 6 & Java 7 (see below).

ISO 8601

md.toString(): --01-24

The toString method generates a String in standard ISO 8601 format. The MonthDay class can also parse such strings.

Current date

You want the month-day of the current date. For the current date we will use the LocalDate class. The LocalDate class represents a date-only value without time-of-day and without time zone.

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.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

Be clear that Locale has nothing to do with time zones. A Locale only applies to the format of text when generating strings, but is separate and distinct from time zone.

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

Now extract a MonthDay object.

MonthDay md = MonthDay.from( today );

today.toString(): 2017-01-25

md.toString(): --01-25

Other formats

You can generate strings in other formats.

The java.time classes can automatically localize some date-time values by Locale. Unfortunately, this does not apply to MonthDay. For MonthDay you must explicitly specify the desired format in a DateTimeFormatter object when you want something other than the standard ISO 8601 format. By the way, I encourage you to stick with the standard format whenever possible, especially for exchanging data. You might even consider training your users to accept this format. The standard format is unambiguous, whereas your UK-US formats (dd/MM, MM/dd) can be entirely ambiguous such as 01/02 being either January 2nd or February 1st.

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. Generally, I advise always specifying a Locale for your DateTimeFormatter rather than relying implicitly on the JVM’s current default Locale. But I do not see any way the Locale would affect the output of this particular format. So I omit the Locale in this example.

DateTimeFormatter fUS = DateTimeFormatter.ofPattern( "MM/dd" );
DateTimeFormatter fUK = DateTimeFormatter.ofPattern( "dd/MM" );

String output = md.format( fUS );

Live code

See this example code run live at IdeOne.com.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 1
    I think this misses the point of the question. OP wanted a way to retrieve the "shortest" date format for a locale - that is, MM/d for Locale.US, d/MM for Locale.UK, and whatever format is appropriate for any other Locale, such as dd.MM for Germany. I don't think it's possible. But what you've answered is a different question entirely. – Dawood ibn Kareem Jan 25 '17 at 01:53
0

I ended up modifying the localized format string provided by DateFormat.getDateInstance(DateFormat.SHORT), removing the year part from the format:

DateFormat shortDateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
String shortPattern = ((SimpleDateFormat) shortDateFormat).toLocalizedPattern();
String shorterPattern = shortPattern.replaceAll("[/\\- ]*[yY]+[^a-zA-Z]*", "");
DateFormat shorterDateFormat = new SimpleDateFormat(shorterPattern);

I skimmed through all 734 locales provided by my system, and the generated date patterns look good for all of them, as far as I can tell.

Some examples comparing the original short pattern to the modified shorter one:

         short      shorter
en_US    M/d/yy     M/d
en_GB    dd/MM/y    dd/MM
de_DE    dd.MM.yy   dd.MM.
fr_FR    dd/MM/y    dd/MM
nl_NL    dd-MM-y    dd-MM
ja_JP    y/MM/dd    MM/dd
Floern
  • 33,559
  • 24
  • 104
  • 119