12

我有一个小时、分钟、日期和毫秒时间戳,并且正在尝试创建一个表示时间的 Date 对象。时间戳以东部夏令时间提供。

在剖析问题时,我创建了一些简单的测试代码来查看发生了什么,并观察到以下情况:

    Date today = new Date();
    int hour = 4, min  = 0, sec  = 0, ms   = 64;
    boolean print = true;

    Calendar cal = GregorianCalendar.getInstance();
    if(print)
        System.out.println("After initializing, time is: "+cal.getTime());
    cal.clear();
    if(print)
        System.out.println("After clearing, time is: "+cal.getTime());
    cal.setTime(today);
    if(print)
        System.out.println("After setting date, time is: "+cal.getTime());
    cal.set(Calendar.HOUR_OF_DAY,hour);
    if(print)
        System.out.println("After setting hour, time is: "+cal.getTime());
    cal.set(Calendar.MINUTE,min);
    if(print)
        System.out.println("After setting minute, time is: "+cal.getTime());
    cal.set(Calendar.SECOND,sec);
    if(print)
        System.out.println("After setting second, time is: "+cal.getTime());
    cal.set(Calendar.MILLISECOND,ms);
    if(print)
        System.out.println("After setting milliseconds, time is: "+cal.getTime());
    cal.setTimeZone(TimeZone.getTimeZone("EDT"));

    System.out.println("After setting time zone, time is: "+cal.getTime());

这将产生输出:

After initializing, time is: Tue Jan 07 16:01:59 EST 2014
After clearing, time is: Thu Jan 01 00:00:00 EST 1970
After setting date, time is: Tue Jan 07 16:01:59 EST 2014
After setting hour, time is: Tue Jan 07 04:01:59 EST 2014
After setting minute, time is: Tue Jan 07 04:00:59 EST 2014
After setting second, time is: Tue Jan 07 04:00:00 EST 2014
After setting milliseconds, time is: Tue Jan 07 04:00:00 EST 2014
After setting time zone, time is: Tue Jan 07 04:00:00 EST 2014

但是,如果我稍微更改代码:

boolean print = false;

我得到以下(不同的)结果(!)

After setting time zone, time is: Mon Jan 06 23:00:00 EST 2014

有谁知道为什么会这样?

4

5 回答 5

7

正如 gtgaxiola 所述:来自日历文档

字段操作部分下:

set(f, value) 将日历字段 f 更改为值。此外,它设置一个内部成员变量来指示日历字段 f 已更改。尽管日历字段 f 立即更改,但在下一次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 之前,不会重新计算日历的时间值(以毫秒为单位)。

问题是您的getTime()调用会重新计算日期,但 setTimeZone(..) 并未将内部成员变量设置isTimeSet为 false。因此,您的第一个输出中的最后一行对您来说是错误的,因为您希望考虑时区,而不是考虑时区。

于 2014-01-07T21:41:46.737 回答
7

来自日历文档

字段操作部分下:

set(f, value) 将日历字段 f 更改为值。此外,它设置一个内部成员变量来指示日历字段 f 已更改。尽管日历字段 f 立即更改,但在下一次调用 get()、getTime()、getTimeInMillis()、add() 或 roll() 之前,不会重新计算日历的时间值(以毫秒为单位)。

因此,多次调用 set() 不会触发多次不必要的计算。作为使用 set() 更改日历字段的结果,其他日历字段也可能更改,具体取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段后,get(f) 不一定会返回调用 set 方法设置的值。具体由具体的日历类决定。

于 2014-01-07T21:21:14.823 回答
5

您需要先设置时区。请参阅下面的 GregorianCalendar.setTimeZone 的定义:

public void setTimeZone(TimeZone value)
{
    zone = value;
    sharedZone = false;
    /* Recompute the fields from the time using the new zone.  This also
     * works if isTimeSet is false (after a call to set()).  In that case
     * the time will be computed from the fields using the new zone, then
     * the fields will get recomputed from that.  Consider the sequence of
     * calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
     * Is cal set to 1 o'clock EST or 1 o'clock PST?  Answer: PST.  More
     * generally, a call to setTimeZone() affects calls to set() BEFORE AND
     * AFTER it up to the next call to complete().
     */
    areAllFieldsSet = areFieldsSet = false;
}
于 2014-01-07T21:23:40.967 回答
3

我会先简单地设置时区:

 Calendar cal = GregorianCalendar.getInstance();

    cal.clear();
    cal.setTimeZone(TimeZone.getTimeZone("EDT"));
    cal.setTime(today);
    cal.set(Calendar.HOUR_OF_DAY,hour);
    cal.set(Calendar.MINUTE,min);
    cal.set(Calendar.SECOND,sec);
    cal.set(Calendar.MILLISECOND,ms);

然而,正如评论中所说,它正在做它应该做的事情,美国东部标准时间凌晨 4 点是晚上 11 点。

甚至更好的解决方案是根本不使用Calendar,例如 joda-time。

编辑:这对我来说是正确的时间。

    Date today = new Date();
    int hour = 4, min  = 0, sec  = 0, ms   = 64;
    boolean print = false;

    Calendar cal = GregorianCalendar.getInstance();
    if(print)
        System.out.println("After initializing, time is: "+cal.getTime());
    cal.clear();
    if(print)
        System.out.println("After clearing, time is: "+cal.getTime());
    cal.setTimeZone(TimeZone.getTimeZone("EDT"));
    if(print)
        System.out.println("After setting time zone, time is: "+cal.getTime());
    cal.setTime(today);
    if(print)
        System.out.println("After setting date, time is: "+cal.getTime());
    cal.set(Calendar.HOUR_OF_DAY,hour);
    if(print)
        System.out.println("After setting hour, time is: "+cal.getTime());
    cal.set(Calendar.MINUTE,min);
    if(print)
        System.out.println("After setting minute, time is: "+cal.getTime());
    cal.set(Calendar.SECOND,sec);
    if(print)
        System.out.println("After setting second, time is: "+cal.getTime());
    cal.set(Calendar.MILLISECOND,ms);
    if(print)
        System.out.println("After setting milliseconds, time is: "+cal.getTime());

    System.out.println("TIME: "+cal.getTime());
于 2014-01-07T21:21:40.223 回答
1

您在 GMT 中创建当前日期的实例:

Calendar cal = GregorianCalendar.getInstance();
cal.setTime(today);

然后将时间更改为凌晨 4 点:

cal.set(Calendar.HOUR_OF_DAY,hour);
cal.set(Calendar.MINUTE,min);
cal.set(Calendar.SECOND,sec);

之后,您将日期从 GMT 转换为 23 00 的 EST:

cal.setTimeZone(TimeZone.getTimeZone("EDT"));

调试器将帮助您在每个步骤中查看这些更改 :)

于 2014-01-07T21:19:14.797 回答