通过 Jackson 序列化 java.sql.Date,同时考虑夏令时

leb*_*ski 2 java mysql date jackson

我正在使用 ajava.sql.Date将日期字段存储在我的域对象之一中。该字段映射到 MySQLDATE列。当我尝试通过 Jackson 将此字段序列化为 JSON 时,Jackson 似乎没有考虑夏令时。

这是我的域对象中的字段的样子:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "EST")
private Date date;
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,Jackson 已被指示将时区解释为 EST。我的 MySQL 数据库也使用 EST 时区:

SHOW VARIABLES LIKE '%zone';
Output:
system_time_zone | EST
time_zone        | SYSTEM
Run Code Online (Sandbox Code Playgroud)

我的问题是,对于夏令时开始和结束之间的日期,杰克逊返回的日期比数据库中存储的日期少一天。我假设这是因为杰克逊没有考虑夏令时。

我通过尝试在 Java 字段中将日期格式从 更改为yyyy-MM-dd来得出此结论。yyyy-MM-dd HH:mm:ss我注意到服务器返回了类似 的内容2017-03-13 00:00:00,但杰克逊将其序列化为2017-03-12 23:00:00(少一小时,这是 EST 时间受夏令时影响的时间)。

有什么办法可以克服这个问题吗?另外,java.sql.Date考虑到它没有时间对应项,使用的类型是否正确?我一直在考虑使用它java.time.LocalDate,但我还没有看到它是否会成功。

提前致谢!

Bas*_*que 5

太长了;博士

\n\n
    \n
  • 切勿使用java.sql.Date也不java.util.Date
    仅使用java.time类。
  • \n
  • 对于仅日期值,请使用LocalDate.
    您只会看到稳定的值,不受夏令时 (DST) 的影响。
  • \n
\n\n

例子:

\n\n
LocalDate.parse( "2019-01-23" )\n
Run Code Online (Sandbox Code Playgroud)\n\n

java.sql.Date不是日期

\n\n

我不确定问题到底是什么,但我怀疑这是由于您使用了可怕的java.sql.Date类,因此没有实际意义。

\n\n

该类java.sql.Date假装表示仅日期值,没有时间和时区然而,由于一些难以想象的糟糕设计决策,该类扩展了java.util.Date. 该java.util.Date课程确实有一个时间,并且采用 UTC。更令人困惑的是,java.util.Date在其源代码中隐藏了一个时区,而没有 getter 和 setter,因此它似乎还不存在,影响equals. 因此java.sql.Date,尽管它的名称如此,尽管其目的只是保留日期,但实际上确实时间设置为 UTC。因此,java.sql.Date在调整时间方面采取一些特技,这可能是您遇到的问题。这些遗留的日期时间类是一大堆热气腾腾的\xe2\x80\xa6 燕麦片。你永远不应该使用它们。

\n\n

引用该类的JavaDocjava.sql.Date

\n\n
\n

毫秒值的薄包装,允许 JDBC 将其识别为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00.000 GMT 以来经过的毫秒数。

\n\n

为了符合 SQL DATE 的定义,java.sql.Date 实例包装的毫秒值必须通过将实例所在的特定时区中的小时、分钟、秒和毫秒设置为零来“规范化”已关联的。

\n
\n\n

顺便说一句,java.sql.Timestamp这也是一团糟糕的烂摊子。它也笨拙地继承自java.util.Date,为纳秒添加了第二个小数秒。也避免此类,现在由java.time.Instantor取代java.time.OffsetDateTime。同样,java.sql.Time被替换为java.time.LocalTime.

\n\n

java.time.LocalDate确实是一次约会

\n\n

通过采用 JSR 310 和 JDBC 4.2,您可以使用现代业界领先的java.time类。到目前为止,Jackson 可能已更新为使用java.time。如果没有,请参阅此问题以获取用于处理 Jackson 中的java.time 的数据类型模块的链接。

\n\n

看起来您的输入字符串采用标准ISO 8601格式,YYYY-MM-DD。在解析/生成表示日期时间值的字符串时,java.time类默认使用标准格式。对于仅日期用途,LocalDate该类实际上是没有时间、也没有区域/偏移的日期。您将看到稳定的日期值,不受夏令时 (DST)的影响。

\n\n

解析。

\n\n
LocalDate ld = LocalDate.parse( "2019-01-23" ) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

店铺。

\n\n
myPreparedStatement.setObject( \xe2\x80\xa6 , ld ) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

取回。

\n\n
LocalDate ld = myResultSet.getObject( \xe2\x80\xa6 , LocalDate.class ) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

关于LocalDate

\n\n

该类LocalDate表示仅日期值,没有时间、时区相对于 UTC 的偏移量

\n\n

时区对于确定日期至关重要。对于任何特定时刻,全球各地的日期都会因地区而异。例如,法国巴黎午夜过后几分钟是新的一天,而Montr\xc3\xa9al Qu\xc3\xa9bec中仍然是 \xe2\x80\x9cyesterday\xe2\x80\x9d中仍然是 \xe2\x80\x9cyesterday\xe2\x80\x9d 。

\n\n

如果未指定时区,JVM 会隐式应用其当前的默认时区。该默认值可能随时更改(!),因此您的结果可能会有所不同。最好明确指定您想要/预期的时区作为参数。如果重要,请与您的用户确认该区域。

\n\n

以、、 或 等格式指定正确的时区名称。切勿使用 2-4 个字母的缩写,例如或 ,因为它们不是Continent/RegionAmerica/MontrealAfrica/CasablancaPacific/AucklandESTIST真正的时区,不是标准化的,甚至不是唯一的(!)。

\n\n
ZoneId z = ZoneId.of( "America/Montreal" ) ;  \nLocalDate today = LocalDate.now( z ) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您想使用 JVM\xe2\x80\x99s 当前默认时区,请询问它并作为参数传递。如果省略,代码读起来会变得不明确,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。

\n\n
ZoneId z = ZoneId.systemDefault() ;  // Get JVM\xe2\x80\x99s current default time zone.\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者指定一个日期。您可以用数字设置月份,1 月至 12 月的合理编号为 1-12。

\n\n
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ;  // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者,更好的是,使用Month预定义的枚举对象,一个对应一年中的每个月。提示:Month在整个代码库中使用这些对象而不仅仅是整数,可以使您的代码更加自文档化,确保值有效并提供类型安全Year对于&同上YearMonth

\n\n
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

关于java.time

\n\n

java.time框架内置于 Java 8 及更高版本中这些类取代了麻烦的旧遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

\n\n

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

\n\n

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

\n\n

您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*

\n\n

从哪里获取 java.time 类?

\n\n\n\n

ThreeTen -Extra项目通过附加类扩展了 java.time。该项目是 java.time 未来可能添加的内容的试验场。您可能会在这里找到一些有用的类,例如Interval、、、等等。YearWeekYearQuarter

\n