【问题标题】:Incorrect date parsing with SimpleDateFormat使用 SimpleDateFormat 进行的日期解析不正确
【发布时间】:2018-02-14 14:27:58
【问题描述】:

我有一个 MySQL 表,其中包含 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)));

对于有出发日期的一行:

  • 2018-02-26 10:50:00 生成的 Java 日期正确

但是对于到达日期:

  • 2018-02-26 12:20:00 生成的 Java 日期设置为 2018-02-26T00:20:00

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());

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

【问题讨论】:

  • 我建议你避免使用SimpleDateFormat 类。它不仅过时了,而且出了名的麻烦。今天我们在java.time, the modern Java date and time API 中做得更好。
  • 您使用的是hh,它适用于 12 小时制时钟。使用 kk 代替 24 小时制。但是是的,您应该尝试改用java.time。 :)
  • 在您的解决方法中,您使用的是 LocalDateTime — 来自 Joda-Time?这已经比旧的 Java 日期和时间类好。
  • 很难理解为什么会有这样的行为。我也很好奇。我想到的是同步,尽管您的代码中没有多个线程(应该归咎于 DAO?)。 日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一个格式,它必须在外部同步。我真的很想知道你的例子中是否有这种情况。
  • 使用 Java 8 或更高版本以及兼容 JDBC 4.2 的 JDBC 驱动程序,您应该能够做到LocalDateTime.of(rs.getObject(DEPART_DATE_VOL, LocalDate.class), rs.getObject(DEPART_HEURE_VOL, LocalTime.class))

标签: java date simpledateformat


【解决方案1】:

在时间部分尝试HH:mm:sshh 用于“上午/下午 (1-12) 小时”,请参阅 javadoc

【讨论】:

  • 当然!像往常一样,跳过最明显的:/
  • @luso 最后,你最好使用智能对象而不是哑字符串。有关现代解决方案,请参阅 other Answer, by Ole V.V.
【解决方案2】:
  1. 在您的数据库中使用timestamp 数据类型,而不是单独的日期和时间列。 MySQL timestamp 采用 UTC,因此应避免数据库端出现任何时区问题。
  2. 使用现代 Java 日期和时间 API java.time。像 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 的 backport 到 Java 6 和 7:

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 我还没有测试我的代码 sn-ps。如果有错字且您无法自行修复,请还原。

你的代码出了什么问题?

kevmo314 is correct 格式模式字符串中的小写 hh 表示从 1 到 12 的 AM 或 PM 小时。所以 12:20:00 确实 表示 00:20:00。要以 24 小时制解释 12,请使用大写的 HH 表示一天中从 0 到 23 的小时。

链接: ThreeTen backport Home

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-23
    • 2012-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-28
    相关资源
    最近更新 更多