为什么在Java日历对象上设置时区没有效果?

ken*_*ood -1 java timezone datetime calendar date

请考虑以下代码段.

    TimeZone tz = TimeZone.getTimeZone("GMT");
    System.out.println(tz);

    Calendar cal = Calendar.getInstance();
    cal.set(2017, Calendar.DECEMBER, 25);

    System.out.println("before setting time zone: " + cal.getTime());
    cal.setTimeZone(tz);

    System.out.println("after setting time zone: " + cal.getTime());
Run Code Online (Sandbox Code Playgroud)

结果如下:

sun.util.calendar.ZoneInfo[id="GMT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
before setting time zone: Mon Dec 25 22:39:53 EST 2017 
after setting time zone: Mon Dec 25 22:39:53 EST 2017
Run Code Online (Sandbox Code Playgroud)

在时区更改后,为什么日期的打印输出仍显示EST?不应该是GMT吗?

Bas*_*que 5

TL;博士

LocalDate.of( 2017 , Month.DECEMBER , 25 )
         .atStartOfDay( ZoneId.of( "America/New_York" ) )
Run Code Online (Sandbox Code Playgroud)

避免遗留日期时间类

您正在使用现在遗留下来的非常麻烦的旧日期时间类.

要直接回答您的问题,该Calendar::getTime方法返回一个Date.在这些类中的许多设计缺陷中,Date::toString在生成字符串时动态应用JVM的当前默认时区.这样你就EST出现了.

永远不要使用这些类.仅使用他们的替代品,业界领先的java.time类.

java.time

LocalDate级表示没有时间一天和不同时区的日期,唯一的价值.

LocalDate xmas2017 = LocalDate.of( 2017 , Month.DECEMBER , 25 ) ;
Run Code Online (Sandbox Code Playgroud)

如果您想要日期时间,即当天的第一时刻,则必须指定时区.时区对于确定日期至关重要.对于任何给定的时刻,日期在全球范围内因地区而异.例如,法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是"昨天" .

指定适当的时区名称,格式continent/region,如America/Montreal,Africa/CasablancaPacific/Auckland.切勿使用3-4字母缩写,例如ESTIST因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!).

永远不要假设这一天从00:00开始.在某些区域中,夏令时(DST)等异常表示该日可能会在某个其他时间开始,例如01:00:00.让java.time确定一天中的第一个时刻.

申请一个ZoneId来获得一个ZonedDateTime.

ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdtNewYork = xmas2017.atStartOfDay( z ) ;
Run Code Online (Sandbox Code Playgroud)

请注意,圣诞节在印度比在纽约早得多.

ZonedDateTime zdtKolkta = xmas2017.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) ) ;
Run Code Online (Sandbox Code Playgroud)

这就是为什么精灵的后勤部门将圣诞老人送到基里巴斯进行首次交付.这些岛屿最早的圣诞节,时区比UTC早14小时.从那里开始,当地球旋转到新的一天的阳光下时,驯鹿向东飞行,一个接一个地追逐连续的中午.

如果您希望将相同的时刻调整为UTC,请提取Instant始终为UTC的时间.

Instant instant = zdt.toInstant() ;
Run Code Online (Sandbox Code Playgroud)

关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

现在处于维护模式Joda-Time项目建议迁移到java.time类.

要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.