132

With Java 8 we know use ZoneId.default() can get system default ZoneId, but how to get default ZoneOffset?

I see that a ZoneId has some "rules" and each rule has a ZoneOffset, is that means a ZoneId may have more than one ZoneOffset?

8
  • try ZoneOffset.systemDefault() Commented Jan 2, 2017 at 13:06
  • 2
    Many timezones have different offsets due to daylight savings time. Commented Jan 2, 2017 at 13:07
  • 30
    ZoneOffset.systemDefault() returns a ZoneId, though. Commented Jan 2, 2017 at 13:08
  • 4
    Current ZoneOffset is a function of ZoneId and Instant. ZoneOffset current = zone.getRules().getOffset(instant) Commented Jan 2, 2017 at 13:09
  • 4
    @FredSuvn I believe yanys is getting at the concept that it makes no sense to ask for an offset without specifying a time zone and a moment (a date-time value). Ex: Daylight Saving Time in America/Los_Angeles causes the offset to be -08:00 currently, but changes to -07:00 in the summer. So you cannot ask for an offset without saying when in effect and for which time zone. See my Answer for more discussion. Commented Jan 2, 2017 at 23:46

2 Answers 2

304

tl;dr

OffsetDateTime.now().getOffset()

But you likely should be using a time zone rather than a mere offset-from-UTC.

ZoneId.systemDefault()

Offset versus Time Zone

An offset-from-UTC is merely a number of hour, minutes, and seconds — nothing more. For example, -08:00 means eight hours behind the UTC, and +05:45 means five hours and forty-five minutes ahead of UTC.

A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. Anomalies such as Daylight Saving Time (DST) causing shifts in the offset over specific periods of time are tracked over time, in the past as they happened, and in the future when politicians have announced planned changes.

So better to use a zone when known.

The offset for any region varies over time. For example, DST in the United States shifts the offset by an hour for about half the year and then restoring that hour back to the offset during the other half of the year. The entire purpose of a time zone is to document those shifts in offset.

So it really makes no sense to ask for an offset without a date-time. In America/Los_Angeles, for example in part of this year the offset is -08:00 but in another part of the year it is -07:00 during DST.

OffsetDateTime

So let's specify a moment as an OffsetDateTime, and then extract the ZoneOffset.

OffsetDateTime odt = OffsetDateTime.now ();
ZoneOffset zoneOffset = odt.getOffset ();

odt.toString(): 2017-01-02T15:19:47.162-08:00

zoneOffset.toString(): -08:00

That now method is actually applying implicitly the JVM’s current default time zone. I suggest you always make that explicit by specifying your desired/expected time zone. Even if you want the current default zone, say so explicitly to make your intentions clear. Eliminate the ambiguity about whether you intended the default or failed to consider time zone as so often happens with programmers. Call ZoneId.systemDefault.

OffsetDateTime odt = OffsetDateTime.now ( ZoneId.systemDefault () );
ZoneOffset zoneOffset = odt.getOffset ();

ZoneId.systemDefault().toString(): America/Los_Angeles

odt: 2017-01-02T15:19:47.162-08:00

zoneOffsetOfOdt: -08:00

A caution about depending on the default zone: This default can be changed at any moment by any code in any thread within the JVM. If important, ask the user for their intended time zone.

You can ask the offset for its amount of time as a total number of seconds.

int offsetSeconds = zoneOffset.getTotalSeconds ();

offsetSeconds: -28800

ZonedDateTime

Another example: Perhaps you want to know what the offset will be on Christmas Day this year in Québec. Specify the time zone America/Montreal, get a ZonedDateTime, ask for its offset as a ZoneOffset object.

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate ld = LocalDate.of( 2017 , 12 , 25 );
ZonedDateTime zdtXmas = ld.atStartOfDay( z );
ZoneOffset zoneOffsetXmas = zdtXmas.getOffset();

zdtXmas.toString(): 2017-12-25T00:00-05:00[America/Montreal]

zoneOffsetXmas.toString(): -05:00

zoneOffsetXmas.getTotalSeconds(): -18000

Table of date-time types in Java, both modern and legacy.

ZoneId

As suggested in the comment by yanys, you can interrogate a ZoneId for a particular ZoneOffset by passing a moment as an Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

This is just another route to the same destination. Just like with OffsetDateTime and ZonedDateTime discussed above, we are specifying (a) a time zone, and (b) a moment.

Instant instant = zdtXmas.toInstant();
ZoneOffset zo = z.getRules().getOffset( instant );

For ZoneId: America/Montreal at instant: 2017-12-25T05:00:00Z the ZoneOffset is: -05:00

See all these examples’ code live at IdeOne.com.

ZoneOffset.systemDefault

The ZoneOffset class, a subclass of ZoneId, inherits the systemDefault method. However, we cannot use that method for the purpose of determining the current offset. The inherited method returns an object of the superclass, ZoneId, not an object of the subclass, ZoneOffset.

ZoneOffset zoneOffset = ZoneOffset.systemDefault() ;  // Error.
// Fails to compile. 
// Every ZoneOffset is a ZoneId, but not every ZoneId is a ZoneOffset. 
// We cannot assign a superclass object to a reference variable of the subclass type.

error: incompatible types: ZoneId cannot be converted to ZoneOffset

But there is another way.

Rules of the time zone

To get the current offset, we must take an alternate route. We need to go through the rules of the default time zone.

ZoneId zoneId = ZoneId.systemDefault ( );
ZoneRules rules = zoneId.getRules ( );

The rules change over time — by definition, as discussed above. So we must specify the moment for which we want to know the offset in effect. We specify the moment as an Instant object, a date and time as seen with an offset of zero hours-minutes-seconds from UTC.

Instant now = Instant.now ( );
ZoneOffset offset = rules.getOffset ( now );

Dump to console.

System.out.println ( "For zone " + zoneId + " at " + now + ", the offset is " + offset );

For zone America/Los_Angeles at 2023-07-07T23:19:09.084624Z, the offset is -07:00

Solar Time versus Political time

A bit more about offsets and time zones…

Solar Time has been used since pre-history, tracking each day by noting when the sun is directly overhead. Poke a stick in the ground, and watch its shadow. When the shadow is shortest, when the shadow begins to grow rather than shrink, then you know it is now noon. Formalize that with a sundial to track the hours pass.

With solar time, as you travel from town to town moving westward, noon arrives a little bit later. Moving eastward, noon arrives a bit sooner. So every town has its own noon, shared only with towns to the north and south along the same longitude.

Solar time was largely abandoned in the modern era. As trains, telegraphs, and telephones arrived, so did the need to coordinate temporally. So a point was picked for its near solar time of noon, and a large swath of land so many miles to the west and to the east is declared to all share the same 12:00 on the clock, the same number of hours offset ahead or behind the Greenwich Prime Meridian line. So began the tradition of every train stop displaying prominently a clock to let the town know of the standard time for their larger region rather than solar time for their own town. Generally, towns in the western edge of that time zone region will see their train station clock read 12:00 a little before the sun is overhead. Clocks in towns in the eastern edge of the region read 12:00 a little after the sun is overhead.

Politicians around the world showed a penchant for changing the offset(s) of their jurisdiction. The reasons vary, such diplomacy, war & occupation, and the silliness of Daylight Saving Time (DST). The reasons vary, but their changes come with surprising frequency. A time zone is a name given to a region to track its history of such changes. So an offset-from-UTC is merely a number of hours-minutes-seconds ahead or behind the prime meridian. A time zone is much more: a history of the past, present, and future changes to the offsets of a particular region. While two neighboring regions may today share the same offset-from-UTC, in the past or future they may differ depending on the differing whims or logic of their politicians.

This means modern time-tracking defined by politicians has little to do with geography. For example, the huge country of India today has a single time zone (offset-from-UTC of +05:30). So solar noon (sun directly over your head) is hours apart in various places across the vast subcontinent. The politicians of India decided this to help unify their diverse democracy. In other examples around the world, we see regions use their time zone as a symbol for international relations such as being different than their offending neighbor country, or choosing the same zone as a neighbor as relations thaw as seen recently in North Korea changing to match South Korea. So, nowadays, solar time is only one of several considerations in time-tracking.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Table of which java.time library to use with which version of Java or Android

7
  • you mean value of offset is different at different times in a zone? thx a lot!
    – FredSuvn
    Commented Jan 4, 2017 at 12:16
  • 2
    @FredSuvn That is the very definition of Daylight Saving Time (DST), half the year one offset and half the year another. And DST is not the only cause; other anomalies cause changes in offset for particular time zones. For example, in autumn 2016, Turkey decided to permanently adjust their offset one hour further ahead of UTC (+03:00). Politicians frequently make such changes, often with little notice. That leaves people scrambling to update their computers’ 'tzdata' database listing of zone definitions. Commented Jan 4, 2017 at 14:53
  • 5
    Best answer I've seen on SO in quite a while 😀 @FredSuvn you should accept the answer by clicking the ✅
    – earcam
    Commented Jul 27, 2017 at 12:19
  • For anyone else who is confused by the equation Time Zone = ( History-of-offsets + Rules-for-anomalies ). This does not mean that when using zone.getRules().getOffset(instant), you need to add any additional anomalies to the acquired offset manually, such as DST. The offset returned by getOffset(instant) is already the exact number of hours, minutes and seconds offset from GMT at that instant in time. (I always thought the offset is just the geographic offset which describes how much earlier/later the sun rises in that region, but it is actually considering all the rules.) Commented Feb 25, 2019 at 10:05
  • 1
    I suspect that the reason my you can't compile with a ZoneOffset.systemDefault() call is that the return type is ZoneId (the parent class of ZoneOffset). Commented Jul 7, 2023 at 13:06
3

Depending on your objective, you may be able to bypass ZoneOffset entirely.

Assuming you just need a ZoneOffset for e.g. LocalDateTime.ofEpochSecond(), you may replace

ZoneOffset offset = OffsetDateTime.now().getOffset();
LocalDateTime dt1 = LocalDateTime.ofEpochSecond(seconds, 0, offset);

with

LocalDateTime dt2 = LocalDateTime.ofInstant(
    Instant.ofEpochSecond(seconds), 
    ZoneId.systemDefault());

where dt1.equals(dt2) is true.

Not the answer you're looking for? Browse other questions tagged or ask your own question.