Java ZoneId Tutorial with Examples

View more Tutorials:

Follow us on our fanpages to receive notifications every time there are new articles. Facebook Twitter

1- ZoneId

The ZoneId class is used to identify a time zone and provide the conversion rules between LocalDateTime and Instant. In terms of offset rules, ZoneId is divided into 2 types:
  1. ZoneId with a fixed time zone offset, such as "UTC+07", "GMT-05:40", "UT-03", "+05:50".
  2. ZoneId with a non-fixed time zone offset, such as "Europe/Paris". Its time zone offset depends on the time on the timeline or depends on the day-of-year.
For example, ZoneId.of("Asia/Ho_Chi_Minh") is the time zone identifier of southern Vietnam. During the period from 1960 to before June 13th, 1975, the time zone offset of this ZoneId was +8, but later changed to +7 to unify with northern Vietnam.

public abstract class ZoneId implements Serializable {
     public abstract String getId();  
     public abstract ZoneRules getRules();
Although ZoneId is an abstract class, it provides a few static factory methods to create ZoneId objects. Two important properties of ZoneId are id and rules.
  • String id: The ID is unique.
  • ZoneRules rules: Rules for determining the time zone offset at a specific time on the timeline.
The ZoneOffset class is a subclass of ZoneId.
Based on the syntax of ID, ZoneId is divided into 3 types:
Type Example getId()
Type1 Region-based ZoneId ZoneId.of("Europe/Paris") Europe/Paris
Type2 Offset-based ZoneId ZoneOffset.of("-06") -06
ZoneOffset.of("+06:05:20") +06:05:20
Type3 UTC/GMT/UT ZoneId ZoneId.ofOffset("UTC", ZoneOffset.of("+06")) UTC+06
ZoneId.of("GMT-06:05:20") GMT-06:05:20

2- Type1: Region-based ZoneId

Region-based ZoneId, the value of the zoneId parameter must be 2 or more characters, and must not start with "UTC", "GMT", "UT", "+", "- ". There are many values provided, such as "Europe/Paris", "Asia/Ho_Chi_Minh",... The static method ZoneId.getAvailableZoneIds() returns a set of those zoneId(s).

public static ZoneId of(String zoneId)
You can also define a geographic area and provide rules for it through the ZoneRulesProvider.
  • TODO Link?

ZoneId zoneId1 = ZoneId.of("Europe/Paris");  
ZoneId zoneId2 = ZoneId.of("Asia/Ho_Chi_Minh");  

System.out.println(zoneId1.getId()); // Europe/Paris
System.out.println(zoneId2.getId()); // Asia/Ho_Chi_Minh

// Show format of Zoned Date Time
ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 6, 22, 0, 0, 0, 0, zoneId2);
System.out.println("zonedDateTime: " + zonedDateTime); // 2021-06-22T00:00+07:00[Asia/Ho_Chi_Minh]
Region-based ZoneId contains historical data about time zone offsets. Let's look at a situation:
On January 1st, 1986, the Nepalese government announced its time zone as +5:45 GMT. That means they are 15 minutes faster than neighboring India. This move is intended to make a difference with the giant neighbor and show the national pride of the Nepalese people.
Nepal's time zone ID is "Asia/Kathmandu". The example below shows that ZoneId.of("Asia/Kathmandu") already contains historical data:

package org.o7planning.zoneid.type.ex;

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class ZoneId_nepal_ex1 {

    public static void main(String[] args) {
        ZoneId nepalZoneId = ZoneId.of("Asia/Kathmandu");

        ZonedDateTime zdt1 = ZonedDateTime.of(1985, 12, 30, 0, 0, 0, 0, nepalZoneId);
        System.out.println(zdt1); // 1985-12-30T00:00+05:30[Asia/Kathmandu]


        ZonedDateTime zdt2 = ZonedDateTime.of(1986, 1, 1, 0, 0, 0, 0, nepalZoneId);
        System.out.println(zdt2); // 1986-01-01T00:15+05:45[Asia/Kathmandu]


Continuing with the example above, we review the time zone offset information from RoleRules:

ZoneId nepalZoneId = ZoneId.of("Asia/Kathmandu");

ZoneRules rules = nepalZoneId.getRules();

LocalDateTime ldt1 = LocalDateTime.of(1985, 12, 30, 0, 0, 0, 0);
ZoneOffset offset1 = rules.getOffset(ldt1);
System.out.println(offset1); // +05:30

LocalDateTime ldt2 = LocalDateTime.of(1986, 1, 1, 0, 15, 0, 0);
ZoneOffset offset2 = rules.getOffset(ldt2);  
System.out.println(offset2); // +05:45

3- Type2: Offset-based ZoneId

// Static method of ZoneOffset class.
public static ZoneOffset of(String offsetId)

public static ZoneOffset ofHours(int hours)  
public static ZoneOffset ofHoursMinutes(int hours, int minutes)  
public static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds)
public static ZoneOffset ofTotalSeconds(int totalSeconds)  
Offset-based ZoneId: These time zone ID(s) start with "+" or "-". The offsetId parameter must have the following format:
  • Z - for UTC
  • +h
  • +hh
  • +hh:mm
  • -hh:mm
  • +hhmm
  • -hhmm
  • +hh:mm:ss
  • -hh:mm:ss
  • +hhmmss
  • -hhmmss

ZoneId zoneId1 = ZoneOffset.of("-06");
ZoneId zoneId2 = ZoneOffset.of("+06:05:20");
ZoneId zoneId3 = ZoneOffset.ofHoursMinutes(9, 45);

System.out.println(zoneId1.getId()); // -06:00
System.out.println(zoneId2.getId()); // +06:05:20
System.out.println(zoneId3.getId()); // +09:45

// Show format of Zoned Date Time:
ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 6, 22, 0, 0, 0, 0, zoneId2);
System.out.println("zonedDateTime: " + zonedDateTime); // 2021-06-22T00:00+06:05:20
Their time zone offsets are fixed:

ZoneId zoneId = ZoneOffset.of("+07:05:30");

System.out.println(zoneId.getId()); // +07:05:30

ZoneRules zoneRules = zoneId.getRules();
ZoneOffset zoneOffset = zoneRules.getOffset(Instant.now());
System.out.println("zoneOffset: " + zoneOffset.getId()); // +07:05:30
  • ZoneOffset
  • ZoneRules

4- Type3: UTC, GMT, UT ZoneId

UTC, GMT, UT ZoneId: Time zone ID(s) of this type begin with "UTC", "GMT" or "UT" followed by "+" or "-".

public static ZoneId ofOffset(String prefix, ZoneOffset offset)

public static ZoneId of(String zoneId)
  • TODO Link?

ZoneId z31 = ZoneId.ofOffset("UTC", ZoneOffset.of("+06"));
ZoneId z32 = ZoneId.ofOffset("GMT", ZoneOffset.of("-06:05:20"));
ZoneId z33 = ZoneId.ofOffset("UT", ZoneOffset.of("+05:20"));

System.out.println(z31.getId()); // UTC+06:00
System.out.println(z32.getId()); // GMT-06:05:20
System.out.println(z33.getId()); // UT+05:20   

// Parser:
ZoneId z31b = ZoneId.of("UTC+06:00");
ZoneId z32b = ZoneId.of("GMT-06:05:20");
ZoneId z33b = ZoneId.of("UT+05:20");

System.out.println(z31b.getId()); // UTC+06:00
System.out.println(z32b.getId()); // GMT-06:05:20
System.out.println(z33b.getId()); // UT+05:20 

UTC, GMT, UT time zone ID(s) have a fixed time zone offset.

ZoneId zoneId = ZoneId.ofOffset("GMT", ZoneOffset.of("+09"));
System.out.println(zoneId); // GMT+09:00

ZoneRules zoneRules = zoneId.getRules();
System.out.println("isFixedOffset? " + zoneRules.isFixedOffset()); // true

ZoneOffset zoneOffset = zoneRules.getOffset(Instant.now());
System.out.println("zoneOffset: " + zoneOffset); // +09:00

// Show format of Zoned Date Time.
ZonedDateTime zonedDateTime = ZonedDateTime.of(2021, 6, 22, 0, 0, 0, 0, zoneId);
System.out.println("zonedDateTime: " + zonedDateTime); // 2021-06-22T00:00+09:00[GMT+09:00]

isFixedOffset? true
zoneOffset: +09:00
zonedDateTime: 2021-06-22T00:00+09:00[GMT+09:00]

5- ZoneId methods

Static factory methods:

public static ZoneId systemDefault()    
public static ZoneId from(TemporalAccessor temporal)  
public static ZoneId of(String zoneId, Map<String, String> aliasMap)  
public static ZoneId of(String zoneId)  
public static ZoneId ofOffset(String prefix, ZoneOffset offset)  
Other methods:

public static Set<String> getAvailableZoneIds()  
public abstract String getId()  
public String getDisplayName(TextStyle style, Locale locale)  
public abstract ZoneRules getRules();
public ZoneId normalized()

6- systemDefault()

Return the system default time zone. On operating systems such as Windows, Linux, Mac OS all allows you to change the system's default time zone.

public static ZoneId systemDefault()    
This method calls the TimeZone.getDefault() method and converts the result to a ZoneId. If the system time zone changes, the result returned by this method also changes.

ZoneId zoneId = ZoneId.systemDefault();



7- of(String)

Return a ZoneId object by searching for an available ZoneId in the system corresponding to the given zoneId string. If not found, it will parse the zoneId string to create a ZoneOffset object (Note: ZoneOffset is a subclass of ZoneId).

public static ZoneId of(String zoneId)  

Case 1:

ZoneId(s) are predefined in the system related to a specific geographic area. You can obtain this set of ZoneId(s) from the ZoneId.getAvailableZoneIds() method.
  • Asia/Bishkek
  • Europe/Paris
  • ...

// ZoneId from region ID
ZoneId zoneId1 = ZoneId.of("Europe/Paris");
System.out.println("zoneId1: " + zoneId1); // Europe/Paris

ZoneId zoneId2 = ZoneId.of("America/Chicago");
System.out.println("zoneId2: " + zoneId2); // America/Chicago

Case 2:

If the zoneId parameter is "Z", the result is ZoneOffset.UTC. Otherwise, if the zoneId parameter is only one character and other than "Z", it is considered invalid and a DateTimeException will be thrown.

ZoneId zoneId = ZoneId.of("Z"); // return ZoneOffset.UTC
System.out.println("ZoneId.of('Z'): " + zoneId); // Z
System.out.println("ZoneOffset.UTC: " + ZoneOffset.UTC); // Z

boolean same = zoneId == ZoneOffset.UTC;  
System.out.println("ZoneId.of('Z') == ZoneOffset.UTC? " + same); // true

Case 3:

If the zoneId parameter starts with "+" or "-" then it will be parsed as a ZoneOffset by the ZoneOffset.of(zoneId) method.

ZoneId zoneId = ZoneId.of("+09:30");
ZoneOffset zoneOffset1 = (ZoneOffset) zoneId;

// Same as:
ZoneOffset zoneOffset2 = ZoneOffset.of("+09:30");

System.out.println("zoneId: " + zoneId); // +09:30
System.out.println("zoneOffset: " + zoneOffset2); // +09:30
System.out.println("zoneOffset1.equals(zoneOffset2)? " + zoneOffset1.equals(zoneOffset2)); // true

Case 4:

If the zoneId parameter is "UTC", "GMT", or "UT", the method returns a ZoneId object with the same ZoneRules as ZoneOffset.UTC.

ZoneId zoneId = ZoneOffset.UTC;

ZoneId zoneId1 = ZoneId.of("UTC");
ZoneId zoneId2 = ZoneId.of("GMT");
ZoneId zoneId3 = ZoneId.of("UT");

// Print out Zone-ID
System.out.println(zoneId.getId());  // Z
System.out.println(zoneId1.getId()); // UTC
System.out.println(zoneId2.getId()); // GMT
System.out.println(zoneId3.getId()); // UT

ZoneRules rules = zoneId.getRules();
ZoneRules rules1 = zoneId1.getRules();
ZoneRules rules2 = zoneId2.getRules();
ZoneRules rules3 = zoneId3.getRules();

System.out.println(rules);  // ZoneRules[currentStandardOffset=Z]
System.out.println(rules1); // ZoneRules[currentStandardOffset=Z]
System.out.println(rules2); // ZoneRules[currentStandardOffset=Z]
System.out.println(rules3); // ZoneRules[currentStandardOffset=Z]

System.out.println("rules1.equals(rules): " + rules1.equals(rules)); // true
System.out.println("rules2.equals(rules): " + rules2.equals(rules)); // true
System.out.println("rules3.equals(rules): " + rules3.equals(rules)); // true

Case 5:

If the zoneId parameter begins with "UTC+", "UTC-", "GMT+", "GMT-", "UT+", "UT-", a ZoneId object is returned. For example, the zoneId parameter that equals to "GMT+09:30" would be equivalent to ZoneId.ofOffset("GMT",ZoneOffset.of("+09:30")).

// UTC
ZoneId zoneId1a = ZoneId.of("UTC+09:30");
ZoneId zoneId1b = ZoneId.ofOffset("UTC", ZoneOffset.of("+09:30"));
ZoneId zoneId1c = ZoneId.ofOffset("UTC", ZoneOffset.ofHoursMinutes(9, 30));
System.out.println(zoneId1a); // UTC+09:30
System.out.println(zoneId1b); // UTC+09:30
System.out.println(zoneId1c); // UTC+09:30

// GMT
ZoneId zoneId2a = ZoneId.of("GMT+05:30:20");
ZoneId zoneId2b = ZoneId.ofOffset("GMT", ZoneOffset.of("+05:30:20"));
ZoneId zoneId2c = ZoneId.ofOffset("GMT", ZoneOffset.ofHoursMinutesSeconds(5, 30, 20));
System.out.println(zoneId2a); // GMT+05:30:20
System.out.println(zoneId2b); // GMT+05:30:20
System.out.println(zoneId2c); // GMT+05:30:20

// UT
ZoneId zoneId3a = ZoneId.of("UT-07");
ZoneId zoneId3b = ZoneId.ofOffset("UT", ZoneOffset.of("-07"));
ZoneId zoneId3c = ZoneId.ofOffset("UT", ZoneOffset.ofHours(-7));
System.out.println(zoneId3a); // UT-07:00
System.out.println(zoneId3b); // UT-07:00
System.out.println(zoneId3c); // UT-07:00

8- of(String, Map<String, String>)

public static ZoneId of(String zoneId, Map<String, String> aliasMap)  

package org.o7planning.zoneid.fm;

import java.time.ZoneId;
import java.util.HashMap;
import java.util.Map;

public class ZoneId_of_aliasMap_ex1 {

    public static void main(String[] args) {
        // Alias IDs:
        Map<String, String> aliasMap = new HashMap<>();
        aliasMap.put("hcm", "Asia/Ho_Chi_Minh");
        aliasMap.put("usc", "US/Central");
        aliasMap.put("prs", "Europe/Paris");

        ZoneId frZoneId = ZoneId.of("prs", aliasMap);
        ZoneId vnZoneId = ZoneId.of("hcm", aliasMap);
        ZoneId usZoneId = ZoneId.of("US/Central", aliasMap);
        ZoneId jpZoneId = ZoneId.of("Asia/Tokyo", aliasMap);
        ZoneId os9ZoneId = ZoneId.of("+09", aliasMap);

        System.out.println(frZoneId); // Europe/Paris
        System.out.println(vnZoneId); // Asia/Ho_Chi_Minh
        System.out.println(usZoneId); // US/Central
        System.out.println(jpZoneId); // Asia/Tokyo
        System.out.println(os9ZoneId); // +09:00

        System.out.println(" ------ ");

        // throws ZoneRulesException - Unknown time-zone ID: xx
        ZoneId xxZoneId = ZoneId.of("xx", aliasMap); // throws ZoneRulesException


Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: xx
    at java.base/java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:279)
    at java.base/java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:234)
    at java.base/java.time.ZoneRegion.ofId(ZoneRegion.java:120)
    at java.base/java.time.ZoneId.of(ZoneId.java:408)
    at java.base/java.time.ZoneId.of(ZoneId.java:356)
    at java.base/java.time.ZoneId.of(ZoneId.java:312)
    at org.o7planning.zoneid.fm.ZoneId_of_aliasMap_ex1.main(ZoneId_of_aliasMap_ex1.java:31)

package org.o7planning.zoneid.fm;

import java.time.ZoneId;
import java.util.Map;

public class ZoneId_of_aliasMap_ex2 {

    public static void main(String[] args) {
        // Alias IDs:
        Map<String, String> aliasMap = ZoneId.SHORT_IDS;

        aliasMap.forEach((k, v) -> System.out.println(k + " --> " + v));

        System.out.println(" ------ ");

        ZoneId zoneId1 = ZoneId.of("JST", aliasMap);
        ZoneId zoneId2 = ZoneId.of("VST", aliasMap);  

        System.out.println(zoneId1); // Asia/Tokyo
        System.out.println(zoneId2); // Asia/Ho_Chi_Minh

NET --> Asia/Yerevan
CST --> America/Chicago
IST --> Asia/Kolkata
AET --> Australia/Sydney
BST --> Asia/Dhaka
ACT --> Australia/Darwin
HST --> -10:00
NST --> Pacific/Auckland
AST --> America/Anchorage
MST --> -07:00
SST --> Pacific/Guadalcanal
CTT --> Asia/Shanghai
PRT --> America/Puerto_Rico
ECT --> Europe/Paris
EAT --> Africa/Addis_Ababa
EST --> -05:00
PNT --> America/Phoenix
PLT --> Asia/Karachi
CNT --> America/St_Johns
IET --> America/Indiana/Indianapolis
VST --> Asia/Ho_Chi_Minh
JST --> Asia/Tokyo
ART --> Africa/Cairo
PST --> America/Los_Angeles
BET --> America/Sao_Paulo
MIT --> Pacific/Apia
CAT --> Africa/Harare
AGT --> America/Argentina/Buenos_Aires

9- ofOffset(String, ZoneOffset)

The prefix parameter can only take one of four values: "UTC", "GMT", "UT", "".

public static ZoneId ofOffset(String prefix, ZoneOffset offset)  

ZoneId zoneId1 = ZoneId.ofOffset("UTC", ZoneOffset.ofHours(9));
ZoneId zoneId2 = ZoneId.ofOffset("GMT", ZoneOffset.ofHoursMinutes(9, 30));
ZoneId zoneId3 = ZoneId.ofOffset("UT", ZoneOffset.ofHours(-7));

ZoneId zoneId4 = ZoneId.ofOffset("", ZoneOffset.ofHours(-5)); // Can cast to ZoneOffset

System.out.println(zoneId1); // UTC+09:00
System.out.println(zoneId2); // GMT+09:30
System.out.println(zoneId3); // UT-07:00
System.out.println(zoneId4); // -05:00
  • TODO Link?

10- from(TemporalAccessor)

Return the ZoneId object associated with the given TemporalAccessor object.

public static ZoneId from(TemporalAccessor temporal)  

package org.o7planning.zoneid.fm;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;

public class ZoneId_from_ex1 {

    public static void main(String[] args) {
        // Obtains the current date-time from the system clock in the default time-zone.
        TemporalAccessor temporalAccessor = ZonedDateTime.now();
        System.out.println("ZonedDateTime: " + temporalAccessor);

        ZoneId zoneId = ZoneId.from(temporalAccessor);
        System.out.println("zoneId: " + zoneId);

ZonedDateTime: 2021-06-24T21:34:23.518679+06:00[Asia/Bishkek]
zoneId: Asia/Bishkek

11- getAvailableZoneIds()

Return a Set<String> containing all available time zone ID(s) in the system. Its number of elements can increase over time, but in common applications it is usually a fixed size. This method is thread-safe.
  • Include all Region-based ZoneId.
  • Exclude Offset-based ZoneId

public static Set<String> getAvailableZoneIds()

package org.o7planning.zoneid.ex;

import java.time.ZoneId;
import java.util.Set;

public class ZoneId_getAvailableZoneIds_ex1 {

    public static void main(String[] args) {
        Set<String> zoneIds = ZoneId.getAvailableZoneIds();

        for (String zoneId : zoneIds) {


12- getRules()

Return a ZoneRules object representing the time-zone rules of this ZoneId.

public abstract ZoneRules getRules()
The time zone offset in ZoneRules obtained from a Region-based ZoneId is not fixed. It depends on the specific time or depends on the time of year.
Example: ZoneId.of("Asia/Ho_Chi_Minh") is the time zone of southern Vietnam. From 1960 to before June 13th, 1975, this ZoneId used time zone +8, then changed to time zone +7 to unify with northern Vietnam.

package org.o7planning.zoneid.ex;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.zone.ZoneRules;

public class ZoneId_getRules_ex1 {

    public static void main(String[] args) {
        ZoneId zoneId = ZoneId.of("Asia/Ho_Chi_Minh");
        ZoneRules zoneRules = zoneId.getRules();
        ZoneOffset zoneOffset1 = zoneRules.getOffset(LocalDateTime.of(1975, 6, 12, 0, 0, 0));
        ZoneOffset zoneOffset2 = zoneRules.getOffset(LocalDateTime.of(1975, 6, 13, 0, 0, 0));
        System.out.println("zoneOffset1: " + zoneOffset1); // +08:00
        System.out.println("zoneOffset2: " + zoneOffset2); // +07:00

zoneOffset1: +08:00
zoneOffset2: +07:00
You can also create a ZoneId with custom time zone rules by the ZoneRulesProvider:
  • ZoneRules
  • ZoneRulesProvider

13- normalized()

The normalized() method returns a normalized ZoneId. Basically, it checks if the timezone offset of this ZoneId is fixed or not. If fixed, ZoneOffset will be returned, otherwise it returns this ZoneId.

public ZoneId normalized()

package org.o7planning.zoneid.ex;

import java.time.ZoneId;

public class ZoneId_normalized_ex1 {

    public static void main(String[] args) {
        ZoneId zoneId1 = ZoneId.of("US/Central");
        System.out.println("zoneId1: " + zoneId1); // US/Central
        System.out.println("zoneId1.normalized(): " + zoneId1.normalized()); // US/Central

        ZoneId zoneId2 = ZoneId.of("Asia/Tokyo");
        System.out.println("\nzoneId2: " + zoneId2); // Asia/Tokyo
        System.out.println("zoneId2.normalized(): " + zoneId2.normalized()); // Asia/Tokyo

        ZoneId zoneId3 = ZoneId.of("UTC-09:30");
        System.out.println("\nzoneId3: " + zoneId3); // UTC-09:30
        System.out.println("zoneId3.normalized(): " + zoneId3.normalized()); // -09:30

zoneId1: US/Central
zoneId1.normalized(): US/Central

zoneId2: Asia/Tokyo
zoneId2.normalized(): Asia/Tokyo

zoneId3: UTC-09:30
zoneId3.normalized(): -09:30

View more Tutorials:

Maybe you are interested

These are online courses outside the o7planning website that we introduced, which may include free or discounted courses.