1

I am using this simple code to fetch the time zone ids of the current time zone that has been set in the system.

public class Main {

    public static void main(String[] args)
    {   TimeZone timezone = TimeZone.getDefault();
        System.out.println(timezone.getID());
    }
}

I use the tzutil /g to determine the time zone and tzutil /s to set the timezone. I have observed when time zone is set to Eastern Standard Time the above code returns id as America/New_York but when it is set to Eastern Standard Time_dstoff then I get id as GMT-05:00 which is undesirable. I want the output in the form of continent/city.

How would I get that?

Parth Maitra
  • 61
  • 1
  • 4
  • I recommend you don’t use `TimeZone`. That class is poorly designed and long outdated. Instead use `ZoneId` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Feb 15 '20 at 07:02
  • 1
    Which continent and city would you want to have from Eastern Standard Time_dstoff? – Ole V.V. Feb 15 '20 at 07:03
  • Why are you doing `getID()` and `TimeZone.getTimeZone(...)`? Wouldn't simply doing `TimeZone timezone = TimeZone.getDefault();` be the same? – Andreas Feb 15 '20 at 07:04
  • 2
    Why is `GMT-05:00` undesirable? – Andreas Feb 15 '20 at 07:05
  • Related: [How to translate between windows and IANA timezones in java](https://stackoverflow.com/questions/31450236/how-to-translate-between-windows-and-iana-timezones-in-java) and [java.time.zone.ZoneRulesProvider for Microsoft time-zones](https://stackoverflow.com/questions/52857334/java-time-zone-zonerulesprovider-for-microsoft-time-zones) – Ole V.V. Feb 15 '20 at 07:08
  • I also recommend that your Java code doesn’t rely on the default time zone of the JVM anyway. The default setting can be changed at any time from another part of your program or another program running in the same JVM, so is fragile. Instead specify explicit time zone in your time zone sensitive operations. – Ole V.V. Feb 15 '20 at 07:10
  • 1
    Your `main` body can be reduced to just `System.out.println(ZoneId.systemDefault());`. – Ole V.V. Feb 15 '20 at 07:16
  • @Andreas Yeah that was a mistake(Was trying out something). Updated the snippet with the actual method. – Parth Maitra Feb 15 '20 at 10:20
  • @Andreas ```GMT-05:00``` is undesirable as I am using an API that accepts only **continent/city** format – Parth Maitra Feb 15 '20 at 10:22
  • @OleV.V. I dont know all the cities that use Eastern Standard Time_dstoff but one of them is Cayman Islands – Parth Maitra Feb 15 '20 at 10:28
  • 1
    @ParthMaitra `America/Cayman` had time zone shifts on 1890-01-01 and 1908-04-22, so not a true `Eastern Standard Time_dstoff` – Andreas Feb 15 '20 at 11:30

2 Answers2

3

My first answer is that you shouldn’t care about the default time zone setting of your JVM. Your Java code shouldn’t rely on it anyway. The default setting can be changed at any time from another part of your program or another program running in the same JVM, so is fragile. Instead specify explicit time zone in your time zone sensitive operations.

If you still want to rely on the default, here’s hack to set it to some continent/city time zone if it happens to be the GMT-05:00 that you consider undesirable.

    ZoneId defaultZoneId = ZoneId.systemDefault();
    ZoneId desiredTimeZone = null;
    if (defaultZoneId.equals(ZoneId.of("GMT-05:00"))) {
        // Pick a date in summer
        LocalDate dateInSummer = Year.now().atMonthDay(MonthDay.of(Month.JULY, 7));
        DateTimeFormatter zoneFormatter = DateTimeFormatter.ofPattern("zzzz", Locale.ENGLISH);
        for (String zid : ZoneId.getAvailableZoneIds()) {
            ZoneId zone = ZoneId.of(zid);
            String timeZoneName = dateInSummer.atStartOfDay(zone).format(zoneFormatter);
            if (zid.startsWith("America/") && timeZoneName.equals("Eastern Standard Time")) {
                desiredTimeZone = zone;
                break;
            }
        }
    }

    System.out.println("Time zone: " + desiredTimeZone);

On my JDK 11 I got:

Time zone: America/Panama

To set it as the default we unfortunately have to go through the poorly designed and otherwise long outdated TimeZone class:

    TimeZone.setDefault(TimeZone.getTimeZone(desiredTimeZone));

There is one more reason to warn against using America/Panama or some other time zone ID instead of GMT-05:00: While Panama has been using offset -05:00 from GMT/UTC since some time in 1908, it hasn’t always, so for historic dates you will get incorrect times from using it.

According to timeanddate.com the places that use Eastern Standard Time all year (no summer time/DST) are:

  • Nunavut - Southampton Island only (Coral Harbour) (Canada) (America/Coral_Harbour time zone)
  • Quintana Roo (Mexico) (America/Cancun time zone)
  • Cayman Islands (America/Cayman)
  • Jamaica (America/Jamaica)
  • Panama (America/Panama)

My Java additionally lists America/Atikokan (also in Canada).

The list doesn’t include any place in the United States. I believe that none of the above have been using offset -05:00 always. That is to say, each of them has had a different offset at some point in history, and Java will use that different offset for historic dates.

So which time zone to use?

Edit: Andreas in a comment pointed out that neither Etc/GMT+5 nor SystemV/EST5 has the problem I mentioned about applying offsets to historic dates that are probably unexpected. So you may consider one of those. The pros of each are, as I see them:

                                    America/Xxx Etc/GMT+5 SystemV/EST5 GMT-05:00
Official IANA time zone?                Yes        Yes
With slash?                             Yes        Yes        Yes
Continent/city?                         Yes
Good for historical dates?                         Yes        Yes         Yes
Prints as EST/Eastern Standard Time?    Yes                   Yes

(Your starting point, GMT-05:00, is only included for the comparison. And which ones are really good for historic dates of course depends on what you require for those.)

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • `America/Panama` had time zone shifts on 1890-01-01 and 1908-04-22, so not a true `Eastern Standard Time_dstoff` – Andreas Feb 15 '20 at 11:31
  • @Andreas As I said, I believe something similar can be said for all the other time zones I have listed. How do you define a true Eastern Standard Time_dstoff? Apparently Either Windows or Java on Windows defines it as GMT-05:00, while the questioner doesn’t. New York observed summer time all year from 1942 to 45. Does that mean that it’s not a true EST (with DST on), or does it mean that every true EST should do the same? – Ole V.V. Feb 15 '20 at 11:46
  • `Etc/GMT+5` and `SystemV/EST5` are both valid time zones with a `/` and no time zone shifts, ever. See [my answer](https://stackoverflow.com/a/60238450/5221149). – Andreas Feb 15 '20 at 11:47
1

To find time zones that are and were always GMT-05:00, aka Eastern Standard Time_dstoff, check all ZoneId rules to find one with:

  • Offset of -5 hours

  • No transitions (past time zone changes)

  • No transition rules (current and future time zone changes)

for (String id : ZoneId.getAvailableZoneIds()) {
    ZoneRules rules = ZoneId.of(id).getRules();
    if (rules.getTransitions().isEmpty() && rules.getTransitionRules().isEmpty()
                && rules.getOffset(Instant.now()).getTotalSeconds() == 5 * -3600) {
        System.out.println(id);
    }
}

Output (OpenJDK 13.0.2 on Windows)

Etc/GMT+5
SystemV/EST5

So if you need to change the time zone from GMT-05:00 to a zone with a / in it, use one of those two. Etc/GMT+5 seems appropriate.

If you want to automate that, so it tries to fix it if the default time zone doesn't contain a /, then something like this might work:

if (! ZoneId.systemDefault().getId().contains("/")) {
    int offset = ZoneId.systemDefault().getRules().getOffset(Instant.now()).getTotalSeconds();
    String candidate = null;
    for (String id : ZoneId.getAvailableZoneIds()) {
        ZoneRules rules = ZoneId.of(id).getRules();
        if (rules.getTransitions().isEmpty() && rules.getTransitionRules().isEmpty()
                       && rules.getOffset(Instant.now()).getTotalSeconds() == offset) {
            if (id.startsWith("Etc/GMT")) {
                candidate = id;
                break;
            }
            if (candidate == null)
                candidate = id;
        }
    }
    if (candidate != null)
        TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of(candidate)));
}
Andreas
  • 154,647
  • 11
  • 152
  • 247