0

I am trying to get all of the zoneId's by giving zone offset e.g. "GMT+2" as string. However, I am not sure if it is possible using any Java 8+ library. Is it possible to obtain all zone id values having the given zone offset?

There is a method called getAvailableZoneIds() in java.time but it does not take offset parameter.

  • 4
    The relation between zoneIds and offsets is not that simple, due to DST and similar transitions. Do you want all zoneIds that ever have/had an offset of "GMT+2", or just currently? – Hulk Aug 25 '21 at 10:56
  • The linked question demonstrates how to get the offset of a zoneId at a given instant – Hulk Aug 25 '21 at 11:27
  • @Hulk Which answer do you mean? –  Aug 25 '21 at 11:39
  • well, the [accepted one](https://stackoverflow.com/a/37501492/2513200) shows that `zoneID.getRules().getOffset(instant);` yields the offset at a given instant (just like the [answer](https://stackoverflow.com/a/68922044/2513200) by deHaar here). Others add more context and alternative ways. Note that the result will depend on the given Instant - in addition to the yearly DST transitions, there are also occasionally (every few years) new zones created. – Hulk Aug 25 '21 at 11:48
  • If I query for the current time, I think there will be no problem. Any idea? –  Aug 25 '21 at 13:02

1 Answers1

5

If you want the zones which are currently having an offset of e.g. +02:00 hours from UTC, you could try to collect the zone names (not directly ZoneId objects) by filtering via getAvailableZoneIds(), getRules() and getOffset(Instant instant). The latter needs an argument defining the moment in time this method is based on.
In this example it is nowInstant.now():

public static void main(String[] args) throws Exception {
    // define the desired offset
    ZoneOffset plusTwo = ZoneOffset.ofHours(2);
    // collect all the zones that have this offset at the moment
    List<String> zonesWithPlusTwo = 
            ZoneId.getAvailableZoneIds()
                  .stream()
                  // filter by those that currently have the given offset
                  .filter(zoneId -> ZoneId.of(zoneId)
                                          .getRules()
                                          .getOffset(Instant.now())
                                          .equals(plusTwo))
                  .sorted()
                  .collect(Collectors.toList());
    // print the collected zones
    zonesWithPlusTwo.forEach(System.out::println);
}

Today, 25th of August 2021, the output was

Africa/Blantyre
Africa/Bujumbura
Africa/Cairo
Africa/Ceuta
Africa/Gaborone
Africa/Harare
Africa/Johannesburg
Africa/Khartoum
Africa/Kigali
Africa/Lubumbashi
Africa/Lusaka
Africa/Maputo
Africa/Maseru
Africa/Mbabane
Africa/Tripoli
Africa/Windhoek
Antarctica/Troll
Arctic/Longyearbyen
Atlantic/Jan_Mayen
CET
Egypt
Etc/GMT-2
Europe/Amsterdam
Europe/Andorra
Europe/Belgrade
Europe/Berlin
Europe/Bratislava
Europe/Brussels
Europe/Budapest
Europe/Busingen
Europe/Copenhagen
Europe/Gibraltar
Europe/Kaliningrad
Europe/Ljubljana
Europe/Luxembourg
Europe/Madrid
Europe/Malta
Europe/Monaco
Europe/Oslo
Europe/Paris
Europe/Podgorica
Europe/Prague
Europe/Rome
Europe/San_Marino
Europe/Sarajevo
Europe/Skopje
Europe/Stockholm
Europe/Tirane
Europe/Vaduz
Europe/Vatican
Europe/Vienna
Europe/Warsaw
Europe/Zagreb
Europe/Zurich
Libya
MET
Poland

EDIT: Considering the comment by @BasilBorque, here's an example method that takes two arguments, a value for an offset and an Instant to base the calculation on:

public static List<String> getZones(int offsetHours, Instant base) {
    // create the offset
    ZoneOffset offset = ZoneOffset.ofHours(offsetHours);
    // collect all the zones that have this offset at the moment 
    return ZoneId.getAvailableZoneIds()
                 .stream()
                 // filter by those that currently have the given offset
                 .filter(zoneId -> ZoneId.of(zoneId)
                                         .getRules()
                                         .getOffset(base)
                                         .equals(offset))
                 .sorted()
                 .collect(Collectors.toList());
}

You can create a local variable (maybe a class member) in order to pass that to the method. That would decrease the amount of calls to Instant.now() and enables you to use Instants different from the moment of calling the method.

deHaar
  • 17,687
  • 10
  • 38
  • 51
  • What about GMT-1 ? It seems to not working for negative time zones. Any idea? –  Aug 25 '21 at 13:01
  • @Henry I just tried it and it actually returned only 2 zones: `Atlantic/Cape_Verde` and `Etc/GMT+1`, now we need to find out if that's wrong (appears to be) or correct... – deHaar Aug 25 '21 at 13:04
  • 1
    @Henry which zones did you expect in the result? There are not many zones in the atlantic ocean... e.g. Brazils FNT zone currently is at GMT-2. – Hulk Aug 25 '21 at 13:10
  • 1
    @Henry Greenland is split between WGST (GMT-2) and EGST (GMT+0), Iceland is at GMT+0 – Hulk Aug 25 '21 at 13:17
  • 1
    A small quibble: To avoid wrapping over a change in time zone while running that code, the `Instant.now())` should be moved to a local variable so it is called only once. – Basil Bourque Aug 25 '21 at 16:07
  • @BasilBourque Ok, what is the solution? Why do not you post it as answer? –  Aug 25 '21 at 17:03
  • @Henry The solution is to use a local variable, maybe make a method and pass an `Instant` as an argument. – deHaar Aug 25 '21 at 17:14
  • @deHaar Thanks a lot for update. What should I use as `Instant` (`base`) parameter? –  Aug 25 '21 at 17:26
  • I would still use `Instant.now()`, but the point here is you define it somewhere else, where it is not called every time you use this method. You could include it in some initialization or automate calling it when really needed (e.g. once each day). – deHaar Aug 25 '21 at 17:28
  • @deHaar Not sure why, but after your update now the first version seems to be working. Have you ever tried the first version? I tried by using -3 (America/Argentina/Buenos_Aires). Could you pls test it? If it is also working I will use the first version. –  Aug 25 '21 at 18:26
  • Similar code can be [run live at IdeOne.com](https://ideone.com/6IYN3T). – Basil Bourque Aug 25 '21 at 20:57