reddragon72
reddragon72

Reputation: 191

trying to get localtimezone compared to a set timezone

I am trying to get the difference between the machine local time to the east coast of the US, though that is non specific.

I am using the following but it always returns the difference from UTC and not the timezone set by the PC.

        LocalDateTime now = LocalDateTime.now();
        ZoneId zone = ZoneId.of("America/New_York");
        ZoneOffset zoneOffSet = zone.getRules().getOffset(now);
        System.out.println(zoneOffSet + " " + ZonedDateTime.now() + " " + Instant.now());

No matter which time zone I set my PC time to it always shows -04:00 for zoneOffSet.

What am I doing wrong here?

Upvotes: 0

Views: 284

Answers (4)

Anonymous
Anonymous

Reputation: 86379

You need a conversion, not the difference

The reason I need this is I am making an app that requires saved appointments based on New York time zone. No matter where you are in the world your local timezone will lock to the appointment time of 8A.M. to 5P.M. in NewYork. This allows the clients to see what time it will be in their area when matching the NY TZ.

In Java it’s easier to convert a time between the user’s time zone and America/New_York time zone than it is to calculate the difference. Why try something complicated when what you need is simple?

I am assuming the following constants:

static final ZoneId SERVER_ZONE = ZoneId.of("America/New_York");
static final LocalTime OPENS_SERVER_TIME = LocalTime.of(8, 0);
static final LocalTime CLOSES_SERVER_TIME = LocalTime.of(17, 0); // 5 PM

To convert a time from New York time to the user’s time zone we need a date. The UTC offset for New York changes between standard and summer time (DST), and the same could be true for the user’s time zone (and if it is, the changeover dates need not coincide).

    LocalDate date = LocalDate.of(2021, Month.OCTOBER, 20);

    ZoneId userZone = ZoneId.systemDefault();
    ZonedDateTime openInUserZone = date.atTime(OPENS_SERVER_TIME)
            .atZone(SERVER_ZONE)
            .withZoneSameInstant(userZone);
    
    System.out.println(openInUserZone);

I ran this in Asia/Dushanbe time zone and got this output:

2021-10-20T17:00+05:00[Asia/Dushanbe]

17:00 at UTC offset +05:00 agrees with 8 AM at offset -04:00, the offset used in New York in October. For the opposite conversion just switch the two time zones in the code that converts.

Upvotes: 0

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79580

Although you can get the timezone offset using ZoneId#getRules#getOffset(LocalDateTime), for simplicity, I prefer using the current ZoneId#getRules#getOffset(Instant) to get this value.

Demo:

import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;

public class Main {
    public static void main(String args[]) {
        // Tests
        ZoneId systemZoneId = ZoneId.systemDefault();
        ZoneId zoneIdNy = ZoneId.of("America/New_York");
        ZoneId zoneIdIndia = ZoneId.of("Asia/Kolkata");

        System.out.printf("Difference between %s and %s: %s%n", systemZoneId.getId(), zoneIdNy.getId(),
                timezoneOffsetDiffBewteen(systemZoneId, zoneIdNy));

        System.out.printf("Difference between %s and %s: %s%n", systemZoneId.getId(), zoneIdIndia.getId(),
                timezoneOffsetDiffBewteen(systemZoneId, zoneIdIndia));
    }

    static String timezoneOffsetDiffBewteen(ZoneId source, ZoneId target) {
        Instant instant = Instant.now();
        long offsetSecondsSource = source.getRules().getOffset(instant).getTotalSeconds();
        long offsetSecondsTarget = target.getRules().getOffset(instant).getTotalSeconds();
        long diff = offsetSecondsTarget - offsetSecondsSource;
        Duration duration = Duration.ofSeconds(diff);
        return String.format("%s%02d:%02d", diff < 0 ? "-" : "+", Math.abs(duration.toHoursPart()),
                duration.toMinutesPart());
    }
}

Output:

Difference between Europe/London and America/New_York: -05:00
Difference between Europe/London and Asia/Kolkata: +04:30

ONLINE DEMO

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


* 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. Note that Android 8.0 Oreo already provides support for java.time.

Upvotes: 0

reddragon72
reddragon72

Reputation: 191

I took what I had posted and added a bit to it to get my local time zone and subtract from one another to get the difference in time zone from JVM(My PC TimeZone) from New Yorks to get the timezone difference.

I then took your code and did the same for the same output but it matches yours so easier for others to read.

    ZoneId z = ZoneId.of( "America/New_York" ) ;
    ZoneRules rules = z.getRules() ;
    ZoneOffset offset = rules.getOffset( now ) ;
    int offsetInSeconds_NewYork = offset.getTotalSeconds() ;
    int offsetInSeconds_MyDefaultZone = 
    ZoneId.systemDefault().getRules().getOffset( now ).getTotalSeconds() ;
    System.out.println(offsetInSeconds_MyDefaultZone / 60 /60);
    System.out.println((offsetInSeconds_NewYork / 60 /60) - (offsetInSeconds_MyDefaultZone / 60 /60));

The reason I need this is I am making an app that requires saved appointments based on New York time zone. No matter where you are in the world your local timezone will lock to the appointment time of 8A.M. to 5P.M. in NewYork. This allows the clients to see what time it will be in their area when matching the NY TZ.

Again thanks for this as I was in the mindset that the code I posted in the OP would decern the difference(.offset) of my PC(JVM) TZ to that of New York. Grabbing both and subtracting seemed to be the only real way. Shame that is not a feature in Java. But you can see my confusion in rules.getOffset( now ) as now can change to any location but it never adjusts the offset to anything but UTC. Very strange.

Thanks for push to settle my head.

Upvotes: 0

Basil Bourque
Basil Bourque

Reputation: 340118

LocalDateTime is not a moment

General tip: Never call LocalDateTime.now(). I cannot imagine a scenario where that is the right thing to do.

The LocalDateTime class purposely lacks any concept of time zone or offset-from-UTC. So LocalDateTime cannot represent a moment, cannot track a specific point on the timeline.

Determine offset in use by a time zone at a particular moment

I cannot discern from your prose exactly what is your goal. But looking at your code, it seems you want to know what is the current offset-from-UTC in effect for a particular time zone. That is quite easy.

A ZoneId represents a particular time zone. Each time zone has a ZoneRules object containing the history of the past, present, and future changes to the offset used by the people in that region.

Pass a moment (an Instant) to those rules to ask for the offset in effect at that time. The result is a ZoneOffset, a number of hours-minutes-seconds.

ZoneId z = ZoneId.of( "America/New_York" ) ;
ZoneRules rules = z.getRules() ;
Instant now = Instant.now() ;  // Capture the current moment as seen in UTC (an offset of zero hours-minutes-seconds). 
ZoneOffset offset = rules.getOffset( now ) ;

You can generate text in standard ISO 8601 format to represent that offset value by calling ZoneOffset#toString.

ZonedDateTime zdt = now.atZone( z ) ;
String message = zdt.toString() + " has an offset of " + offset + "." ;
System.out.println( message ) ;

See this code run live at IdeOne.com.

2021-10-09T00:36:54.432057-04:00[America/New_York] has an offset of -04:00.

You said:

No matter what I set my PC time to it always shows -04:00 for zoneOffSet.

Of course. No matter where you are in the world, the people in the New York region set their clocks four hours behind UTC. The default time zone used on your computer has no impact on that fact.

If you want to compare the offset of your particular time zone against the offset of another time zone, run the same code as seen above to get a second ZoneOffset for the same Instant. To access your JVM’s current default time zone, call ZoneId.systemDefault.

You could compare them by converting each offset to a total number of seconds (positive or negative). Call ZoneOffset#getTotalSeconds.

int offsetInSeconds_NewYork = offset.getTotalSeconds() ;
int offsetInSeconds_MyDefaultZone = ZoneId.systemDefault().getRules().getOffset( now ).getTotalSeconds() ;

Upvotes: 1

Related Questions