-1

我在 Java 中使用 SimpleTimeZone 类时遇到问题。首先,JavaDoc 很好,但在开始和结束规则方面不太容易理解。但是在网上找到的一些例子的帮助下,我设法做对了(我仍然不明白为什么 8 代表 day_of_month 一个月的第二周!!!但无论如何)

现在我编写了一个简单的 Junit 测试来验证我的理解:

package test;

import static org.junit.Assert.assertEquals;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.SimpleTimeZone;

import org.apache.log4j.Logger;
import org.junit.Test;



public class SimpleTimeZoneTest {

Logger log = Logger.getLogger(SimpleTimeZoneTest.class);

@Test   
public void testTimeZoneWithDST() throws Exception {


    Calendar testDateEndOut = new GregorianCalendar(2012, Calendar.NOVEMBER, 4, 01, 59, 59);
    Calendar testDateEndIn = new GregorianCalendar(2012, Calendar.NOVEMBER, 4, 02, 00, 00);
    Calendar testDateStartOut = new GregorianCalendar(2012, Calendar.MARCH, 11, 01, 59, 59);
    Calendar testDateStartIn = new GregorianCalendar(2012, Calendar.MARCH, 11, 02, 00, 00);

    SimpleTimeZone est = new SimpleTimeZone(-5 * 60 * 60 * 1000, "EST");
    est.setStartRule(Calendar.MARCH, 8, -Calendar.SUNDAY, 2 * 60 * 60 * 1000);
    est.setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);

    Calendar theCal = new GregorianCalendar(est);

    theCal.setTimeInMillis(testDateEndOut.getTimeInMillis());

    log.info(" Cal date = " + new Timestamp(theCal.getTimeInMillis()) + " : " + theCal.getTimeZone().getDisplayName());
    log.info(" Cal use DST = " + theCal.getTimeZone().useDaylightTime());
    log.info(" Cal In DST = " + theCal.getTimeZone().inDaylightTime(theCal.getTime()));
    log.info("offset = " + theCal.getTimeZone().getOffset(theCal.getTimeInMillis()));
    log.info("DTS offset= " + theCal.getTimeZone().getDSTSavings());
    assertEquals("End date Should be In DST", true, theCal.getTimeZone().inDaylightTime(theCal.getTime()));

    theCal.setTimeInMillis(testDateEndIn.getTimeInMillis());

    log.info(" Cal date = " + new Timestamp(theCal.getTimeInMillis()) + " : " + theCal.getTimeZone().getDisplayName());
    log.info(" Cal use DST = " + theCal.getTimeZone().useDaylightTime());
    log.info(" Cal In DST = " + theCal.getTimeZone().inDaylightTime(theCal.getTime()));
    log.info("offset = " + theCal.getTimeZone().getOffset(theCal.getTimeInMillis()));
    log.info("DTS offset= " + theCal.getTimeZone().getDSTSavings());
    assertEquals("End date Should be Out DST", false, theCal.getTimeZone().inDaylightTime(theCal.getTime()));

    theCal.setTimeInMillis(testDateStartIn.getTimeInMillis());

    log.info(" Cal date = " + new Timestamp(theCal.getTimeInMillis()) + " : " + theCal.getTimeZone().getDisplayName());
    log.info(" Cal use DST = " + theCal.getTimeZone().useDaylightTime());
    log.info(" Cal In DST = " + theCal.getTimeZone().inDaylightTime(theCal.getTime()));
    log.info("offset = " + theCal.getTimeZone().getOffset(theCal.getTimeInMillis()));
    log.info("DTS offset= " + theCal.getTimeZone().getDSTSavings());
    assertEquals("Start date Should be in DST", true, theCal.getTimeZone().inDaylightTime(theCal.getTime()));

    theCal.setTimeInMillis(testDateStartOut.getTimeInMillis());

    log.info(" Cal date = " + new Timestamp(theCal.getTimeInMillis()) + " : " + theCal.getTimeZone().getDisplayName());
    log.info(" Cal use DST = " + theCal.getTimeZone().useDaylightTime());
    log.info(" Cal In DST = " + theCal.getTimeZone().inDaylightTime(theCal.getTime()));
    log.info("offset = " + theCal.getTimeZone().getOffset(theCal.getTimeInMillis()));
    log.info("DTS offset= " + theCal.getTimeZone().getDSTSavings());
    assertEquals("Start date Should be Out DST", false, theCal.getTimeZone().inDaylightTime(theCal.getTime()));





}

}

好的,我想测试日期限制,看看 inDaylightTime 是否返回正确的东西!

所以,我的规则是:

DST 开始于 3 月的第二个星期日凌晨 2 点 DST 结束于 11 月的第一个星期日凌晨 2 点

在 2012 年(现在)这给了我们 3 月 11 日凌晨 2 点和 11 月 4 日凌晨 2 点

你可以看到我的考试日期设置正确!!!

那么这是我的测试运行的输出:

2012-11-01 18:22:44,344 INFO [test.SimpleTimeZoneTest] - < Cal date = 2012-11-04 01:59:59.0 : Eastern Standard Time>
2012-11-01 18:22:44,345 INFO [test.SimpleTimeZoneTest] - < Cal use DST = true>
2012-11-01 18:22:44,345 INFO [test.SimpleTimeZoneTest] - < Cal In DST = false>
2012-11-01 18:22:44,345 INFO [test.SimpleTimeZoneTest] - <offset = -18000000>
2012-11-01 18:22:44,345 INFO [test.SimpleTimeZoneTest] - <DTS offset= 3600000>

我的第一个断言失败了,并告诉我 2012-11-04 01:59:59 不是 inDST ... !!!!!!???

如果我输入 2012-11-04 00:59:59,则测试通过!

这 1 小时的间隔让我很困惑……谁能解释这种行为?

哦,顺便说一句,如果有人可以详细说明:

est.setStartRule(Calendar.MARCH, 8, -Calendar.SUNDAY, 2 * 60 * 60 * 1000);

为什么 8 表示三月的第二周......以及 -SUNDAY。我无法在一个真实的日历示例中弄清楚这件事!!!

谢谢

4

2 回答 2

2

我的第一个断言失败了,并告诉我 2012-11-04 01:59:59 不是 inDST ... !!!!!!???

在 11 月 4 日凌晨 2:00,我们“退回”到凌晨 1:00,因此时间 1:59:59 发生了两次,一次在 DST 之内,一次在 DST 之后;所以,这个声明:

Calendar testDateEndOut =
    new GregorianCalendar(2012, Calendar.NOVEMBER, 4, 01, 59, 59);

真是模棱两可。碰巧的是,JDK 决定它指的是 1:59:59 的第二次出现,即 DST 之外的那个,这就是您看到该结果的原因。

编辑添加:您可以消除这种歧义,并确保您获得第一次出现,方法是最初设置testDateEndOut为 00:00:00,然后写入testDateEndOut.setTimeInMillis (testDateEndOut.getTimeInMillis() + 7199000L)以将其向前移动所需的数量。

哦,顺便说一句,如果有人可以详细说明:

est.setStartRule(Calendar.MARCH, 8, -Calendar.SUNDAY, 2 * 60 * 60 * 1000);

为什么 8 表示三月的第二周......以及 -SUNDAY。

java.util.SimpleTimeZone支持很多不同的情况,所以它使用了一些魔法。您正在使用的四参数版本setStartRule,因此您的参数如下:

  • startMonth- 你已经明白了。MARCH意思是三月。
  • startDay- 注意:一天,而不是一周。8指本月的第八天。
  • startDayOfWeek- 您使用负值表示您想要在上述指定日期或之后的一天。-SUNDAY表示周日或之后。
  • startTime- 你明白这一点。

总而言之,(Calendar.MARCH, 8, -Calendar.SUNDAY, 2 * 60 * 60 * 1000)表示 3 月 8 日或之后的第一个星期日的凌晨 2:00(即 [3 月 8 日,3 月 14 日] 范围内的星期日),即 3 月的第二个星期日。

于 2012-11-01T22:59:05.047 回答
1

我没有检查其他所有内容,但最直接的第一个问题是您在默认Calendar时区创建所有值......这意味着当您编写时:

theCal.setTimeInMillis(testDateEndOut.getTimeInMillis());

...这已经应用了您的系统默认时区。

我强烈建议您完全摆脱theCal,并将您的代码更改为:

SimpleTimeZone est = new SimpleTimeZone(-5 * 60 * 60 * 1000, "EST");
est.setStartRule(Calendar.MARCH, 8, -Calendar.SUNDAY, 2 * 60 * 60 * 1000);
est.setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);

Calendar testDateEndOut = new GregorianCalendar(est);
testDateEndOut.set(2012, Calendar.NOVEMBER, 4, 01, 59, 59);
Calendar testDateEndIn = new GregorianCalendar(est);
testDateEndIn.set(2012, Calendar.NOVEMBER, 4, 02, 00, 00);
Calendar testDateStartOut = new GregorianCalendar(est);
testDateStartOut.set(2012, Calendar.MARCH, 11, 01, 59, 59);
Calendar testDateStartIn = new GregorianCalendar(est);
testDateStartIn.set(2012, Calendar.MARCH, 11, 02, 00, 00);

这可能无法解决问题,但至少可以更容易地推理它们。

或者,您可以使用 UTC 日历并使用 DST 转换前后的UTC时刻来测试您的时区。

除此之外,我强烈建议您使用Joda Time而不是java.util.*所有日期/时间工作。这是一个更好的API - 尽管文档DateTimeZoneBuilder也可能有点棘手。

于 2012-11-01T22:49:07.780 回答