11

I need to get time difference between two dates in different time zones. Currently I am doing this:

Calendar c1=Calendar.getInstance(TimeZone.getTimeZone("EDT"));
Calendar c2=Calendar.getInstance(TimeZone.getTimeZone("GMT"));
String diff=((c2.getTimeInMillis()-c1.getTimeInMillis())/(1000*60*60))+" hours";
new AlertDialog.Builder(this).setMessage(diff).create().show();

I get 0 hours. What am I doing wrong?

gideon
  • 19,329
  • 11
  • 72
  • 113
sam
  • 111
  • 1
  • 3
  • For new readers to this old question I strongly recommend you don’t use `Calendar` and `TimeZone` . Both classes are 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/). May also use other classes from the same API. – Ole V.V. Jan 28 '23 at 12:48
  • Two things are wrong: (1) The `TimeZone` class used when you asked didn’t understand `EDT` and very confusingly tacitly gave you GMT, thus pretending all was well. So your two times zones were the same. (2) Your expectations to the `Calendar` class (also cumbersome and now fortunately long outdated) were wrong. The time-in-millis it returned were independent of time zone. Related: [Java: getTimeZone without returning a default value](https://stackoverflow.com/questions/33373442/java-gettimezone-without-returning-a-default-value). – Ole V.V. Jan 28 '23 at 14:33

4 Answers4

17

getTimeInMillis() returns the number of milliseconds since the epoch in UTC. In other words, the time zone is irrelevant to it.

I suspect you actually want:

long currentTime = System.currentTimeMillis();
int edtOffset = TimeZone.getTimeZone(srcZoneId).getOffset(currentTime);
int gmtOffset = TimeZone.getTimeZone(targetZoneId).getOffset(currentTime);
int hourDifference = (gmtOffset - edtOffset) / (1000 * 60 * 60);
String diff = hourDifference + " hours";

... where srcZoneId and targetZoneId are valid time zone IDs. Note that "EDT" is not a time zone ID; it's sort of "half a time zone" at best. I would strongly recommend avoiding the 3-letter abbreviations, which don't actually identify time zones. For example, using "America/New_York" and "Europe/London" for the above would be fine.

And of course, while the above was a reasonable approach back in 2011, if you're using Java 8 or above it's definitely worth using classes in java.time rather java.util.Calendar, java.util.TimeZone etc.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • And what in case of Daylight Saving? will that handle it? – Gugan Oct 03 '13 at 10:40
  • 1
    @Gugan: I don't know what will happen if you use an abbreviation for the time zone name - but if you give it a full time zone ID such as `Europe/London`, that will indeed handle daylight saving. – Jon Skeet Oct 03 '13 at 10:45
3

java.time

The question and the answer written at that time use the java.util date-time API which was the right thing to do in 2011. In March 2014, the modern Date-Time API was released as part of the Java 8 standard library which supplanted the legacy date-time API and since then it is strongly recommended to switch to java.time, the modern date-time API.

Solution using java.time

Do not use three-letter timezone ID: Before doing it using the modern date-time API, let's see the following important note from the Java 7 Timezone documentation:

Three-letter time zone IDs

For compatibility with JDK 1.1.x, some other three-letter time zone IDs (such as "PST", "CTT", "AST") are also supported. However, their use is deprecated because the same abbreviation is often used for multiple time zones (for example, "CST" could be U.S. "Central Standard Time" and "China Standard Time"), and the Java platform can then only recognize one of them.

The desired solution:

import java.time.Instant;
import java.time.ZoneId;
import java.util.concurrent.TimeUnit;

class Main {
    public static void main(String[] args) {
        Instant now = Instant.now();
        long offest1InSec = ZoneId.of("America/New_York").getRules().getOffset(now).getTotalSeconds();
        long offest2InSec = ZoneId.of("Etc/UTC").getRules().getOffset(now).getTotalSeconds();
        long hours = TimeUnit.HOURS.convert(offest2InSec - offest1InSec, TimeUnit.SECONDS);
        System.out.println(hours);
    }
}

Output now*:

5

ONLINE DEMO

It gets even simpler if the desired difference is from GMT/UTC

The above solution is a general solution for any two time zones. However, if the desired difference is from GMT/UTC, it gets even simpler. In this case, you don't need to calculate the difference because the offset of a time zone is always given w.r.t. UTC whose offset is 00:00 hours.

Demo:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.TimeUnit;

class Main {
    public static void main(String[] args) {
        long hours = Math.abs(TimeUnit.HOURS.convert(
                ZoneId.of("America/New_York").getRules().getOffset(Instant.now()).getTotalSeconds(), TimeUnit.SECONDS));
        System.out.println(hours);
        
        // Alternatively,
        hours = Math.abs(TimeUnit.HOURS.convert(
                ZonedDateTime.now(ZoneId.of("America/New_York")).getOffset().getTotalSeconds(), TimeUnit.SECONDS));
        System.out.println(hours);
    }
}

Output:

5
5

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


* US Eastern Time (e.g. America/New_York) observes DST. During the DST period, its offset from UTC is 4 hours and during the non-DST period (e.g. now, 28-Jan-2023), it's 5 hours. Check this page to learn more about it.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

Jon is close, but due to character restrictions I can't edit his answer. This is the same code but with "EDT" changed to "EST" for Eastern Standard Time.

long currentTime = System.currentTimeMillis();
int edtOffset = TimeZone.getTimeZone("EST").getOffset(currentTime);
int gmtOffset = TimeZone.getTimeZone("GMT").getOffset(currentTime);
int hourDifference = (gmtOffset - edtOffset) / (1000 * 60 * 60);
String diff = hourDifference + " hours";

But this solution makes a major assumption that TimeZone.getAvailableIDs() has within it's string array both "EST" and "GMT". If that method doesn't contain those timezone strings it will come back as 0 offset.

Josh Lehman
  • 278
  • 5
  • 14
1

Here is my code two calculate the time difference between two different timezones. like current timezone(GMT+05:00) and foreign timezone(GMT+05:30).

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                LocalDate today = LocalDate.now();
                Duration timeDifference = Duration.between(today.atStartOfDay(ZoneId.of(foreigntimezone)), today.atStartOfDay(ZoneId.of(currenttimezone)));
                holder.textViewTimeDifference.setText(timeDifference.toString());
            } else {
                long now = System.currentTimeMillis();
                long diffMilliSeconds = TimeZone.getTimeZone(gmtReplaceUTC).getOffset(now) - TimeZone.getTimeZone(currentTimeZone).getOffset(now);
                if (diffMilliSeconds > 0) {
                    String timeDifference = formatTime(diffMilliSeconds);
                    holder.textViewTimeDifference.setText(timeDifference);
                } else {
                    String timeDifference = formatTime(-diffMilliSeconds);
                    holder.textViewTimeDifference.setText("-" + timeDifference);
                }
            }
}

Result: for above 26 API level (PT+30M) and for below (30:00)

Anwar Zahid
  • 227
  • 3
  • 10