-1

We keep time zones of the cities in our config table using ID of Time Zone values as Europe/London, Asia/Tokyo, etc. However, when selecting time zones from the list, user needs to see the GMT values for these time zones and we want to display Display Name of Time Zone Options as (UTC+00:00) Dublin, Edinburgh, Lisbon, London rather than Europe/London. As we know, the GMT values are not fixed and we should make some conversion in order to display the current time zone display value. So, how could I do this in Java?

2 Answers2

1

Let's say, you have the ids in a Set<String>, just like ZoneId.getAvailableZoneIds().

You can use a ZoneId in order to create the current moment in time and format its offset as desired:

public static void main(String[] args) throws IOException {
    // provide two formatters for the desired outputs GMT... and UTC...
    DateTimeFormatter gmtFormatter = DateTimeFormatter.ofPattern("OOOO");
    DateTimeFormatter utcFormatter = DateTimeFormatter.ofPattern("'UTC'xxx");
    // stream all the IDs (sorted) and create a ZonedDateTime at each ZoneId
    ZoneId.getAvailableZoneIds().stream().sorted().forEach(zoneId -> {
        ZonedDateTime now = ZonedDateTime.now(ZoneId.of(zoneId));
        // then print the zone id and the other representations using the formatters
        System.out.println(
                String.format("%-32s %9s / %9s", 
                                zoneId.toString(), 
                                now.format(gmtFormatter),
                                now.format(utcFormatter)
                ));
    });
}

The first formatter only consider the zone in GMT+/-HH:mm representation and the second one just the literals UTC plus the offset as +/-HH:mm.

The (shortened!) output is

Africa/Abidjan                         GMT / UTC+00:00
Africa/Accra                           GMT / UTC+00:00
Africa/Addis_Ababa               GMT+03:00 / UTC+03:00
Africa/Algiers                   GMT+01:00 / UTC+01:00
Africa/Asmara                    GMT+03:00 / UTC+03:00
Africa/Asmera                    GMT+03:00 / UTC+03:00
Africa/Bamako                          GMT / UTC+00:00
...
US/Alaska                        GMT-09:00 / UTC-09:00
US/Aleutian                      GMT-10:00 / UTC-10:00
US/Arizona                       GMT-07:00 / UTC-07:00
US/Central                       GMT-06:00 / UTC-06:00
US/East-Indiana                  GMT-05:00 / UTC-05:00
US/Eastern                       GMT-05:00 / UTC-05:00
US/Hawaii                        GMT-10:00 / UTC-10:00
US/Indiana-Starke                GMT-06:00 / UTC-06:00
US/Michigan                      GMT-05:00 / UTC-05:00
US/Mountain                      GMT-07:00 / UTC-07:00
US/Pacific                       GMT-08:00 / UTC-08:00
US/Samoa                         GMT-11:00 / UTC-11:00
UTC                                    GMT / UTC+00:00
Universal                              GMT / UTC+00:00
W-SU                             GMT+03:00 / UTC+03:00
WET                                    GMT / UTC+00:00
Zulu                                   GMT / UTC+00:00

Here's an idea for your other requirement, listing cities for each offset:

public static void main(String[] args) throws IOException {
    // provide two formatters for the desired outputs GMT... and UTC...
    DateTimeFormatter gmtFormatter = DateTimeFormatter.ofPattern("OOOO");
    DateTimeFormatter utcFormatter = DateTimeFormatter.ofPattern("'UTC'xxx");
    // get a map of zones grouped by offset
    Map<ZoneId, List<ZoneId>> zonesPerOffset = 
            ZoneId.getAvailableZoneIds().stream()
                                        .map(ZoneId::of)
                                        .collect(Collectors.toList())
                                        .stream()
                                        .collect(Collectors.groupingBy(zoneId -> ZonedDateTime.now(zoneId).getOffset()));
    // print them
    zonesPerOffset.forEach((offset, zones) -> {
        String o = ZonedDateTime.now(zones.get(0)).format(utcFormatter);
        String zs = String.join(", ", zones.stream().map(zone -> {
            String[] idParts = zone.getId().split("/");
            return idParts[idParts.length - 1];
        }).collect(Collectors.toList()));
        String out = String.format("(%9s) %s", o, zs);
        System.out.println(out);
    });
}

Output (shortened):

(UTC+04:00) Yerevan, GMT-4, Dubai, Reunion, Mauritius, Saratov, Samara, Mahe, Baku, Muscat, Volgograd, Astrakhan, Tbilisi, Ulyanovsk
(UTC+00:00) London, GMT, GMT-0, Jersey, St_Helena, Guernsey, Isle_of_Man, GMT+0, Banjul, GMT, Freetown, GB-Eire, Bamako, GB, Conakry, Portugal, Universal, Sao_Tome, Nouakchott, Troll, UTC, Universal, Faeroe, Abidjan, Eire, Accra, Faroe, UCT, GMT0, Dublin, Zulu, Ouagadougou, Reykjavik, Madeira, Zulu, Iceland, Lisbon, Canary, Lome, Greenwich, Belfast, GMT0, Danmarkshavn, Dakar, Bissau, WET, Greenwich, Timbuktu, UCT, Monrovia, UTC
(UTC+08:00) Kuching, Chungking, GMT-8, Perth, Macao, Macau, Choibalsan, Shanghai, Ulan_Bator, Chongqing, Ulaanbaatar, Taipei, Manila, PRC, Ujung_Pandang, Harbin, Singapore, Brunei, West, Hong_Kong, Makassar, Hongkong, Kuala_Lumpur, Irkutsk, Singapore
(UTC+12:00) Kwajalein, Wallis, Funafuti, Nauru, Kwajalein, Wake, Norfolk, Tarawa, Kamchatka, GMT-12, Anadyr, Majuro

Doesn't look that well this way, but you can apply filters as desired. Maybe remove elements with leading UTC and more, maybe filter out specific continents, whatever.


As methods:

public static ZoneOffset getOffsetOf(ZoneId zoneId) {
    return ZonedDateTime.now(zoneId).getOffset();
}

// this one may throw an Exception if you pass an invalid zoneId as String!
public static ZoneOffset getOffsetOf(String zoneId) {
    return ZonedDateTime.now(ZoneId.of(zoneId)).getOffset();
}

public static List<ZoneId> getAllZoneIdsWith(ZoneOffset zoneOffset) {
    return ZoneId.getAvailableZoneIds()
                 .stream()
                 .filter(zoneId -> ZonedDateTime.now(ZoneId.of(zoneId))
                                                .getOffset().equals(zoneOffset))
                 .map(ZoneId::of)
                 .collect(Collectors.toList());
}

public static String getGmtFormat(ZoneId zoneId) {
    return String.format("(%s) %s",
                        ZonedDateTime.now(zoneId)
                                     .format(DateTimeFormatter.ofPattern("OOOO")),
                        zoneId.getId());
}

Use the last method in order to get the GMT formats of all available ZoneIds:

List<String> gmtFormats = ZoneId.getAvailableZoneIds()
                                .stream()
                                .map(zone -> getGmtFormat(ZoneId.of(zone)))
                                .collect(Collectors.toList());
deHaar
  • 17,687
  • 10
  • 38
  • 51
  • Thanks a lot for your answer, voted up. On the other hand, the other example seems more suitable for my displaying format. So, could you please have a look at @tricksSpecialist's answer and let me know if there is any part that should be changed? –  Dec 02 '21 at 07:17
  • Also, I think @tricksSpecialist's approach returns the GMT values based on Daylight Saving Time, is that true? For example on 28 March 2021 the GMT value will be changed in London. So, will that method get the correct GMT based on current date? For example now GMT+00 , and after 28 March, GMT+01). Is that true? –  Dec 02 '21 at 07:19
  • Well, I think the other answer is not bad, but I find it overhead to create a `ZoneComparator` and an `enum OffsetBase`. However, let's call that *a matter of taste* here. As far as I can see, both of our answers are likely to provide the correct offset-zone relations depending on daylight saving times, both are using `java.time.ZoneId`, `ZoneOffset` and so on, the base functionality is quite the same, that's why I would choose the shorter code ;-) – deHaar Dec 02 '21 at 07:34
  • Yes, I could also use your approach. Could you please update the format as the following: (GMT+14:00) Pacific/Apia, (GMT+14:00) Pacific/Kiritimati, (GMT+14:00) Etc/GMT-14 –  Dec 02 '21 at 07:50
  • And, what about **[How to make objects of an enum in java](https://stackoverflow.com/questions/28436543/how-to-make-objects-of-an-enum-in-java/28436571)**? Do you have any suggestion regarding to that question? –  Dec 02 '21 at 07:50
  • @Henry Try the last method I just added (`getGmtFormat(ZoneId)`). – deHaar Dec 02 '21 at 07:55
  • Thanks a lot, but I need the full list. So, could you please add a new method that accepts `ZoneId.getAvailableZoneIds()` ? Thanks in advance for your kind helps –  Dec 02 '21 at 07:57
  • @Henry See the most recent edit, it's an example of how to use the method for a full list. – deHaar Dec 02 '21 at 08:29
  • It seems to be ok, but I pass the lists to the frontend as enum: `enums.put("AvailableZoneIds", gmtFormats());` However, I want to pass 2 parameters in this enum e.g. object. One of them is **(GMT+01:00)**, the other part is **(GMT+01:00) Europe/Berlin**. So, how should I pass it as 2 parameters in this ``AvailableZoneIds` enum? –  Dec 02 '21 at 08:38
  • I don't quite get what you are trying to achieve with this `enum`, do you want to create an `enum` programmatically? – deHaar Dec 02 '21 at 08:44
  • I pass this enum to the frontend. But I need to send the values with GMT to the frontend. So, I am trying to pass 2 type of values in the same enum. Any idea? –  Dec 02 '21 at 08:56
  • Sounds like you should be passing a `Map` or an object. Why don't you write a class for this? Shouldn't be that hard now. – deHaar Dec 02 '21 at 09:03
  • Actually I am trying to add multiple elements to the Java object as mentioned on https://stackoverflow.com/questions/70196491/add-multiple-elements-to-object-in-java?noredirect=1#comment124088917_70196491. Any idea regarding to that question? You may post that page. –  Dec 02 '21 at 09:26
  • I think there is no need, you can just create your own `class ZoneInfo`, for example, and give it all the necessary attributes and methos. – deHaar Dec 02 '21 at 10:29
0

you can check this code below if return what you need:

    public class Stackoverflow {
    
    public static void main(String[] args) {
        List<String> gmt = getTimeZoneList(OffsetBase.GMT);
                gmt.forEach(System.out::println);
    
    }
    
    public static List<String> getTimeZoneList(OffsetBase base) {
         
        LocalDateTime now = LocalDateTime.now();
        return ZoneId.getAvailableZoneIds().stream()
          .map(ZoneId::of)
          .sorted(new ZoneComparator())
          .map(id -> String.format(
            "(%s%s) %s", 
            base, getOffset(now, id), id.getId()))
          .collect(Collectors.toList());
    }
    
    private static String getOffset(LocalDateTime dateTime, ZoneId id) {
        return dateTime
          .atZone(id)
          .getOffset()
          .getId()
          .replace("Z", "+00:00");
    }
    
    private static class ZoneComparator implements Comparator<ZoneId> {
        
        @Override
        public int compare(ZoneId zoneId1, ZoneId zoneId2) {
            LocalDateTime now = LocalDateTime.now();
            ZoneOffset offset1 = now.atZone(zoneId1).getOffset();
            ZoneOffset offset2 = now.atZone(zoneId2).getOffset();
            
            return offset1.compareTo(offset2);
        }
    }
    enum OffsetBase {
        GMT, UTC
    }
   }

the result was as following :

(GMT+14:00) Pacific/Apia
(GMT+14:00) Pacific/Kiritimati
(GMT+14:00) Etc/GMT-14

this is just example as the list is very long


update based on requester, so to get GMT for specific zone id.

public static List<String> getTimeZoneList(OffsetBase base, String wantedZoneId) {
     
    LocalDateTime now = LocalDateTime.now();
    return ZoneId.getAvailableZoneIds().stream()
      .map(ZoneId::of)
      .sorted(new ZoneComparator())
      .filter(zoneId -> zoneId.getId().equals(wantedZoneId))
      .map(id -> String.format(
        "(%s%s) %s", 
        base, getOffset(now, id), id.getId()))
      .collect(Collectors.toList());
}

this function is the same, just add [ filter ] function

  • @Henry please if this valid answer accept it also i updated it based on your request – tricksSpecialist Dec 01 '21 at 17:45
  • Ok, no need to update, I modified based on my needs. Thanks a lot. –  Dec 02 '21 at 05:59
  • But I just need a confirmation. I think this methods returns the GMT values based on **Daylight Saving Time**, is that true? For example on 28 March 2021 the GMT value will be changed in London. So, will this method get the correct GMT based on current date? For example now **GMT+00** , and after 28 March, **GMT+01**). Is that true? –  Dec 02 '21 at 06:23
  • @Henry yes, true and you can try it as i did just instead of `LocalDateTime.now();` try this `LocalDate date = LocalDate.of(2021, 3, 28); LocalDateTime now = LocalDateTime.of(date, LocalTime.MIDNIGHT);` and you can change the above data to after 28 and see the result you want – tricksSpecialist Dec 04 '21 at 06:19