设置Java Calendar的值不会给出预期的日期时间

我有一个小时,分钟,日期和毫秒时间戳,我正在尝试创建一个表示时间的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 

有谁知道为什么会这样?

您需要先设置时区。 请参阅下面的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; } 

如gtgaxiola所述:来自日历文档

Field Manipulation部分下:

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

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

从日历文档

Field Manipulation部分下:

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

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

我只想先设定时区:

  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点的评论所说。

甚至更好的解决方案就是不要使用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()); 

您在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转换为EST,即23 00:

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

调试器将帮助您在每一步中看到这些更改:)