64

I need to save the phone's timezone in the format [+/-]hh:mm

I am using TimeZone class to deal with this, but the only format I can get is the following:

PST -05:00
GMT +02:00

I would rather not substring the result, is there any key or option flag I can set to only get the value and not the name of that timezone (GMT/CET/PST...)?

Ojonugwa Jude Ochalifu
  • 26,627
  • 26
  • 120
  • 132
Mr Bean
  • 979
  • 1
  • 7
  • 17
  • For new readers to the question 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. May 06 '21 at 03:59

8 Answers8

120

I need to save the phone's timezone in the format [+/-]hh:mm

No, you don't. Offset on its own is not enough, you need to store the whole time zone name/id. For example I live in Oslo where my current offset is +02:00 but in winter (due to ) it is +01:00. The exact switch between standard and summer time depends on factors you don't want to explore.

So instead of storing + 02:00 (or should it be + 01:00?) I store "Europe/Oslo" in my database. Now I can restore full configuration using:

TimeZone tz = TimeZone.getTimeZone("Europe/Oslo")

Want to know what is my time zone offset today?

tz.getOffset(new Date().getTime()) / 1000 / 60   //yields +120 minutes

However the same in December:

Calendar christmas = new GregorianCalendar(2012, DECEMBER, 25);
tz.getOffset(christmas.getTimeInMillis()) / 1000 / 60   //yields +60 minutes

Enough to say: store time zone name or id and every time you want to display a date, check what is the current offset (today) rather than storing fixed value. You can use TimeZone.getAvailableIDs() to enumerate all supported timezone IDs.

Brent Worden
  • 10,624
  • 7
  • 52
  • 57
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 7
    Nope, I am interacting with an api, however good or bad it is designed. Let's not go into that. They require me to send a timezone like hh:mm only.... naff i know. – Mr Bean Jul 09 '12 at 16:57
  • 3
    @MrBean: so by coincidence I've shown you what to do: if you already have `TimeZone` class, just call `getOffset()` with current date and convert it from milliseconds to minutes. Then format to `hh:mm`. – Tomasz Nurkiewicz Jul 09 '12 at 16:59
  • Great update Tomasz, however, can I ask you to expand a little. I am storing a date in the serverside Database as UTC (obviously) and Im storing my TZ name (in your example "Europe/Oslo") also in the serverside Database. I want to pass a date back to the client with the correct offset applied, should I (can I?) do that, or do I pass the date and offset separately? The problem being that Im using GWT on the client, so Im unable to use java.util.TimeZone and so cannot convert "Europe/Oslo" as com.google.gwt.i18n.shared.TimeZone.createTimeZone("Europe/Oslo") doesnt work!! – johnvdenley Aug 13 '12 at 03:15
  • @johnvdenley: please ask a separate question, feel free to post follow-up link here. – Tomasz Nurkiewicz Aug 13 '12 at 08:03
  • OK, I did and its here, although looks like the only solution is to convert the time to apply the required timezone on the server, turn it into a string, parse it into a string[] and pass that back to the client... http://stackoverflow.com/questions/11989219/sending-a-date-and-timezone-from-gae-server-to-gwt-client – johnvdenley Aug 16 '12 at 16:22
  • 1
    That was exactly the answer I was looking for, very well presented, thank you! – djule5 Mar 27 '13 at 15:23
77

@MrBean - I was in a similar situation where I had to call a 3rd-party web service and pass in the Android device's current timezone offset in the format +/-hh:mm. Here is my solution:

public static String getCurrentTimezoneOffset() {

    TimeZone tz = TimeZone.getDefault();  
    Calendar cal = GregorianCalendar.getInstance(tz);
    int offsetInMillis = tz.getOffset(cal.getTimeInMillis());

    String offset = String.format("%02d:%02d", Math.abs(offsetInMillis / 3600000), Math.abs((offsetInMillis / 60000) % 60));
    offset = (offsetInMillis >= 0 ? "+" : "-") + offset;

    return offset;
} 
PuguaSoft
  • 891
  • 7
  • 3
26

With Java 8 now, you can use:

Integer offset  = ZonedDateTime.now().getOffset().getTotalSeconds();

to get the current system time offset from UTC. Then you can convert it to any format you want. Found it useful for my case.

Example: https://docs.oracle.com/javase/tutorial/datetime/iso/timezones.html

informatik01
  • 16,038
  • 10
  • 74
  • 104
voidMainReturn
  • 3,339
  • 6
  • 38
  • 66
13

With Java 8, you can achieve this with the following code.

  • To get the Zone offset

      TimeZone tz = TimeZone.getDefault();
      String offsetId = tz.toZoneId().getRules().getStandardOffset(Instant.now()).getId();
    

    and the offsetId will be something like +01:00.

    Note ZoneRules::getStandardOffset give you the offset of the zone ID prior Daylight Saving Time gets applied, since the offset of the zone ID may have changed over time, as the Javadoc mention.

    Gets the standard offset for the specified instant in this zone. This provides access to historic information on how the standard offset has changed over time. The standard offset is the offset before any daylight saving time is applied. This is typically the offset applicable during winter.

  • To get the current offset with DST

    TimeZone.getDefault().toZoneId().getRules().getOffset(Instant.now()).getId()
    

    The returned identifier will return the actual offset for this zone.

    Gets the offset applicable at the specified instant in these rules. The mapping from an instant to an offset is simple, there is only one valid offset for each instant. This method returns that offset.

The way the functions ZoneRules::getStandardOffset / ZoneRules::getOffset work, i.e. they needs an Instant as parameter is because the specific time point is needed to compute the offset for this zone. Because the rule for this zone may change over time.

For example a zone may have changed its TimeZone, like Samoa in 2011. Or countries that change their daylight saving time policies, like South Korea or Japan did in the past, maybe some countries in Europe in the future.

It is the reason why @Tomasz Nurkiewicz's answer recommends to not store offset in signed hour format directly, but make the code computes the offset each time it is needed.

Other APIs

ZoneId.systemDefault().getRules().getStandardOffset(Instant.now())
ZoneId.systemDefault().getRules().getOffset(Instant.now())
// ZonedDateTime don't expose the standard offset (i.e without DST)
ZonedDateTime.now().getOffset()

References

bric3
  • 40,072
  • 9
  • 91
  • 111
SuN
  • 421
  • 5
  • 11
  • Note this answer gives the ID of the timezone. Use `.getRules().getOffset(Instant.now())` for the current offset. Also check [@ArvindKumarAvinash's answer](https://stackoverflow.com/a/67409440/48136) / [voidMainReturn's answer](https://stackoverflow.com/a/47701853/48136) for alternative way. – bric3 Jul 28 '23 at 08:31
9
ZoneId here = ZoneId.of("Europe/Kiev");
ZonedDateTime hereAndNow = Instant.now().atZone(here);
String.format("%tz", hereAndNow);

will give you a standardized string representation like "+0300"

5

java.time: the modern date-time API

You can get the offset of a timezone using ZonedDateTime#getOffset.

Note that the offset of a timezone that observes DST changes as per the changes in DST. For other places (e.g. India), it remains fixed. Therefore, it is recommended to mention the moment when the offset of a timezone is shown. A moment is mentioned as Instant.now() and it represents the date-time in UTC (represented by Z which stands for Zulu and specifies the timezone offset of +00:00 hours).

Display offset of all timezones returned by ZoneId.getAvailableZoneIds():

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

public class Main {
    public static void main(String[] args) {
        // Test
        Instant instant = Instant.now();
        System.out.println("Timezone offset at " + instant);
        System.out.println("==============================================");
        ZoneId.getAvailableZoneIds()
            .stream()
            .sorted()
            .forEach(strZoneId -> System.out.printf("%-35s: %-6s%n",
                strZoneId, getTzOffsetString(ZoneId.of(strZoneId), instant)));
    }

    static String getTzOffsetString(ZoneId zoneId, Instant instant) {
        return ZonedDateTime.ofInstant(instant, zoneId).getOffset().toString();
    }
}

Output:

Timezone offset at 2021-05-05T21:45:34.150901Z
==============================================
Africa/Abidjan                     : Z     
Africa/Accra                       : Z     
Africa/Addis_Ababa                 : +03:00
...
...
...    
W-SU                               : +03:00
WET                                : +01:00
Zulu                               : Z  

Learn more about the the modern date-time API* from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • Note this answer gives the current offset, but if you need the timezone id better use [@Sun's answer](https://stackoverflow.com/a/57953711/48136). – bric3 Jul 28 '23 at 08:33
4

We can easily get the millisecond offset of a TimeZone with only a TimeZone instance and System.currentTimeMillis(). Then we can convert from milliseconds to any time unit of choice using the TimeUnit class.

Like so:

public static int getOffsetHours(TimeZone timeZone) {
    return (int) TimeUnit.MILLISECONDS.toHours(timeZone.getOffset(System.currentTimeMillis()));
}

Or if you prefer the new Java 8 time API

public static ZoneOffset getOffset(TimeZone timeZone) { //for using ZoneOffsett class
    ZoneId zi = timeZone.toZoneId();
    ZoneRules zr = zi.getRules();
    return zr.getOffset(LocalDateTime.now());
}

public static int getOffsetHours(TimeZone timeZone) { //just hour offset
    ZoneOffset zo = getOffset(timeZone);
    TimeUnit.SECONDS.toHours(zo.getTotalSeconds());
}
MeetTitan
  • 3,383
  • 1
  • 13
  • 26
  • 1
    you can have timezones that are not in whole hours e.g. Banglore is GMT +5.5 hours so I not recommend to use the getOffsetHours – Richard Stokes Nov 26 '19 at 06:16
  • @RichardStokes, Don't round to an int, then. The maths is right, even if you need a time zone offset that cannot be represented as an integer. – MeetTitan May 02 '23 at 01:23
2

I know this is old, but I figured I'd give my input. I had to do this for a project at work and this was my solution.

I have a Building object that includes the Timezone using the TimeZone class and wanted to create zoneId and offset fields in a new class.

So what I did was create:

private String timeZoneId;
private String timeZoneOffset;

Then in the constructor I passed in the Building object and set these fields like so:

this.timeZoneId = building.getTimeZone().getID();
this.timeZoneOffset = building.getTimeZone().toZoneId().getId();

So timeZoneId might equal something like "EST" And timeZoneOffset might equal something like "-05:00"

I would like to not that you might not

Crislips
  • 420
  • 1
  • 4
  • 13
  • 2
    building.getTimeZone().toZoneId().getId() returns the text (like "Europe/Oslo"), not the offset (like "2") – SoloPilot Oct 25 '17 at 02:33