使用 SimpleDateFormat 的日期解析不正确

Incorrect date parsing with SimpleDateFormat

我有一个 MySQL table 带有 date() 和 time() 列,例如

CREATE TABLE `vol` (
  `ID` bigint(20) unsigned NOT NULL,
  `DEPART_DATE_VOL` date NOT NULL,
  `DEPART_HEURE_VOL` time NOT NULL,
  `ARRIVEE_DATE_VOL` date NOT NULL,
  `ARRIVEE_HEURE_VOL` time NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我需要在 WHERE 子句中使用 date() 查询行,这样我就没有 datetime() 或时间戳(另外,我无法修改数据库)

在我的 DAO 中获取信息时,我连接日期和时间列来解析 Java 日期,例如:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
addon.setDateDepart(sdf.parse(rs.getString(DEPART_DATE_VOL) + " " + rs.getString(DEPART_HEURE_VOL)));
addon.setDateArrivee(sdf.parse(rs.getString(ARRIVEE_DATE_VOL) + " " + rs.getString(ARRIVEE_HEURE_VOL)));

出发日期为:

但到达日期:

TMP 解决方案

现在,我会坚持

addon.setDateDepart(new LocalDateTime(rs.getString(DEPART_DATE_VOL) + "T" + rs.getString(DEPART_HEURE_VOL)).toDate());

addon.setDateArrivee(new LocalDateTime(rs.getString(ARRIVEE_DATE_VOL) + "T" + rs.getString(ARRIVEE_HEURE_VOL)).toDate());

这似乎可行,尽管我不确定我是否会 运行 遇到时区问题...

时间部分尝试HH:mm:sshh用于"Hour in am/pm (1-12)",见javadoc.

  1. 在数据库中使用 timestamp 数据类型,而不是单独的日期和时间列。 MySQL timestamp 是 UTC,所以应该避免数据库端的任何时区问题。
  2. 使用 java.time,现代 Java 日期和时间 API。像Date这样的旧日期和时间类设计得很糟糕,尤其是SimpleDateFormat是出了名的麻烦,所以避免使用。
  3. 在您的 Java 程序和数据库之间传输日期时间对象,而不是字符串。

阅读更多内容 in this question: Java Best Practice for Date Manipulation/Storage for Geographically Diverse Users。如果你做不到这一切,那就尽力而为。

详情

如果您可以在数据库中存储时间戳,则可以将它们检索为 java.time.Instant:

Instant departVol = rs.getObject(DEPART_INSTANT_VOL, Instant.class);

如果您无法更改 addon.setDateDepart() 接受的类型,请像这样转换为 Date

addon.setDateDepart(Date.from(departVol));

或者在使用 ThreeTen Backport 的版本中,java.time 到 Java 6 和 7 的 backport:

addon.setDateDepart(DateTimeUtils.toDate(departVol));

如果您不能更改数据库设计,您仍然可以从中检索 java.time 个对象:

LocalDate departDateVol = rs.getObject(DEPART_DATE_VOL, LocalDate.class);
LocalTime departHeureVol = rs.getObject(DEPART_HEURE_VOL, LocalTime.class);
LocalDateTime departDateHeureVol = LocalDateTime.of(departDateVol, departHeureVol);

转换为老式 Date 是不稳定的,因为如您所说,可能存在时区问题。尝试:

addon.setDateDepart(
        Date.from(departDateHeureVol.atZone(ZoneId.systemDefault()).toInstant()));

如果这里似乎存在时区问题,您需要指定正确的时区而不是 ZoneId.systemDefault()

如果您的 Java 版本或 JDBC 驱动程序不支持从结果集中获取正确的日期时间对象,则使用现代 类 可以直接解析字符串:

LocalDate departDateVol = LocalDate.parse(rs.getString(DEPART_DATE_VOL);
LocalTime departHeureVol = LocalTime.parse(rs.getString(DEPART_HEURE_VOL);

使用这些对象继续上述操作。

PS 我还没有测试我的代码片段。如有错别字,无法自行修改,请回复。

你的代码出了什么问题?

格式模式字符串中的小写字母 hh 表示上午或下午的小时,从 1 到 12。所以 12:20:00 确实 意思是 00:20:00。要在 24 小时制上解释 12,请使用大写字母 HH 表示从 0 到 23 的一天中的小时。

Link: ThreeTen backport Home