计算 1918-03-24 之后的天数时,Joda 时间差一错误
Joda-time off-by-one error when counting days after 1918-03-24
使用 Joda-Time 计算 1900-01-01
和 1918-03-24
之后的日期之间的天数似乎给出了差一的结果。
使用 Java 8 java.time
给出了正确的结果。 Joda-Time 不计算的原因是什么 1918-03-25
?
使用 Joda-time v2.9.9.
public static void main(String[] args) {
jodaDiff("1918-03-24");
javaDiff("1918-03-24");
jodaDiff("1918-03-25");
javaDiff("1918-03-25");
jodaDiff("1918-03-26");
javaDiff("1918-03-26");
jodaDiff("2017-10-10");
javaDiff("2017-10-10");
}
private static void jodaDiff(String date) {
DateTime start = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forID("UTC"));
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd");
DateTime end = dateDecoder.parseDateTime(date);
int diff = Days.daysBetween(start, end).getDays();
System.out.println("Joda " + date + " " + diff);
}
private static void javaDiff(String date) {
LocalDate start = LocalDate.parse("1900-01-01");
LocalDate end = LocalDate.parse(date);
int diff = (int) ChronoUnit.DAYS.between(start, end);
System.out.println("Java " + date + " " + diff + "\n");
}
输出:
Joda 1918-03-24 6656
Java 1918-03-24 6656
Joda 1918-03-25 6656
Java 1918-03-25 6657
Joda 1918-03-26 6657
Java 1918-03-26 6658
Joda 2017-10-10 43015
Java 2017-10-10 43016
问题是您的 DateTimeFormatter
使用的是系统默认时区。理想情况下,您应该解析为 LocalDate
值而不是 DateTime
,但无论如何您都可以通过使用 UTC 作为格式化程序来修复它:
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd").withZoneUTC();
要使用 LocalDate
进行解析,只需使用:
org.joda.time.LocalDate start = new org.joda.time.LocalDate(1900, 1, 1);
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd");
org.joda.time.LocalDate end = dateDecoder.parseLocalDate(date);
(显然,如果您不使用 Java 8,则无需完全限定它。)
Joda-time off-by-one error when counting days after 1918-03-24
试试这个,我更正了你的程序它给你例外的答案与 Java
public static void main(String[] args) {
jodaDiff("1918-03-24");
javaDiff("1918-03-24");
jodaDiff("1918-03-25");
javaDiff("1918-03-25");
jodaDiff("1918-03-26");
javaDiff("1918-03-26");
jodaDiff("2017-10-10");
javaDiff("2017-10-10");
}
private static void jodaDiff(String date) {
DateTime start = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forID("UTC"));
DateTime end = new DateTime(date);
int diff = Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays();
System.out.println("Joda " + date + " " + diff);
}
private static void javaDiff(String date) {
LocalDate start = LocalDate.parse("1900-01-01");
LocalDate end = LocalDate.parse(date);
int diff = (int) ChronoUnit.DAYS.between(start, end);
System.out.println("Java " + date + " " + diff + "\n");
}
DateTime start = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forID("UTC"));
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd").withZone(DateTimeZone.forID("UTC"));
当你创建第一次约会时,你正在确定区域,而第二次则不然。所以要么删除区域,要么在两个地方设置相同的区域。
您也可以将 DateTime 都转换为 LocalDate,它也适用于此示例,但第一个解决方案应该更好。
is correct and direct to the point. I'd just like to add more details about what's happening and why you get these results (as you - Jon 也回复了提示,这也是正确的)。
您的 JVM 默认时区可能是 Europe/London
(或任何其他具有 DST change in March 24th 1918 的时区)。您可以通过在 Joda-Time 中使用 DateTimeZone.getDefault()
和在 Java 中使用 ZoneId.systemDefault()
来检查 8.
您在 Joda 中创建的开始日期是 1900-01-01T00:00Z
(1 月 1 日st 1900 年午夜 UTC)。但是,结束日期只能通过使用年、月和日来创建。但是 DateTime
还需要时间 (hour/minute/second/millisecond) 和时区。由于未指定,它被设置为 JVM 默认时区 的午夜(不保证是 UTC - 根据 JVM 配置,您可以获得不同的结果)。
假设您的默认时区是伦敦(这就是我重现问题的方式 - 在我的 JVM 默认时区 (America/Sao_Paulo
) 中它不会发生)。
1981 年 3 月 25,伦敦采用夏令时,因此当您使用 1918-03-25
创建结束日期时,结果是 3 月 25 th 1981 年伦敦时区午夜 - 但由于 DST 更改,结果为 1918-03-25T00:00+01:00
- 在 DST 期间,伦敦使用偏移量 +01:00
,这意味着它是一个UTC 的 提前 小时(因此此结束日期相当于 1918-03-24T23:00Z
- 或 March 24th 1981 at 11下午 (UTC)).
因此,小时差为 159767,不足以完成 6657 天,因此差值为 6656 天(四舍五入始终为最低值 - 差值必须至少为 159768 小时才能完成 6657天)。
当您使用 LocalDate
时,不考虑时间和夏令时的影响(LocalDate
只有日、月和年),您会得到正确的差异。如果您也将结束日期设置为 UTC,您也会得到正确的结果,因为 UTC 没有 DST 更改。
顺便说一句,如果您使用 Java 8 ZonedDateTime
,并使用 UTC 开始日期和伦敦时区结束日期(而不是使用 LocalDate
),您获得相同的结果差异。
没有直接关系,但在 Joda-Time 中,您可以使用常量 DateTimeZone.UTC
来引用 UTC - 调用 forID("UTC")
是多余的,因为它 returns 无论如何都是常量(DateTimeZone.forID("UTC")==DateTimeZone.UTC
returns true
).
使用 Joda-Time 计算 1900-01-01
和 1918-03-24
之后的日期之间的天数似乎给出了差一的结果。
使用 Java 8 java.time
给出了正确的结果。 Joda-Time 不计算的原因是什么 1918-03-25
?
使用 Joda-time v2.9.9.
public static void main(String[] args) {
jodaDiff("1918-03-24");
javaDiff("1918-03-24");
jodaDiff("1918-03-25");
javaDiff("1918-03-25");
jodaDiff("1918-03-26");
javaDiff("1918-03-26");
jodaDiff("2017-10-10");
javaDiff("2017-10-10");
}
private static void jodaDiff(String date) {
DateTime start = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forID("UTC"));
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd");
DateTime end = dateDecoder.parseDateTime(date);
int diff = Days.daysBetween(start, end).getDays();
System.out.println("Joda " + date + " " + diff);
}
private static void javaDiff(String date) {
LocalDate start = LocalDate.parse("1900-01-01");
LocalDate end = LocalDate.parse(date);
int diff = (int) ChronoUnit.DAYS.between(start, end);
System.out.println("Java " + date + " " + diff + "\n");
}
输出:
Joda 1918-03-24 6656
Java 1918-03-24 6656Joda 1918-03-25 6656
Java 1918-03-25 6657Joda 1918-03-26 6657
Java 1918-03-26 6658Joda 2017-10-10 43015
Java 2017-10-10 43016
问题是您的 DateTimeFormatter
使用的是系统默认时区。理想情况下,您应该解析为 LocalDate
值而不是 DateTime
,但无论如何您都可以通过使用 UTC 作为格式化程序来修复它:
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd").withZoneUTC();
要使用 LocalDate
进行解析,只需使用:
org.joda.time.LocalDate start = new org.joda.time.LocalDate(1900, 1, 1);
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd");
org.joda.time.LocalDate end = dateDecoder.parseLocalDate(date);
(显然,如果您不使用 Java 8,则无需完全限定它。)
Joda-time off-by-one error when counting days after 1918-03-24
试试这个,我更正了你的程序它给你例外的答案与 Java
public static void main(String[] args) {
jodaDiff("1918-03-24");
javaDiff("1918-03-24");
jodaDiff("1918-03-25");
javaDiff("1918-03-25");
jodaDiff("1918-03-26");
javaDiff("1918-03-26");
jodaDiff("2017-10-10");
javaDiff("2017-10-10");
}
private static void jodaDiff(String date) {
DateTime start = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forID("UTC"));
DateTime end = new DateTime(date);
int diff = Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays();
System.out.println("Joda " + date + " " + diff);
}
private static void javaDiff(String date) {
LocalDate start = LocalDate.parse("1900-01-01");
LocalDate end = LocalDate.parse(date);
int diff = (int) ChronoUnit.DAYS.between(start, end);
System.out.println("Java " + date + " " + diff + "\n");
}
DateTime start = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeZone.forID("UTC"));
DateTimeFormatter dateDecoder = DateTimeFormat.forPattern("YYYY-MM-dd").withZone(DateTimeZone.forID("UTC"));
当你创建第一次约会时,你正在确定区域,而第二次则不然。所以要么删除区域,要么在两个地方设置相同的区域。
您也可以将 DateTime 都转换为 LocalDate,它也适用于此示例,但第一个解决方案应该更好。
您的 JVM 默认时区可能是 Europe/London
(或任何其他具有 DST change in March 24th 1918 的时区)。您可以通过在 Joda-Time 中使用 DateTimeZone.getDefault()
和在 Java 中使用 ZoneId.systemDefault()
来检查 8.
您在 Joda 中创建的开始日期是 1900-01-01T00:00Z
(1 月 1 日st 1900 年午夜 UTC)。但是,结束日期只能通过使用年、月和日来创建。但是 DateTime
还需要时间 (hour/minute/second/millisecond) 和时区。由于未指定,它被设置为 JVM 默认时区 的午夜(不保证是 UTC - 根据 JVM 配置,您可以获得不同的结果)。
假设您的默认时区是伦敦(这就是我重现问题的方式 - 在我的 JVM 默认时区 (America/Sao_Paulo
) 中它不会发生)。
1981 年 3 月 25,伦敦采用夏令时,因此当您使用 1918-03-25
创建结束日期时,结果是 3 月 25 th 1981 年伦敦时区午夜 - 但由于 DST 更改,结果为 1918-03-25T00:00+01:00
- 在 DST 期间,伦敦使用偏移量 +01:00
,这意味着它是一个UTC 的 提前 小时(因此此结束日期相当于 1918-03-24T23:00Z
- 或 March 24th 1981 at 11下午 (UTC)).
因此,小时差为 159767,不足以完成 6657 天,因此差值为 6656 天(四舍五入始终为最低值 - 差值必须至少为 159768 小时才能完成 6657天)。
当您使用 LocalDate
时,不考虑时间和夏令时的影响(LocalDate
只有日、月和年),您会得到正确的差异。如果您也将结束日期设置为 UTC,您也会得到正确的结果,因为 UTC 没有 DST 更改。
顺便说一句,如果您使用 Java 8 ZonedDateTime
,并使用 UTC 开始日期和伦敦时区结束日期(而不是使用 LocalDate
),您获得相同的结果差异。
没有直接关系,但在 Joda-Time 中,您可以使用常量 DateTimeZone.UTC
来引用 UTC - 调用 forID("UTC")
是多余的,因为它 returns 无论如何都是常量(DateTimeZone.forID("UTC")==DateTimeZone.UTC
returns true
).