Android Java日历时区更改未按预期工作
我使用java日历在午夜(GMT)创建一些日期,然后将时区更改为我的本地时区,以确保请求的时间为1:00(GMT+1)。 下面的代码有效,包括2个成功的断言Android Java日历时区更改未按预期工作,android,calendar,timezone,java-7,Android,Calendar,Timezone,Java 7,我使用java日历在午夜(GMT)创建一些日期,然后将时区更改为我的本地时区,以确保请求的时间为1:00(GMT+1)。 下面的代码有效,包括2个成功的断言 TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT"); TimeZone TIMEZONE_LOCAL = TimeZone.getDefault(); // GMT + 1 private Calendar getMidnightGmtCalendarWithLocalTimezone(
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()的实现,显然它也会计算一些时间,所以它不仅仅是收集值。我仍然不明白为什么我的第二次断言失败了
当我取消注释日志行时,第二次断言再次成功(只是为了确保问题是由calendar.get()引起的,而不是断言!)
第一个问题:为什么会发生这种情况?
第二个问题:当我将时区GMT的时间设置为午夜时,如何确保我有一个带有本地时区的日历实例?(换句话说,如何正确转换时区?)
编辑:
我在Android上使用这个,所以不能使用Java8。Joda time(如下所示)是一个很好的选择,我将对此进行深入研究。但是,作为我问题的答案,我想知道这在Java7 Calendar中是如何工作的,因为我现在必须使用该代码。因此,我运行了以下代码片段:
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
)
在进一步研究之后,我找到了这样一个答案,它正确地解释了calendar对象只存储毫秒,而不考虑时区
因此,如果我是你,我不会太担心断言
,我会确保我得到一个带有所需时区的日历
实例(如果默认时区设置为你需要的时区,则为默认时区),例如:
因此,我运行了以下代码段:
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
)
在进一步研究之后,我找到了这样一个答案,它正确地解释了calendar对象只存储毫秒,而不考虑时区
因此,如果我是你,我不会太担心断言
,我会确保我得到一个带有所需时区的日历
实例(如果默认时区设置为你需要的时区,则为默认时区),例如:
tl;博士
使用java.time,而不是java.util.Calendar
Instant.parse( "2017-01-01T00:00:00Z" ) // UTC.
.atZone( ZoneId.of( "Africa/Algiers" ) ) // Time zone with offset +01:00.
避免日历
旧的日期时间类(包括)很麻烦,设计不好,容易混淆,而且有缺陷。避开它们。试图理解他们过度紧张的API毫无意义
传统的日期时间类被内置在Java8和更高版本中的一系列类所取代
Instant
该类以纳秒的分辨率表示时间线上的一个时刻
Instant instant = Instant.parse( "2017-01-01T00:00:00Z" );
instant.toString():2017-01-01T00:00:00Z
ZoneDateTime
您要求在UTC提前一小时的时区中查看。所以,让我们尝试应用时区来获得一个
zdt.toString():2017-01-01T01:00+01:00[非洲/阿尔及尔]
OffsetDateTime
如果希望通过参数而不是解析字符串来构造日期时间(如上所示),请将参数传递给的工厂方法。指定方便的常数。月份的编号是合理的,1-12月份为1-12,与日历相比
OffsetDateTime odt = OffsetDateTime.of( 2017 , 1 , 1 , 0 , 0 , 0 , 0 , ZoneOffset.UTC ) ;
odt.toString():2017-01-01T00:00Z
与上面类似,我们可以调整到一个时区。针对异常情况进行调整,例如:
zdt.toString():2017-01-01T01:00+01:00[非洲/阿尔及尔]
看这个
Locale
关于问题代码中Locale
的使用…Locale
与时区完全正交,相互独立。Locale
对象只影响生成的表示日期时间值的字符串的格式
关于java.time
该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&
该项目现已启动,建议迁移到类
要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是
从哪里获得java.time类
- 后来
- 内置的李>
- 标准JavaAPI的一部分,带有捆绑实现
- Java9添加了一些次要功能和修复
- 及
- 大部分java.time功能都在中向后移植到Java6和Java7
-
- 该项目专门为Android采用了ThreeTen Backport(如上所述)
- 看
该项目使用其他类扩展了java.time。这个项目是java.time将来可能添加的一个试验场。您可以在这里找到一些有用的类,如、、和。tl;博士
使用java.time,而不是java.util.Calendar
Instant.parse( "2017-01-01T00:00:00Z" ) // UTC.
.atZone( ZoneId.of( "Africa/Algiers" ) ) // Time zone with offset +01:00.
避免日历
旧的日期时间类(包括)很麻烦,设计不好,容易混淆,而且有缺陷。避开它们。试图理解他们过度紧张的API毫无意义
传统的日期时间类被内置在Java8和更高版本中的一系列类所取代
Instant
该类以纳秒的分辨率表示时间线上的一个时刻
Instant instant = Instant.parse( "2017-01-01T00:00:00Z" );
instant.toString():2017-01-01T00:00:00Z
ZoneDateTime
您要求在UTC提前一小时的时区中查看。所以,让我们尝试应用时区来获得一个
zdt.toString():2017-01-01T01:00+01:00[非洲/阿尔及尔]
OffsetDateTime<