在测试我的应用程序时,我遇到了一个奇怪的问题。当我在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());
}
我得到的输出如下: -
**********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
对于第一个,我将其设为+0630
和IDT
,而对于第二个,我得到+0530
和IST
这是预期的。< / p>
在看了@Elliott Frisch后,我在1942年之前尝试了一个日期: -
calendar.set(1915, Calendar.APRIL, 12, 5, 34, 12);
System.out.println(format.format(calendar.getTime()));
System.out.println(calendar.getTime());
输出: -
1915-04-12 05:34:12+0553
Mon Apr 12 05:34:12 IST 1915
再次说明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)
哪个工作正常。 我想知道为什么java会受到它的影响,如果这是一个已知问题,那么可能的解决方法是什么。
提前致谢。
答案 0 :(得分:13)
这可能是Java(而不是JavaScript)的预期行为。
正如上面RobG的评论所暗示的那样,编程语言可能支持也可能不支持历史时间规则(例如DST和时区偏移)。在您的情况下,您的Java运行时似乎支持它,而您的JavaScript运行时则不支持它。
可以在timeanddate.com找到印度的历史时区和DST规则列表。该列表确认了Java日期的时区偏移:
根据Wolfram检查您的日期| Alpha进一步确认您的Java日期UTC偏移:1915,1943,1946
维基百科提供了有关time in India的更多信息:
加尔各答时间被正式维持为一个单独的时区,直到1948年
Calcutta time可以指定为UTC + 5:54或UTC + 5:53:20。后者与您的代码示例一致。
维基百科条目进一步表明,当前的IST时区与UTC + 5:30的偏移量在整个印度都没有完全生效,直到1955年。
作为pointed out by Elliott Frisch并通过上述timeanddate.com链接确认,DST在第二次世界大战期间生效。在你对他的回答的评论中,你说:
这是我们应该保存在数据库中并在应用程序中使用它的方式,或者我们使用一些解决方法
我想这取决于。如果您确实需要准确地将日期区分为时间点,则需要与时区无关的表示,例如UTC或unix时间(或自unix时期以来的毫秒)。如果你只使用同一时区的本地日期,一个简单的字符串表示(例如YYYY-MM-DD hh:mm:ss)就足够了。
答案 1 :(得分:10)
有一个war。从维基百科链接,印度在第二次世界大战期间观察到了DST,从1942年至1945年。
答案 2 :(得分:6)
避免使用与最早版本的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 );
转储到控制台。
System.out.println( "zdt1943: " + zdt1943 );
System.out.println( "zdt1945: " + zdt1945 );
System.out.println( "zdt1946: " + zdt1946 );
System.out.println( "zdt2016: " + zdt2016 );
运行时。我们看到的问题与您的问题中描述的行为相同,在战争期间有offset-from-UTC六个半小时,之后有五个半小时。无论使用Asia/Kolkata
还是Asia/Calcutta
,我们都会遇到相同的行为。 java.time类使用tzdata,以前称为 Olson数据库。
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提前六个半小时,而在后来的几年中,定义比UTC早了五个半小时。就像“5:34”意味着夏天在西雅图的UTC时间晚8小时,但在冬天则是7小时,因为Daylight Saving Time (DST)胡说八道。
但我怀疑这些可能是错误的价值观;请继续阅读。
我们看到的行为似乎与我对这个维基百科页面Time in Calcutta的阅读不符。该页面描述了除了工作时间或半小时以外的奇数偏移,例如UTC+05:54
,我们在任何相应的代码示例中都没有看到。
所以我怀疑 tzdata 不包含印度的这个历史数据。但只是这个外行的猜测;我不是历史学家。
虽然我不知道印度历史时期的具体时间及其在tzdata中的处理,但似乎我们的日期时间库都没有处理这些历史的细微差别。
但是我们不应该期待这样的处理!知道 tzdata 不承诺完全覆盖time zones before 1970 。
当提到历史日期时间值时,我建议您只使用文本而不是任何日期时间数据类型。数据类型的目的是用于验证和计算。你可能既没有为历史价值做任何事情。我无法想象你是在确定发票从1943年开始逾期的天数。
也许你应该编辑你的问题来描述为什么你如此准确地将这些历史日期时间值存储在数据库中。如果你只是在试验并注意到这些问题,那么就要知道你不应该期望在过去(1970年之前)到远期的未来(过去几周通知政治家有时会提出突然的时区定义变化)进行精确的日期时间处理。 。
Upshot:尝试精确处理历史日期时间值充满了各种各样的问题,对我来说似乎毫无意义。
它可能的解决方法是什么
我建议使用“本地”日期时间值作为ISO 8601格式的文本,不带任何时区。
答案 3 :(得分:1)
我建议保留与数据库中日期相当的纪元。我相信,无论白天节约光,一段时间的时间和日期代表实际,无论是IDT还是IST。我会使用示例https://stackoverflow.com/a/6687502并将所有日期转换为纪元并存储到数据库中。我将反转逻辑以显示数据库中的日期时间以及IDT / IST指示符,以避免给用户造成混淆。