当前位置: 首页 > 知识库问答 >
问题:

从java.sql.Timestamp到joda的LocalDate的意外转换

隆功
2023-03-14

我在将时间戳对象转换为joda的LocalTime时遇到问题。

参见以下示例:

public static void main(String[] args) {

    Timestamp t = Timestamp.valueOf("1111-11-11 00:00:00");
    System.out.println(t); //-- prints '1111-11-11 00:00:00.0'
    System.out.println(new LocalDate(t)); //-- prints '1111-11-17'

    Calendar calendar = Calendar.getInstance();
    calendar.setTime(t);
    System.out.println(LocalDate.fromCalendarFields(calendar)); //-- prints '1111-11-11'
}

我无法确定为什么“new LocalDate(t)”会导致“1111-11-17”。有人能帮我吗?

在使用joda time hibernate填充bean的LocalDate类型属性时,我注意到了这个“问题”。

共有2个答案

赵同
2023-03-14

过去几个世纪发生这种情况的原因与(全球)从儒略历切换到公历有关。基本上,当各国以零敲碎打的方式实施新日历系统时,他们会在此过程中损失几天或几周(在某些情况下,甚至几个月)。例如,在西班牙,儒略历1582年10月4日之后紧接着是公历1582年10月15日。维基百科对这个话题进行了不错的讨论。

只要您的应用程序不处理太远的过去日期,您就不必处理与此相关的差异。正如您在评论中提到的,系统。出来println(新的LocalDate(Timestamp.valueOf(“1999-11-11 00:00:00”)))正确输出1999-11-11。如果您需要使用这些较旧的日期,我建议您为儒略历寻找一个替代日期。

(顺便说一句,我很想知道如果你问当地日期,在西班牙,1582年10月5日会发生什么...一个从未发生过的日子。)

郝冥夜
2023-03-14

这确实与不同类型的日历有关。java.sql.Timestamp,如java.util.Date,没有任何日历信息,因为它们仅存储为数字,即自1970年以来的毫秒数,隐含的假设是在1582年10月4日至10月15日之间发生了跳转,当时正式采用公历,取代了旧的儒略历。因此,您不可能拥有代表1582年10月7日的Date(或Timestamp)对象。如果您尝试创建这样的日期,您将自动结束10天后。例如:

    Calendar c = Calendar.getInstance();
    c.setTimeZone(TimeZone.getTimeZone("UTC"));
    c.set(1582, Calendar.OCTOBER, 7, 0, 0, 0);
    c.set(Calendar.MILLISECOND, 0);
    d = c.getTime();
    System.out.println("Date: " + d);
    // Prints:
    // Date: Sun Oct 17 00:00:00 GMT 1582

换句话说,Date对象有一个隐含的Julian Gregorian年表,自动在这两者之间切换。

JodaTime更聪明一些,它支持多个年表,包括连续的朱利安年表、前公历年表、混合的朱利安-公历年表,以及与公历年表几乎相同的标准ISO年表。如果您阅读了LocalDate的JavaDoc。从CalendarFields方法中,您将看到它提到:

此factory方法忽略日历的类型,并始终创建具有ISO年表的LocalDate。

混合的儒略公历年表的行为类似于隐含的Java日期,在两个不同的日历之间自动切换。纯年表假设他们的日历系统永远在使用,因此例如,它假设公历从时间开始就被使用。

让我们看看每个年表如何对待1111-11-11日期:

    Calendar c = Calendar.getInstance();
    c.setTimeZone(TimeZone.getTimeZone("UTC"));
    c.set(1111, Calendar.NOVEMBER, 11, 0, 0, 0);
    c.set(Calendar.MILLISECOND, 0);
    Date d = c.getTime();
    System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
    System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));

结束为:

Date: Sat Nov 11 00:00:00 GMT 1111 (-27079747200000 milliseconds)
ISO: 1111-11-18T00:00:00.000Z
Julian+Gregorian: 1111-11-11T00:00:00.000Z
Julian: 1111-11-11T00:00:00.000Z
Gregorian: 1111-11-18T00:00:00.000Z

正如你所看到的,两个现代年表(ISO和Gregorian)报告了正确的日期,如果它们从一开始就被使用的话,而使用儒略历的两个年表报告了当时已知的日期,尽管事后我们知道,与真正的春分日相比,它要推迟7天。

让我们看看开关周围发生了什么:

    c.set(1582, Calendar.OCTOBER, 15, 0, 0, 0);
    d = c.getTime();
    System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
    System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));

最终为:

Date: Fri Oct 15 00:00:00 GMT 1582 (-12219292800000 milliseconds)
ISO: 1582-10-15T00:00:00.000Z
Julian+Gregorian: 1582-10-15T00:00:00.000Z
Julian: 1582-10-05T00:00:00.000Z
Gregorian: 1582-10-15T00:00:00.000Z

所以唯一剩下的就是朱利安历法。在所有还没有接受公历的国家,这是一个有效的日期,当时很多国家都是这样。希腊在1923年做出了改变。。。

在那之前的一毫秒,日期是:

    c.add(Calendar.MILLISECOND, -1);
    d = c.getTime();
    System.out.println("Date: " + d + " (" + d.getTime() + " milliseconds)");
    System.out.println("ISO: " + new DateTime(d, ISOChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian+Gregorian: " + new DateTime(d, GJChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Julian: " + new DateTime(d, JulianChronology.getInstance(DateTimeZone.forID("UTC"))));
    System.out.println("Gregorian: " + new DateTime(d, GregorianChronology.getInstance(DateTimeZone.forID("UTC"))));

含义:

Date: Thu Oct 04 23:59:59 GMT 1582 (-12219292800001 milliseconds)
ISO: 1582-10-14T23:59:59.999Z
Julian+Gregorian: 1582-10-04T23:59:59.999Z
Julian: 1582-10-04T23:59:59.999Z
Gregorian: 1582-10-14T23:59:59.999Z

ISO和格里高利年表报告的日期实际上在格里高利历中并不存在,因为10月15日之前没有格里高利历,但该日期在延长的、前格里高利历中有效。这就像在BCE纪念碑上找到一个BCE日期。。。在基督诞生之前,没有人知道他们在基督之前。

因此,问题的根源在于日期字符串不明确,因为您不知道在哪个日历中测量。5772年是未来的一年,还是当前的希伯来年?Java假设一个混合的儒略公历。JodaTime为不同的日历提供了广泛的支持,默认情况下它假设ISO8601年表。您的日期会自动从1111中使用的儒略历转换为我们当前使用的ISO年表。如果您希望您的JodaTime增强的时间戳使用与java.sql.Timestamp类相同的年表,那么在构建JodaTime对象时显式选择GJChronology

 类似资料:
  • 问题内容: 我正在使用JodaTime 1.6.2。 我有一个需要转换为(Joda)或进行映射的a。 原因是我想出了如何在a 和a 之间进行转换: 因此,如果我可以在和之间进行转换,则可以继续转换为。感谢您朝着正确的方向前进! 问题答案: 乔达时间 要将JodaTime转换为,只需执行 要将JodaTime转换为,只需执行 Java时间 要将Java8转换为,只需执行 要将Java8转换为,只需执

  • 问题内容: 我试图在执行更多计算之前将TimeZone信息简单地添加回LocalDate中。LocalDate来自使用ObjectLab LocalDateCalculator将天数添加到现有DateTime中,但是该方法需要返回修改后的ReadableInstant来形成一个Interval,然后可以检查该Interval。 我正在尝试的代码相当于将Joda LocalDate转换为Joda D

  • 我正在使用JodaTime获取创建帐户的日期和时间。格式为 当我得到它时,我把它作为字符串存储在我的数据库中,所以我四处寻找将它从字符串格式化为LocalDate的方法,但在我在网上找到的任何东西中都没有成功。我的下一步是一个可怕的解决方案,在我看来,循环遍历字符串,直到找到T,并删除它后面的所有内容。所以我只剩下 但理想情况下,如果可能,将日期设置为

  • 在Java8中,如何将(在中)转换为(在中)?

  • 问题内容: 将JodaTime转换为对象的最简单方法是什么? 问题答案: 乔达时间 要将JodaTime转换为,请执行 要将JodaTime转换为,请执行 Java时间 要将Java8转换为,请执行 要将Java8转换为,请执行 您可能会想用来缩短它,但是没有直接的API可以获取系统默认的区域偏移量。 要将Java8转换为,请执行

  • 问题内容: 我试图在 Joda中 更改by 的值: __ 对于,值是: 对于,值是: 没有时区差异。 如何使用TimeZone获取正确的时间戳? 例如,我想获取“ 2013-05-13 01:56:27.0”。 提前致谢。 编辑: 使用 MySQL ,列类型当然是。 问题答案: 实际上,这不是一个重复的问题。这是我几次后解决我的问题的方式: 这是从所需时区获取偏移的方法。 回到代码,我们从查询的结