1

I want to get a difference in hours between a current time in a specific timezone and UTC time. I tried this:

 LocalTime time = LocalTime.now();
 System.out.println(time); //21:05:42:764

 LocalTime utcTime = LocalTime.now(ZoneId.of("UTC"));
 System.out.println(utcTime);  //18:05:42:769

 System.out.println(Duration.between(utcTime, time).getSeconds()/3600);  //2
 System.out.println(Duration.between(time, utcTime).getSeconds()/3600);  //-3

Why is the difference between the last two results and are there better ways to do it? I need to get the number 3.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
parsecer
  • 4,758
  • 13
  • 71
  • 140
  • 2
    So you're trying to compute the time zone offset? – Ben P. Jun 13 '19 at 18:25
  • @Ben P. 10 I'm trying to compute from which UTC+X timezone a given LocalTime comes from. So the number of hours offset I suppose? – parsecer Jun 13 '19 at 18:26
  • Is the given `LocalTime` guaranteed to represent "now"? Right now it is 2:28pm eastern time... how will you differentiate between a LocalTime I record at 2:28pm and a LocalTime that someone in Chicago records one hour from now? They will both just tell you "2:28pm". – Ben P. Jun 13 '19 at 18:28
  • @Ben P. My program would be given hours, minutes and seconds I'll convert to LocalTime. That time would represent someone's local time *in that moment*. Then I'll compare it to UTC time *in that moment*. There would be delay, but milliseconds, seconds at most. – parsecer Jun 13 '19 at 18:30
  • Are you using the java.time library, the JSR-310 backport, or Joda time? – Ben P. Jun 13 '19 at 18:33
  • @Ben P. I'm using classes from `java.time` package – parsecer Jun 13 '19 at 18:35

2 Answers2

4

Duration is the wrong class. There is zero duration between "now" in one time zone and "now" in another. For a fun but memorable way to think about this, see here.

You appear to be seeking to know the current offset from UTC for a given time zone. You can use the ZonedDateTime class for that:

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
ZoneOffset offset = zdt.getOffset();
int offsetMinutes = offset.getTotalSeconds() / 60;
double offsetHours = ((double) offsetMinutes) / 60;
System.out.println(offsetHours);  // 5.5

You could also just use ZonedDateTime.now() on the first line, if you want to use the computer's current time zone.

With regard to LocalTime - that is just the time portion (hours, minutes, seconds, and smaller). Since there is no date associated, you can't necessarily determine which time zone offset it belongs to. There is more than one date that "today" going on at any given moment. Time zone offsets range from UTC-12 to UTC+14, so there are indeed values where the same time of day is happening on two different dates somewhere on the planet.

As an example, 08:00:00 in Hawaii (Pacific/Honolulu) on 2019-01-01 is also 08:00:00 in Kiribati (Pacific/Kiritimati), but on 2019-01-02 - the following date! (Reference here.) Thus, if you had a LocalTime object with 08:00:00 and it was 08:00:00 in one of those two zones, you'd not be able to tell which one it was, or what the corresponding UTC offset should be.

Also, keep in mind that time zone offsets are not limited to whole hours. There are present-day time zones with half-hour and 45-minute offset. Historically, there have been others.

Lastly, keep in mind that an offset is not necessarily enough to identify a time zone. Many time zones share offsets at some points in time, but differ in others. See "Time Zone != Offset" in the timezone tag wiki.

Oh, and about your results getting 2 in one direction and -3 in the other - this is a rounding error due to your integer division. If you print out the seconds value, you'll notice they are one second apart (10799, vs -10800). Dig closer and you'll find that "now" included fractional seconds that were truncated with the getSeconds call. (You called .now() twice, so they were at slightly different times.)

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • The OP is using `LocalTime`, which doesn't have time zones, and the `now(ZoneId)` will produce a `LocalTime` with the current time in that zone. So `LocalTime.now(ZoneId.of("UTC"))` will produce a different value than `LocalTime.now()` (unless your JVM default time zone happens to currently have the same offset as UTC). – Mark Rotteveel Jun 13 '19 at 18:46
  • Indeed. I'm suggesting they *don't* use `LocalTime` or take a difference of duration, but rather use `ZonedDateTime`. – Matt Johnson-Pint Jun 13 '19 at 18:47
  • But they **do** produce different values. Otherwise I don't think I understand your answer. – Mark Rotteveel Jun 13 '19 at 18:48
  • Yes, calling "now" twice will return two different nows, and calling for the UTC time and the local time will only be close if the local time zone is UTC (or is zero offset from UTC). We are saying the same things. – Matt Johnson-Pint Jun 13 '19 at 18:49
4

Why is the difference between the last two results

The reason that you're getting different results for the two computed durations is a combination of the fact that there is some tiny amount of time elapsed between the two recordings and the fact that the duration start time is included in the range but the duration end time is not.

Consider these times instead: 6:00:00:001 vs 8:00:00:000. Here it is very obvious that we're only exactly one millisecond off of two hours, but when we think about seconds we're either going to get 7199 or -7200. When we then do integer math (i.e. divide by 3600), we're going to get 1 or -2.

If it weren't for the one extra millisecond on the first timestamp, the absolute value of the two would be identical.

Ben P.
  • 52,661
  • 6
  • 95
  • 123