Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android time zone and DST issue

I have an application which uses a schedule. The user picks what time the schedule should start/end, and I display that start/end time back to the user. Problem is, the time that is displayed back is off, since the DST change.

I can fix the issue for my time zone (Eastern time) or I can fix the issue for GMT, and if I setup a specific case for GMT, Alaskan time is still wrong. Any suggestions?

here's my code:

the time that is being displayed:

long startTimeMillis = (startHour * 1000 * 60 * 60) + (startMin * 1000 * 60) - getTimeOffset();

getTimeOffset:

TimeZone tz = TimeZone.getDefault();

//deal with GMT weirdness
if (tz.getRawOffset() == 0)
    return tz.getRawOffset() + tz.getDSTSavings();
else
    return tz.getRawOffset();

I would think that I need something like:

else if(tz.inDaylightTime(new Date()))
    return tz.getRawOffset() + tz.getDSTSavings();

But if I do that, then Eastern time shows 1 hour less than it should, and Alaskan time shows 2 hours less. If I do the opposite: (- instead of +)

else if(tz.inDaylightTime(new Date()))
    return tz.getRawOffset() - tz.getDSTSavings();

Then Eastern time is 1 hour more than it should be, but Alaskan time is correct.

ADDENDUM:

I've also tried using tz.getOffset(new Date().getTime()) in each of those situations instead of tz.getRawOffset(). This was actually the first thing that I tried, because according to Google's documentation, this function is supposed to handle DST for you.

END ADDENDUM

I've also tried using Calendars, like this:

Calendar calendar = GregorianCalendar.getInstance();

return calendar.get(Calendar.ZONE_OFFSET);

This gives the correct time for EST time, but 1 hour ahead for GMT and 1 hour behind for Alaska. And I've tried this:

if(tz.inDaylightTime(new Date()))
    return calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET);
else
    return calendar.get(Calendar.ZONE_OFFSET);

But this leaves EDT 1 hour short.

I've also tried this:

return tz.getOffset(calendar.get(Calendar.ERA),
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH),
            calendar.get(Calendar.DAY_OF_WEEK),
            calendar.get(Calendar.MILLISECOND));

which leaves EDT 1 hour short as well.

And I get the same results if I use

GregorianCalendar calendar = new GregorianCalendar(tz);

instead of

Calendar calendar = GregorianCalendar.getInstance();

How do I do this correctly???

like image 805
Randy Avatar asked Mar 16 '13 15:03

Randy


People also ask

Does Google Calendar adjust for daylight Savings?

Google Calendar uses Coordinated Universal Time (UTC) to help avoid issues with daylight saving time. When events are created, they're converted into UTC, but you'll always see them in your local time. If an area switches their time zone, events created before we knew about the change might be in the wrong time zone.

How DST affects time zones?

The simplest way to remember "time zone math" for the states that recognize Daylight saving time is three-two-one: three hours difference from Eastern to Pacific, two hours difference from Eastern to Mountain, and one hour difference from Eastern to Central.


2 Answers

OK, I finally figured out how to do it properly. This is how I made it work:

long startTimeMillis = (startHour * 1000 * 60 * 60) + (startMinute * 1000 * 60);
startTimeMillis  -= getTimeOffset(startTimeMillis);

getTimeOffset():

public static int getTimeOffset(long time)
{
    TimeZone tz = TimeZone.getDefault();

    return tz.getOffset(time);
}
like image 166
Randy Avatar answered Sep 27 '22 18:09

Randy


Sounds like you're setting the emulator timezone, which you are retrieving via tz.getDefault(). Calendars will handle daylight savings times internally - IF they are set to a TimeZone that observes daylight savings at all. Some of the TimeZones, such as "America/Phoenix" and "MDT" don't observe savings time, while "America/Denver" does.

The Alaska Time Zone observes standard time by subtracting nine hours from Coordinated Universal Time (UTC−9). During daylight saving time its time offset is eight hours (UTC−8). https://en.wikipedia.org/wiki/Alaska_Time_Zone

You can use TimeZone.getAvailableIDs() to get a list of all the TimeZone names/keys. Check out the differences between America/Anchorage, Etc/GMT+9, America/Alaska.

EDIT: updated code Here's some example code you a couple ways you can play around with dates and DST:

package com.codeshane.examples;

import java.text.DateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class Times {
public static final String[] yourZoneKeys =
  {"America/Anchorage", "America/Juneau", "America/Nome", "America/Yakutat",
   "Etc/GMT+9", "Pacific/Gambier", "SystemV/YST9", "SystemV/YST9YDT","US/Alaska"};

public static void main(String[] args){
    for (String timezoneKey: yourZoneKeys){
        TimeZone tz = getSelectedTimezone(timezoneKey);
        displayTimezoneInfo(tz);
        showTimeInNewCalendar(tz);
    }       
}

static void displayTimezoneInfo(TimeZone tz){
    System.out.println(
      "\n> TZ ID:" + tz.getID() +
      "  Display:" + tz.getDisplayName() +
      "  savings:" + tz.getDSTSavings() +
      "   offset:" + tz.getRawOffset() +
      ""
    );
}

static void showTimeInNewCalendar(TimeZone tz) {
    DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);

    /***** DST US 2013 = Mar 10 - Nov 3
     * Daylight Saving Time (United States) 2013
     * began at 2:00 AM on Sunday, March 10
     *  ends at 2:00 AM on Sunday, November 3
     **/

    //The Calendar method output the same results, but since both
    //methods went through the DateFormat, I went ahead and just
    //used the DateFormat's internal date via getCalendar().set(..)
    //but left the Calendar code (commented out) for you to compare.

    //Calendar gc = Calendar.getInstance();
    //gc.setTimeZone(tz);
    //gc.set(2013, 2, 10, 1, 30, 0);

    // Setting the Calendar's date and time
    df.getCalendar().set(2013, 2, 10, 1, 30, 0);

    // Display some times at 30 minute intervals
    for (int i =0;i<3;i++){

        df.setTimeZone(tz);
        System.out.println(
          "\n " + tz.getID() +
          " " + df.format(df.getCalendar().getTime()) +// sdf. .format(myDateFormat, gc) +
          //" gc date:" + df.format(gc.getTime()) +
          "  " + tz.inDaylightTime(df.getCalendar().getTime()) +
          ""
        );
        df.getCalendar().add(Calendar.MINUTE, 30);
        //gc.add(Calendar.MINUTE, 30);
    }
}

private static TimeZone getSelectedTimezone(String timezoneKey){
    if (null==timezoneKey) return TimeZone.getDefault(); // system/network-provided
    return TimeZone.getTimeZone(timezoneKey); // Gets specified tz, or "GMT" if tzKey is invalid
}

}

EDIT: added output You'll notice some of the timezones spring forward when 30 minutes is added at the "Spring forward" time we just observed on 3/10/2013, while others don't. If you select these timezones that observe DST, you'll get the change; whereas the others won't.

TZ ID:America/Anchorage Display:Alaska Standard Time savings:3600000 offset:-32400000

America/Anchorage Sunday, March 10, 2013 1:30:00 AM AKST false

America/Anchorage Sunday, March 10, 2013 3:00:00 AM AKDT true

America/Anchorage Sunday, March 10, 2013 3:30:00 AM AKDT true

TZ ID:America/Juneau Display:Alaska Standard Time savings:3600000 offset:-32400000

America/Juneau Sunday, March 10, 2013 1:30:00 AM AKST false

America/Juneau Sunday, March 10, 2013 3:00:00 AM AKDT true

America/Juneau Sunday, March 10, 2013 3:30:00 AM AKDT true

TZ ID:America/Nome Display:Alaska Standard Time savings:3600000 offset:-32400000

America/Nome Sunday, March 10, 2013 1:30:00 AM AKST false

America/Nome Sunday, March 10, 2013 3:00:00 AM AKDT true

America/Nome Sunday, March 10, 2013 3:30:00 AM AKDT true

TZ ID:America/Yakutat Display:Alaska Standard Time savings:3600000 offset:-32400000

America/Yakutat Sunday, March 10, 2013 1:30:00 AM AKST false

America/Yakutat Sunday, March 10, 2013 3:00:00 AM AKDT true

America/Yakutat Sunday, March 10, 2013 3:30:00 AM AKDT true

TZ ID:Etc/GMT+9 Display:GMT-09:00 savings:0 offset:-32400000

Etc/GMT+9 Sunday, March 10, 2013 1:30:00 AM GMT-09:00 false

Etc/GMT+9 Sunday, March 10, 2013 2:00:00 AM GMT-09:00 false

Etc/GMT+9 Sunday, March 10, 2013 2:30:00 AM GMT-09:00 false

TZ ID:Pacific/Gambier Display:Gambier Time savings:0 offset:-32400000

Pacific/Gambier Sunday, March 10, 2013 1:30:00 AM GAMT false

Pacific/Gambier Sunday, March 10, 2013 2:00:00 AM GAMT false

Pacific/Gambier Sunday, March 10, 2013 2:30:00 AM GAMT false

TZ ID:SystemV/YST9 Display:Alaska Standard Time savings:0 offset:-32400000

SystemV/YST9 Sunday, March 10, 2013 1:30:00 AM AKST false

SystemV/YST9 Sunday, March 10, 2013 2:00:00 AM AKST false

SystemV/YST9 Sunday, March 10, 2013 2:30:00 AM AKST false

TZ ID:SystemV/YST9YDT Display:Alaska Standard Time savings:3600000 offset:-32400000

SystemV/YST9YDT Sunday, March 10, 2013 1:30:00 AM AKST false

SystemV/YST9YDT Sunday, March 10, 2013 2:00:00 AM AKST false

SystemV/YST9YDT Sunday, March 10, 2013 2:30:00 AM AKST false

TZ ID:US/Alaska Display:Alaska Standard Time savings:3600000 offset:-32400000

US/Alaska Sunday, March 10, 2013 1:30:00 AM AKST false

US/Alaska Sunday, March 10, 2013 3:00:00 AM AKDT true

US/Alaska Sunday, March 10, 2013 3:30:00 AM AKDT true

I also recommend http://www.worldtimeserver.com as it will let you compare your results with what they should be.

like image 35
CodeShane Avatar answered Sep 27 '22 19:09

CodeShane