Java Date时区打印不同年份的不同时区,需要解决方法

Mri*_*jay 18 javascript java timezone calendar date

在测试我的应用程序时,我遇到了一个奇怪的问题 当我在1945年之前输入一个日期时,它会改变时区.

我有这个简单的程序来显示问题.

public static void main(String[] args) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ");
    Calendar calendar = Calendar.getInstance();

    System.out.println("**********Before 1945");
    calendar.set(1943, Calendar.APRIL, 12, 5, 34, 12);
    System.out.println(format.format(calendar.getTime()));
    System.out.println(calendar.getTime());

    System.out.println("**********After 1945");
    calendar.set(1946, Calendar.APRIL, 12, 5, 34, 12);
    System.out.println(format.format(calendar.getTime()));
    System.out.println(calendar.getTime());
}
Run Code Online (Sandbox Code Playgroud)

我得到的输出如下: -

**********Before 1945
1943-04-12 05:34:12+0630
Mon Apr 12 05:34:12 IDT 1943

**********After 1945
1946-04-12 05:34:12+0530
Fri Apr 12 05:34:12 IST 1946
Run Code Online (Sandbox Code Playgroud)

对于第一个,我得到它+0630IDT,而第二个,我正在+0530IST预计.

编辑:-

在看了@Elliott Frisch之后,我在1942年之前尝试了一个日期: -

calendar.set(1915, Calendar.APRIL, 12, 5, 34, 12);
System.out.println(format.format(calendar.getTime()));
System.out.println(calendar.getTime());
Run Code Online (Sandbox Code Playgroud)

输出: -

1915-04-12 05:34:12+0553
Mon Apr 12 05:34:12 IST 1915
Run Code Online (Sandbox Code Playgroud)

在这里,它说,IST但显示+0553.不应该+0530.

只是为了比较,我在javascript中尝试了同样的事情: -

new Date("1946-04-12 05:34:12") //prints Fri Apr 12 1946 05:34:12 GMT+0530 (IST)
new Date("1943-04-12 05:34:12") //prints Fri Apr 12 1943 05:34:12 GMT+0530 (IST)
new Date("1915-04-12 05:34:12") //prints Mon Apr 12 1915 05:34:12 GMT+0530 (IST)
Run Code Online (Sandbox Code Playgroud)

哪个工作正常.我想知道为什么java会受到它的影响,如果它是一个已知问题,它可能的解决方法是什么.

提前致谢.

Tom*_*aas 13

这可能是Java(而不是JavaScript)的预期行为.

正如RobG上面的评论所暗示的那样,编程语言可能支持也可能不支持历史时间规则(例如DST和时区偏移).在您的情况下,您的Java运行时似乎支持它,而您的JavaScript运行时则不支持它.

可以在timeanddate.com上找到印度的历史时区和DST规则列表.该列表确认了Java日期的时区偏移:

  • 直到1941年:UTC + 5:53:20
  • 1941年:UTC + 6:30
  • 1942年:UTC + 5:30
  • 1943-44:UTC + 6:30
  • 从1945年开始:UTC + 5:30

检查您的日期对Wolfram | Alpha进一步确认您的Java日期UTC偏移:1915年,1943年,1946年

维基百科提供有关印度时间的更多信息:

加尔各答时间正式保持为一个单独的时区,直到1948年

加尔各答时间可以指定为UTC + 5:54或UTC + 5:53:20.后者与您的代码示例一致.

维基百科条目进一步表明,当前的IST时区与UTC + 5:30的偏移量在整个印度都没有完全生效,直到1955年.

正如Elliott Frisch指出,并通过上述timeanddate.com的链接证实,DST在第二次世界大战期间生效.在你对他的回答的评论中,你说:

这是我们应该保存在数据库中并在应用程序中使用它的方式,或者我们使用一些解决方法

我想这取决于.如果您确实需要准确地将日期区分为时间点,则需要与时区无关的表示,例如UTC或unix时间(或自unix时期以来的毫秒).如果您只使用同一时区的本地日期,则简单的字符串表示(例如YYYY-MM-DD hh:mm:ss)就足够了.


Ell*_*sch 10

发生了一场战争.从维基百科链接,印度在第二次世界大战期间观察到DST,从1942年至1945年.


Bas*_*que 6

java.time

避免使用与最早版本的Java捆绑在一起的麻烦的旧日期时间类.现在遗留下来,取而代之的是java.time类.

ZoneId z = ZoneId.of( "Asia/Kolkata" );  // "Asia/Calcutta"
LocalTime lt = LocalTime.of( 5 , 34 , 12 );

ZonedDateTime zdt1943 = ZonedDateTime.of( LocalDate.of( 1943 , Month.APRIL , 12 ) , lt , z );
ZonedDateTime zdt1945 = ZonedDateTime.of( LocalDate.of( 1945 , Month.APRIL , 12 ) , lt , z );
ZonedDateTime zdt1946 = ZonedDateTime.of( LocalDate.of( 1946 , Month.APRIL , 12 ) , lt , z );
ZonedDateTime zdt2016 = ZonedDateTime.of( LocalDate.of( 2016 , Month.APRIL , 12 ) , lt , z );
Run Code Online (Sandbox Code Playgroud)

转储到控制台.

System.out.println( "zdt1943: " + zdt1943 );
System.out.println( "zdt1945: " + zdt1945 );
System.out.println( "zdt1946: " + zdt1946 );
System.out.println( "zdt2016: " + zdt2016 );
Run Code Online (Sandbox Code Playgroud)

请参阅IdeOne.com中的实时代码.

跑步时 我们看到了与您的问题中描述的相同的行为,在战争期间和之后的五个半时间内,UTC偏差为六个半小时.我们得到同样的行为是否使用Asia/KolkataAsia/Calcutta.java.time类使用tzdata,以前称为Olson Database.

zdt1943:1943-04-12T05:34:12 + 06:30 [亚洲/加尔各答]

zdt1945:1945-04-12T05:34:12 + 06:30 [亚洲/加尔各答]

zdt1946:1946-04-12T05:34:12 + 05:30 [亚洲/加尔各答]

zdt2016:2016-04-12T05:34:12 + 05:30 [亚洲/加尔各答]

在问题中......

当我在1945年之前输入一个日期时,它会改变时区.

不,它不会改变时区.结果表明,在早些年,"5:34"被定义为比UTC早6个半小时,而在后来的几年中,定义比UTC早了5.5个小时.正如"5:34"意味着夏季西雅图时间晚8个小时,但冬天则是7个小时,因为夏令时(DST)无意义.

但我怀疑这些可能是错误的价值观; 请继续阅读.

时间在加尔各答

我们所看到的行为似乎与我对这个维基百科页面"加尔各答的时间"的阅读不符.该页面描述了除了工作时间或半小时以外的奇数偏移,例如UTC+05:54,我们在任何相应的代码示例中都没有看到.

所以我怀疑tzdata不包含印度的这一历史数据.但只是这个外行的猜测; 我不是历史学家.

不要将日期时间类型用于历史值

虽然我不知道印度历史时期的特定时间及其在tzdata中的处理,但似乎我们的日期时间库都没有处理这些历史的细微差别.

但是我们不应该期待这样的处理!知道的tzdata不能保证完全覆盖1970年以前的时区.

在引用历史日期时间值时,我建议您只使用文本而不是任何日期时间数据类型.数据类型的目的是用于验证和计算.你可能既没有为历史价值做任何事情.我无法想象你是在确定发票从1943年开始逾期的天数.

也许你应该编辑你的问题来描述为什么你如此准确地将这些历史日期时间值存储在数据库中.如果你只是在试验并注意到这些问题,那么就要知道你不应该期望在过去(1970年之前)到远期的未来(过去几周通知政治家有时会提出突然的时区定义变化)进行精确的日期时间处理. .

Upshot:尝试精确处理历史日期时间值充满了各种各样的问题,对我来说似乎毫无意义.

什么是可能的解决方法

我建议使用"本地"日期时间值作为ISO 8601格式的文本,不带任何时区.