简体   繁体   English

Android确定某个设备上的给定日期/时间是否低于DST

[英]Android determine if a given date/time is under DST on a device

Given the current user settings in a device, is it possible to determine if a given date/time will be under daylight savings? 根据设备中的当前用户设置,是否可以确定给定的日期/时间是否会在夏令时下进行? I am not asking if the phone currently has DST in effect. 我不是在问这款手机目前是否有DST有效。 I need to know if a given (future) date, using the current settings in the phone, will be under DST. 我需要知道使用手机中当前设置的给定(未来)日期是否属于夏令时。 I can use Date(), Calendar(), or Joda. 我可以使用Date(),Calendar()或Joda。 I would also be curious as to how the class/method deals with the ambiguities, such as 2:30 AM this Sunday (North America). 我也很好奇这个课程/方法如何处理歧义,例如本周日凌晨2:30(北美)。 That time won't exist because we will spring past it. 那个时间不会存在,因为我们将超越它。 Similarly, in the fall, 1:30 AM will happen twice when we fall back. 同样地,在秋季,当我们退回时,凌晨1:30将发生两次。 The first 1:30 AM is not under DST, but the second one is. 第一个凌晨1:30不在夏令时,但第二个是。

Determining if epoch time is in DST in a given timezone is easy. 确定纪元时间是否在给定时区的DST中很容易。

public static boolean isInDst(TimeZone tz, Date time)
{
    Calendar calendar = Calendar.getInstance(tz);
    calendar.setTime(time);
    // or supply a configured calendar with TZ as argument instead

    return calendar.get(Calendar.DST_OFFSET) != 0;
}

Converting local time to epoch looks easy as far as API goes 就API而言,将本地时间转换为时代看起来很容易

    Calendar calendar = Calendar.getInstance(tz);
    calendar.set(year, month, date, hourOfDay, minute);
    Date epoch = calendar.getTime();

but gets tricky due to ambiguous and gap values. 但由于含糊不清和差距值而变得棘手。

I have written a utility to test for ambiguous and gap values, with aim to use Java only API (no Joda-Time etc), but READ javadoc comment VERY carefully about using this for FUTURE times only. 我编写了一个实用程序来测试模糊和间隙值,目的是使用仅Java的API(没有Joda-Time等),但READ javadoc非常谨慎地评论仅在未来时间使用它。 Also I'd not use it for far future because local authorities keep changing DST rules. 我也不会在将来使用它,因为地方当局不断改变DST规则。 I tested this utility on South Australia (southern hemisphere +9:30/+10:30), New York, and Lord Howe Island (half hour DST! +10:30/+11:00), but time is tricky thing, so use it at your own risk. 我在南澳大利亚(南半球+9:30 / + 10:30),纽约和豪勋爵岛(半小时DST!+10:30 / + 11:00)测试了这个实用程序,但时间很棘手,所以使用它需要您自担风险。

/**
 * Utility method to help handle Day Light Savings transitions. It
 * calculates possible time values the parameters could mean and returns all
 * possible values.
 * 
 * This method should ONLY be used for detecting ambiguous times in the
 * future, and not in the past. This method WILL fail to detect ambiguous
 * times in the past when the time zone would observe DST at the specified
 * time, but no longer does for current and future times. It also can fail
 * to detect ambiguous time in the past if at the specified time the DST
 * offset was different from the latest DST offset.
 * 
 * This method can fail to detect potentially ambiguous times if the
 * calendar uses leap seconds and leap second(s) are added/removed during
 * DST transition.
 * 
 * @param calendar
 *            the calendar to use, must have the correct time zone set. This
 *            calendar will be set, do not rely on the value set in the
 *            calendar after this method returns.
 * @param year
 * @param month
 * @param dayOfMonth
 *            zero based month index as used by {@link Calendar} object.
 * @param hourOfDay
 * @param minute
 * @return Array of {@link Date} objects with each element set to possible
 *         time the parameters could mean or null if the parameters are not
 *         possible, e.g. fall into the missing hour during DST transition,
 *         or complete garbage. One element array means there is only one
 *         non-ambiguous time the parameters can mean, that is, there is no
 *         DST transition at this time. Two element array means the
 *         parameters are ambiguous and could mean one of the two values. At
 *         this time more than two elements can not be returned, but
 *         calendars are strange things and this limit should not be relied
 *         upon.
 * @throws IllegalArgumentException
 *             if setting the specified values to the calendar throws same
 *             exception {@link Calendar#set(int, int, int, int, int)} or if
 *             invalid values are found but are not due to DST transition
 *             gap.
 */
public static Date[] getPossibleTimes(
        Calendar calendar,
        int year, int month, int dayOfMonth, int hourOfDay, int minute)
        throws IllegalArgumentException
{
    calendar.clear();
    try
    {
        // if calendar is set to non-lenient then setting time in the gap
        // due to DST transition will throw exception
        calendar.set(
                year,
                month,
                dayOfMonth,
                hourOfDay,
                minute
                );
    }
    catch (IllegalArgumentException ex)
    {
        if (calendar.isLenient())
        {
            throw ex;
        }
        return null;
    }

    // if validated fields do not match input values
    // this can be when set hour is missing due to DST transition
    // in which case calendar adjusts that hours and returns values
    // different to what was set
    if (
            calendar.get(Calendar.YEAR) != year
            || calendar.get(Calendar.MONTH) != month
            || calendar.get(Calendar.DAY_OF_MONTH) != dayOfMonth
            || calendar.get(Calendar.HOUR_OF_DAY) != hourOfDay
            || calendar.get(Calendar.MINUTE) != minute
            )
    {
        // the values are not possible.
        return null;
    }

    Date value1 = calendar.getTime();

    int dstSavings = calendar.getTimeZone().getDSTSavings();
    if (dstSavings == 0)
    {
        return new Date[] { value1 };
    }

    // subtract DST offset
    calendar.add(Calendar.MILLISECOND, - dstSavings);

    // check if the resulting time fields are same as initial times
    // this could happen due to DST transition, and makes the input time ambiguous
    if (
            calendar.get(Calendar.YEAR) == year
            && calendar.get(Calendar.MONTH) == month
            && calendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth
            && calendar.get(Calendar.HOUR_OF_DAY) == hourOfDay
            && calendar.get(Calendar.MINUTE) == minute
            )
    {
        Date value2 = calendar.getTime();
        return new Date[] { value2, value1, };
    }

    // checking with added DST offset does not seem to be necessary,
    // but time zones are confusing things, checking anyway.

    // reset
    calendar.setTime(value1);

    // add DST offset
    calendar.add(Calendar.MILLISECOND, dstSavings);

    // same check for ambiguous time
    if (
            calendar.get(Calendar.YEAR) == year
            && calendar.get(Calendar.MONTH) == month
            && calendar.get(Calendar.DAY_OF_MONTH) == dayOfMonth
            && calendar.get(Calendar.HOUR_OF_DAY) == hourOfDay
            && calendar.get(Calendar.MINUTE) == minute
            )
    {
        Date value2 = calendar.getTime();
        return new Date[] { value1, value2, };
    }

    return new Date[] { value1, };
}

Just to show some test results, here is test method: 只是为了显示一些测试结果,这里是测试方法:

public static void test2()
{
    TimeZone tz = TimeZone.getTimeZone("America/New_York");

    System.out.format("id=%s\n", tz.getID());
    System.out.format("display=%s\n", tz.getDisplayName());
    System.out.format("raw offset=%f hours\n", tz.getRawOffset() / 1000f / 60f / 60f);
    System.out.format("dstSaving=%f minutes\n", tz.getDSTSavings() / 1000f / 60f);
    System.out.format("observesDST=%b\n", tz.observesDaylightTime());

    System.out.format("\n");

    // Time Tuple class simply holds local time, month is zero based as per Calendar
    TimeTuple [] testTimes = new TimeTuple[]{
            new TimeTuple(2014, 2, 9, 1, 59), // Non-ambiguous standard NY
            new TimeTuple(2014, 2, 9, 2, 00), // GAP NY
            new TimeTuple(2014, 2, 9, 2, 59), // GAP NY
            new TimeTuple(2014, 2, 9, 3, 00), // Non-ambiguous DST NY
            new TimeTuple(2014, 10, 2, 0, 59), // Non-ambiguous DST NY
            new TimeTuple(2014, 10, 2, 1, 00), // Ambiguous DST in NY
            new TimeTuple(2014, 10, 2, 1, 59), // Ambiguous DST in NY
            new TimeTuple(2014, 10, 2, 2, 00), // Non-ambiguous standard NY
    };

    Calendar calendar = GregorianCalendar.getInstance(tz);
    Date[] possibleTimeValues = null;
    for (TimeTuple tt: testTimes)
    {
        possibleTimeValues = getPossibleTimes(
                calendar,
                tt.year, // year
                tt.month, // zero based month
                tt.dayOfMonth, // date
                tt.hourOfDay, // hours
                tt.minute // minutes
                );
        printTimeAmbiguouity(calendar, possibleTimeValues, tt);
    }
}

static DateFormat TZ_FORMAT = new SimpleDateFormat("Z zzzz");

static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S Z zzzz");

public static void printTimeAmbiguouity(Calendar calendar, Date[] possibleTimeValues, TimeTuple tt)
{
    TZ_FORMAT.setTimeZone(calendar.getTimeZone());
    DATE_FORMAT.setTimeZone(calendar.getTimeZone());

    System.out.format("\tinput local time   %s ----- ", tt.toString());

    if (possibleTimeValues == null)
    {
        System.out.format("Impossible/invalid/DST_gap\n");

        calendar.set(tt.year, tt.month, tt.dayOfMonth, tt.hourOfDay, tt.minute);
        Date adjustedTime = calendar.getTime();
        calendar.add(Calendar.HOUR, -6);
        Date limitTime = calendar.getTime();
        Date preTranstionTime = getPreviousTransition(calendar, adjustedTime, limitTime);
        Date postTranstionTime = new Date(preTranstionTime.getTime() + 1);

        System.out.format(
                "\tadjusted           %s\n\ttranstion   from   %s\n\t              to   %s\n",
                DATE_FORMAT.format(adjustedTime),
                DATE_FORMAT.format(preTranstionTime),
                DATE_FORMAT.format(postTranstionTime));
    }
    else if (possibleTimeValues.length == 1)
    {
        System.out.format("NonAmbiguous Valid\n");
        System.out.format("\ttimezone           %s\n", TZ_FORMAT.format(possibleTimeValues[0]));
    }
    else
    {
        System.out.format("Ambiguous\n");
        for (Date time: possibleTimeValues)
        {
            System.out.format("\tpossible value     %s\n", TZ_FORMAT.format(time));
        }
    }
    System.out.format("\n");
}

I'm not including getPreviousTransition() method because I believe that code is even less suitable for production. 我不包括getPreviousTransition()方法,因为我认为代码更不适合生产。

Test Output: 测试输出:

id=America/New_York
display=Eastern Standard Time
raw offset=-5.000000 hours
dstSaving=60.000000 minutes
observesDST=true

input local time   2014-02-09 01:59 ----- NonAmbiguous Valid
timezone           -0500 Eastern Standard Time

input local time   2014-02-09 02:00 ----- Impossible/invalid/DST_gap
adjusted           2014-03-09 03:00:00.0 -0400 Eastern Daylight Time
transtion   from   2014-03-09 01:59:59.999 -0500 Eastern Standard Time
              to   2014-03-09 03:00:00.0 -0400 Eastern Daylight Time

input local time   2014-02-09 02:59 ----- Impossible/invalid/DST_gap
adjusted           2014-03-09 03:59:00.0 -0400 Eastern Daylight Time
transtion   from   2014-03-09 01:59:59.999 -0500 Eastern Standard Time
              to   2014-03-09 03:00:00.0 -0400 Eastern Daylight Time

input local time   2014-02-09 03:00 ----- NonAmbiguous Valid
timezone           -0400 Eastern Daylight Time

input local time   2014-10-02 00:59 ----- NonAmbiguous Valid
timezone           -0400 Eastern Daylight Time

input local time   2014-10-02 01:00 ----- Ambiguous
possible value     -0400 Eastern Daylight Time
possible value     -0500 Eastern Standard Time

input local time   2014-10-02 01:59 ----- Ambiguous
possible value     -0400 Eastern Daylight Time
possible value     -0500 Eastern Standard Time

input local time   2014-10-02 02:00 ----- NonAmbiguous Valid
timezone           -0500 Eastern Standard Time

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 确定指定日期的Java夏令时(DST)是否处于活动状态 - Determine Whether Daylight Savings Time (DST) is Active in Java for a Specified Date Java日期DST调整我的日期/时间 - Java date DST adjusts my date / time 使用Joda,如何确定给定日期是否在时间x和比x早3小时的时间之间? - Using Joda, How do I determine if a given date is between time x and a time 3 hours earlier than x? Java中可以使用什么日期时间格式来处理夏令时 - What Date time format can be used to handle DST in Java 在格式化日期时忽略DST(Dayligt节省时间) - Ignore DST (Dayligt Saving Time) while formating date 将日期和时间存储到 Firebase 在 Android 工作室的用户子下? - Store a date&time into Firebase under a User child in Android studio? 以编程方式确定 Android 设备性能 - Determine Android device performance programmatically 如何获取Android设备打开时间的日期? - How can i get a date of android device opening time? Glassfish记录日期和DST? - Glassfish Logs Date and DST? 在Android系统中,对于日期和时间的每一次变化,有没有办法知道用户是否提前或延迟了设备时间或日期? - In Android system, for every change in date and time, is there a way to know if the user has advanced or delayed the device time or date?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM