我使用java日历在午夜(GMT)创建一些日期,然后将时区更改为我的本地时区,以确保请求的时间为1:00(GMT +1)。 以下代码有效,包括2个成功的断言。
TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT");
TimeZone TIMEZONE_LOCAL = TimeZone.getDefault(); // GMT + 1
private Calendar getMidnightGmtCalendarWithLocalTimezone() {
Calendar calendar = Calendar.getInstance(Locale.GERMAN);
calendar.set(2017, Calendar.JANUARY, 1, 0, 0, 0);
calendar.setTimeZone(TIMEZONE_GMT); // Probably not required
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY)); // Assert 1
// Log.i("TAG", "" + calendar.get(Calendar.HOUR_OF_DAY));
calendar.setTimeZone(TIMEZONE_LOCAL);
assertEquals(1, calendar.get(Calendar.HOUR_OF_DAY)); // Assert 2
return calendar;
}
现在我删除了第一个断言,我预计不会有任何变化。现实:第二个断言现在失败了!我试图了解.get()的实现,显然它也计算了一些时间,所以它不仅仅是收集价值。我仍然不知道为什么我的第二个断言失败了。
当我取消注释Log行时,第二个断言再次成功(只是为了确保问题来自calendar.get(),而不是断言!)
所以第一个问题:为什么会发生这种情况? Second qeustion:当我为Timezone GMT设置午夜的时间时,我如何确保我有一个带有本地时区的日历实例? (换句话说,我怎样才能正确转换时区?)
修改 我在Android上使用它,所以不能使用Java8。 Joda时间(如下所示)是一个很好的选择,我肯定会对此进行研究。但是,作为我的问题的答案,我想知道如何使用Java7 Calendar,因为我现在已经绑定了该代码。
答案 0 :(得分:4)
使用java.time,而不是stFields
。
<CFHTTPPARAM VALUE="#stFields#" TYPE="body">
java.util.Calendar
包括serializeJSON
function在内的旧日期时间类很麻烦,设计很差,容易混淆和有缺陷。避免他们。尝试理解他们过度的API是没有意义的。
遗留日期时间类被Java 8及更高版本中内置的Calendar
类所取代。
Instant.parse( "2017-01-01T00:00:00Z" ) // UTC.
.atZone( ZoneId.of( "Africa/Algiers" ) ) // Time zone with offset +01:00.
java.time package类代表Instant
中时间轴上的一个时刻,分辨率为纳秒。
Calendar
instant.toString():2017-01-01T00:00:00Z
Instant
您要求在比UTC早一个小时的时区内看到它。因此,让我们尝试应用时区UTC来获取Africa/Algiers
。
Instant instant = Instant.parse( "2017-01-01T00:00:00Z" );
zdt.toString():2017-01-01T01:00 + 01:00 [非洲/阿尔及尔]
ZonedDateTime
如果要通过参数构造ZonedDateTime
日期时间而不是解析字符串(如上所示),请将参数传递给UTC的工厂方法。指定方便常量OffsetDateTime
。与ZoneId z = ZoneId.of( "Africa/Algiers" );
ZonedDateTime zdt = instant.atZone( z );
相比,月份的数字是明智的,1月到12月是1-12。
OffsetDateTime
odt.toString():2017-01-01T00:00Z
与上述类似,我们可以调整为时区。对ZoneOffset.UTC
等异常进行了调整。
Calendar
zdt.toString():2017-01-01T01:00 + 01:00 [非洲/阿尔及尔]
请参阅此Daylight Saving Time (DST)。
OffsetDateTime odt = OffsetDateTime.of( 2017 , 1 , 1 , 0 , 0 , 0 , 0 , ZoneOffset.UTC ) ;
关于在问题代码中使用ZoneId z = ZoneId.of( "Africa/Algiers" );
ZonedDateTime zdt = odt.atZoneSameInstant( z );
... Locale
与时区完全正交,分开且不同。 Locale
对象仅影响生成的字符串的格式,以表示日期时间值。
code run line in IdeOne.com框架内置于Java 8及更高版本中。这些类取代了麻烦的旧java.time日期时间类,例如legacy,java.util.Date
和&amp; Calendar
现在位于SimpleDateFormat
的Joda-Time项目建议迁移到maintenance mode类。
要了解详情,请参阅java.time。并搜索Stack Overflow以获取许多示例和解释。规范是Oracle Tutorial。
从哪里获取java.time类?
How to use ThreeTenABP…项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如ThreeTen-Extra,Interval
,YearWeek
和YearQuarter
。
答案 1 :(得分:0)
所以,我运行了以下代码片段:
public static void main(String[] args) throws Exception {
TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT");
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin"));
calendar.set(2017, Calendar.JANUARY, 1, 0, 0, 0);
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
calendar.setTimeZone(TIMEZONE_GMT); // Probably not required
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
}
在执行它时,我在控制台中得到了0
和23
这是正确的。
然后我注释掉了第一个Sysout
语句,并打印了0
(我希望它能打印23
)。
在进一步探索之后,我发现了this这样的答案正确地解释了日历对象只存储了毫秒,并且没有考虑TimeZone
。
所以,如果我是你,我不会过分担心asserts
,我会确保我得到一个Calendar
实例,其中包含所需时区(如果默认时区设置为默认时区,则为默认时区)你需要的时区),例如:
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin"));
//OR
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin"));
//OR
Calendar calendar = Calendar.getInstance(); //If default timezone is Europe/Berlin