It sounds like a simple question, but the answer is complicated. The time zone abbreviations supported by Java (whether we are talking the modern DateTimeFormatter
or the troublesome and outdated SimpleDateFormat
) are not defined by Java itself but by the locale data that Java uses. Complications include
- Java can be configured to take locale data from different sources. This means that setting the system property
java.locale.providers
when you run your program will change the set of abbreviations supported.
- The default locale providers were changed from Java 8 to Java 9. So running your program on a different Java version will give you a different set of supported time zone abbreviations.
- CLDR, the locale data that are the default from Java 9, come in versions. So every new Java version may come with a different set of supported abbreviations.
- Time zone abbreviations come in languages. So a formatter with French locale will support other abbreviations than a formatter with German locale, for example.
- Time zone abbreviations are ambiguous. So even if
PST
is a supported abbreviation, you don’t know whether it parses into Pitcairn Standard Time, Pacific Standard Time or Philippine Standard Time.
One way to get an idea: Run a program formatting dates at different times of year into a time zone abbreviation. Since many time zones use summer time (DST) and use a different abbreviation during summer time, you will want to try to hit a date in the summer and a date in the standard time of year for those zones.
System.out.println("java.locale.providers: " + System.getProperty("java.locale.providers"));
DateTimeFormatter zoneAbbreviationFormatter = DateTimeFormatter.ofPattern("zzz", Locale.FRENCH);
Instant northernSummer = Instant.parse("2019-07-01T00:00:00Z");
Instant southernSummer = Instant.parse("2020-01-01T00:00:00Z");
Set<String> supportedAbbreviations = new TreeSet<>();
for (String zid : ZoneId.getAvailableZoneIds()) {
ZoneId zone = ZoneId.of(zid);
supportedAbbreviations.add(northernSummer.atZone(zone).format(zoneAbbreviationFormatter));
supportedAbbreviations.add(southernSummer.atZone(zone).format(zoneAbbreviationFormatter));
}
System.out.println("" + supportedAbbreviations.size() + " abbreviations");
System.out.println(supportedAbbreviations);
Output on my Java 9 was (scroll right to see it all):
java.locale.providers: null
205 abbreviations
[ACDT, ACST, ACT, ACWST, ADT, AEDT, AEST, AFT, AKDT, AKST, ALMT, AMST, AMT, AQTT, ART, AST, AWST, AZOST, AZOT, AZT, America/Punta_Arenas, Asia/Atyrau, Asia/Barnaul, Asia/Famagusta, Asia/Tomsk, BDT, BNT, BOT, BRST, BRT, BST, BTT, CAT, CCT, CDT, CEST, CET, CHADT, CHAST, CHOT, CHUT, CKT, CLST, CLT, COT, CST, CVT, CXT, ChST, DAVT, DDUT, EASST, EAST, EAT, ECT, EDT, EEST, EET, EGST, EGT, EST, Etc/GMT+1, Etc/GMT+10, Etc/GMT+11, Etc/GMT+12, Etc/GMT+2, Etc/GMT+3, Etc/GMT+4, Etc/GMT+5, Etc/GMT+6, Etc/GMT+7, Etc/GMT+8, Etc/GMT+9, Etc/GMT-1, Etc/GMT-10, Etc/GMT-11, Etc/GMT-12, Etc/GMT-13, Etc/GMT-14, Etc/GMT-2, Etc/GMT-3, Etc/GMT-4, Etc/GMT-5, Etc/GMT-6, Etc/GMT-7, Etc/GMT-8, Etc/GMT-9, Europe/Astrakhan, Europe/Kirov, Europe/Saratov, Europe/Ulyanovsk, FJST, FJT, FKT, FNT, GALT, GAMT, GET, GFT, GILT, GMT, GST, GYT, HADT, HAST, HDT, HKT, HOVT, HST, ICT, IDT, IOT, IRDT, IRKT, IRST, IST, JST, KGT, KOST, KRAT, KST, LHDT, LHST, LINT, MAGT, MART, MAWT, MDT, MEST, MET, MHT, MIST, MMT, MSK, MST, MUT, MVT, MYT, NCT, NDT, NFT, NOVT, NPT, NRT, NST, NUT, NZDT, NZST, OMST, PDT, PET, PETT, PGT, PHOT, PHT, PKT, PMDT, PMST, PONT, PST, PWT, PYST, PYT, RET, ROTT, SAKT, SAMT, SAST, SBT, SCT, SGT, SRET, SRT, SST, SYOT, TAHT, TFT, TJT, TKT, TLT, TMT, TOT, TVT, ULAT, UTC, UYT, UZT, VET, VLAT, VOST, VUT, WAKT, WEST, WET, WFT, WGST, WGT, WIB, WIT, WITA, WSDT, WSST, XJT, YAKT, YEKT]
Edit:
You may modify the code snippet to produce the time zones that each abbreviation may be parsed to too.
I would certainly expect that the formatter can parse the same time zone abbreviations that it can produce by formatting.
Supplement: Code for getting each abbreviation with the time zones that it may parse into:
Set<String> zids = ZoneId.getAvailableZoneIds();
Map <String, List<String>> supportedAbbreviations = new TreeMap<>();
supportedAbbreviations.putAll(zids.stream()
.collect(Collectors.groupingBy(zid -> northernSummer.atZone(ZoneId.of(zid))
.format(zoneAbbreviationFormatter))));
supportedAbbreviations.putAll(zids.stream()
.collect(Collectors.groupingBy(zid -> southernSummer.atZone(ZoneId.of(zid))
.format(zoneAbbreviationFormatter))));
System.out.println("" + supportedAbbreviations.size() + " abbreviations");
supportedAbbreviations.forEach((a, zs) -> System.out.format("%-5s %s%n", a, zs));
Excerpt from the output (still on my Java 9):
205 abbreviations
ACDT [Australia/Yancowinna, Australia/Adelaide, Australia/Broken_Hill, Australia/South]
ACST [Australia/North, Australia/Darwin]
ACT [America/Eirunepe, America/Porto_Acre, Brazil/Acre, America/Rio_Branco]
ACWST [Australia/Eucla]
ADT [Canada/Atlantic, America/Grand_Turk, America/Moncton, Atlantic/Bermuda, America/Halifax, America/Glace_Bay, America/Thule, America/Goose_Bay, SystemV/AST4ADT]
AEDT [Australia/Hobart, Australia/Tasmania, Australia/ACT, Australia/Victoria, Australia/Canberra, Australia/Currie, Australia/NSW, Australia/Sydney, Australia/Melbourne]
AEST [Australia/Queensland, Australia/Brisbane, Australia/Lindeman]
…
WSDT [Pacific/Apia]
WSST [Pacific/Apia]
XJT [Asia/Kashgar, Asia/Urumqi]
YAKT [Asia/Chita, Asia/Yakutsk, Asia/Khandyga]
YEKT [Asia/Yekaterinburg]