简体   繁体   中英

How can I normalize timezones when comparing two Java Calendars?

I'm furious with the 7th of November.

I've written a method to calculate the number of days between two Java Date objects (before it gets mentioned, JodaTime is not an option) -- the method works the majority of the time, but when the start xor end date occurs during daylight savings time, the output is off by a day.

Do Calendars have some way of overriding the timezone of dates? I don't care what timezone the dates are actually in, but they need to be the same one!

Code below:

public int getDayRange() {

        //startDate = "Sat Nov 06 00:00:00 EDT 2010";
        //endDate = "Sun Nov 07 23:59:59 EST 2010";

    TimeZone tz = TimeZone.getTimeZone("UTC");

    GregorianCalendar cal1 = new GregorianCalendar(tz); 
    GregorianCalendar cal2 = new GregorianCalendar(tz); 

    cal1.setTime(startDate);
    cal2.setTime(endDate);

    long ms1 = cal1.getTime().getTime(); 
    long ms2 = cal2.getTime().getTime(); 
    long difMs = ms2-ms1; 
    long msPerDay = 1000*60*60*24; 

    double days = difMs / msPerDay;

    return (int) Math.floor(days)+1;
        //returns 3(!!!) days (wrong)
}

Never try to count days using 1000*60*60*24 as a day. It's just plain wrong. If you really need to implement the calculation yourself for some reason, use the YEAR and DAY_OF_YEAR fields on the Calendar to count differences in actual days.

You cannot simply "override" the timezone of a java.util.Date because it has no timezone information associated with it. You need to know what timezone it was intended to be interpreted in, and use that timezone when converting it to a human representation. Arbitrarily using UTC to interpret it will, as you have discovered, not deliver consistent results!

  public int getDayRange() {

        //startDate = "Sat Nov 06 00:00:00 EDT 2010";
        //endDate = "Sun Nov 07 23:59:59 EST 2010";

    TimeZone tz1 = TimeZone.getTimeZone("EDT");
    TimeZone tz2 = TimeZone.getTimeZone("EST");

    GregorianCalendar cal1 = new GregorianCalendar(tz1); 
    GregorianCalendar cal2 = new GregorianCalendar(tz2); 

    cal1.setTime(startDate);
    cal2.setTime(endDate);

    if (cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) {
        return cal2.get(Calendar.DAY_OF_YEAR) - cal1.get(Calendar.DAY_OF_YEAR) + 1;
    } else {
        //this gets complicated, but you can see what to do, plenty of examples online
    }

  }

Your system should store all timestamps in UTC!
If not you will never get that to work!
Then the difference calculation is simple.

If you need to display localTime too, you have to store the TimeZone together with the date as a pair: Example: (long timeUtc, int timezoneOffSetToUtc). The timezoneOffSetToUtc is the offset valid at the creation date of the pair.

The TimeZone should only be instantiated from String like ("Austria / Vienna").

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM