7
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, new Locale("SV", "SE"));
((SimpleDateFormat) dateFormat).toPattern();

In Java 8 this line produces "yyyy-MM-dd" while in Java 9 it produces "y-MM-dd".

This has some serious issues for our legacy code, is there some way to revert the behaviour?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Jeppz
  • 912
  • 1
  • 8
  • 31
  • that is not compiling – ΦXocę 웃 Пepeúpa ツ Feb 16 '18 at 15:11
  • 1
    @ΦXocę웃Пepeúpaツ ok it works in intellij evaluate expression but it doesnt compile without casting it, updated my example. – Jeppz Feb 16 '18 at 15:14
  • 2
    Possibly because of the [CLDR date time patterns](https://stackoverflow.com/questions/46244724/jdk-dateformater-parsing-dayofweek-in-german-locale-java8-vs-java9/46245412#46245412)? The point from migration guide being *If your application starts successfully, look carefully at your tests and ensure that the behavior is the same as on JDK 8. For example, a few early adopters have noticed that their dates and currencies are formatted differently. See Use CLDR Locale Data by Default.* – Naman Feb 16 '18 at 15:54
  • 3
    If you need the string `"yyyy-MM-dd"`, just use the string `"yyyy-MM-dd"`. If you ask a factory for the pattern for a locale and style, you accept that this is a moving target. I doubt that Sweden is willing to nail down their date format just for the sake of some legacy Java code… – Holger Feb 16 '18 at 16:15
  • 2
    When using Java 9, why on earth are you still using the long outdated and notoriously troublesome `SimpleDateFormat`? I can understand if you don’t want to remove it from your legacy code right now, but for the task at hand, use `DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, null, IsoChronology.INSTANCE, new Locale("SV", "SE"))` (this doesn’t solve your problem, though, it too behaves differently in Java 8 and 9). – Ole V.V. Feb 16 '18 at 16:59
  • @OleV.V. Because it's in a huge legacy monolith and there is plenty of stuff to fix. Is it safe to replace it in one place and leave SimpleDateFormat in another that shares code? – Jeppz Feb 18 '18 at 13:23
  • @Holger Well it's obviously not hardcoded to SV SE in our app, that just in the test but it was the easier way to ask the question. – Jeppz Feb 18 '18 at 13:24
  • 1
    @Jeppz it doesn’t matter which locale you are querying, none of them guarantees to have never changing properties. – Holger Feb 19 '18 at 07:15
  • @Jeppz I rewrote my answer to try to cover that part too. – Ole V.V. Feb 19 '18 at 09:20

1 Answers1

8
    System.setProperty("java.locale.providers", "COMPAT,CLDR");
    DateFormat dateFormat
            = DateFormat.getDateInstance(DateFormat.SHORT, new Locale("sv", "SE"));
    System.out.println(((SimpleDateFormat) dateFormat).toPattern());

Running on my jdk-9.0.4 this prints

yyyy-MM-dd

You may want to set the property with -D on the command line instead, it shouldn’t make any difference.

In Java 9 the Common Locale Data Repository (CLDR) from the Unicode Consortium is used as the default source of locale data, which wasn’t the case in earlier Java versions. Setting the system property as above enables the Java 8 behaviour. As nullpointer said in a comment, you can read a bit more here: Use CLDR Locale Data by Default.

Basil Bourque is correct in his comment, it is customary to use lowercase abbrevation for language, so you should probably specify sv to make sure you don’t confuse your reader.

One might also wonder whether it makes any difference whether there’s one y or four in the pattern. The way I read the SimpleDateFormat documentation, one y will interpret a 2-digit year according to the 80-20 rule: it’s within the last 80 years or within the next 20. yyyy will interpret a 2-digit year as a year in the first century AD. Assuming your years are in the 4-digit range (1000 through 9999), I wouldn’t expect it to be a problem, though.

If this was only for Swedish locale, the modern version of the code would give the same result:

DateTimeFormatterBuilder.getLocalizedDateTimePattern(
        FormatStyle.SHORT, null, IsoChronology.INSTANCE, new Locale("SV", "SE")));

However, for a number of locales (en-CY, Cyprus English, for example) the result differs from DateFormat to DateTimeFormatter, so if your goal was maximal backward compatibility, stick with the outmoded DateFormat class.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 2
    Perhaps it does not matter, but I believe the various locale-related standards use lowercase in the language code. So perhaps that should be `new Locale( “sv” , “SE” )`? – Basil Bourque Feb 16 '18 at 18:12
  • 1
    I was thinking the same, @BasilBourque, but took the Locale verbatim from the question. I have run the code, it works. – Ole V.V. Feb 16 '18 at 18:54
  • 2
    `new Locale("SV", "SE").equals(new Locale("sv", "SE"))` returns `true`, so I take it it doesn’t matter. @BasilBourque – Ole V.V. Feb 17 '18 at 06:27
  • "one y will interpret a 2-digit year according to the 80-20 rule", I rather think, TWO yy will cause this interpretation. ONE y versus yyyy is just about padding, and then the question arises why the OP might have issues with padding in legacy code. Is it sensible to have years like 768 (formatted as such or as "0768")? Probably a data error. That is the real question. – Meno Hochschild Feb 27 '18 at 16:33
  • Both are true, @MenoHochschild. “For parsing with the abbreviated year pattern ("y" or "yy"), SimpleDateFormat must interpret the abbreviated year relative to some century.” From [Java 9 `SimpleDateFormat` documentation](https://docs.oracle.com/javase/9/docs/api/java/text/SimpleDateFormat.html). – Ole V.V. Feb 27 '18 at 16:43
  • Oh I see. But this is a change totally incompatible with CLDR-standard. In Minguo calendar, we have actually the year 107. Here the old interpretation with only one y is relevant. – Meno Hochschild Feb 27 '18 at 17:37
  • 1
    See also the conflict between [CLDR](http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) and new `SimpleDateFormat` about the meaning of "y" (shaking my head). – Meno Hochschild Feb 27 '18 at 17:55
  • 3
    Caution: The property "java.locale.providers" is only read at the Java runtime startup, so the later call to System.setProperty() won't have a effect. (See [LocaleServiceProvider API](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/spi/LocaleServiceProvider.html) ). So setting this property in-code (as in the given example by @OleV.V.) is not best-pratice and may lead to hard to find errors. – VinZ Nov 21 '18 at 16:45