使用 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)));
出发日期为:
- 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());
这似乎可行,尽管我不确定我是否会 运行 遇到时区问题...
时间部分尝试HH:mm:ss
。 hh
用于"Hour in am/pm (1-12)",见javadoc.
- 在数据库中使用
timestamp
数据类型,而不是单独的日期和时间列。 MySQL timestamp
是 UTC,所以应该避免数据库端的任何时区问题。
- 使用
java.time
,现代 Java 日期和时间 API。像Date
这样的旧日期和时间类设计得很糟糕,尤其是SimpleDateFormat
是出了名的麻烦,所以避免使用。
- 在您的 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
我有一个 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)));
出发日期为:
- 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());
这似乎可行,尽管我不确定我是否会 运行 遇到时区问题...
时间部分尝试HH:mm:ss
。 hh
用于"Hour in am/pm (1-12)",见javadoc.
- 在数据库中使用
timestamp
数据类型,而不是单独的日期和时间列。 MySQLtimestamp
是 UTC,所以应该避免数据库端的任何时区问题。 - 使用
java.time
,现代 Java 日期和时间 API。像Date
这样的旧日期和时间类设计得很糟糕,尤其是SimpleDateFormat
是出了名的麻烦,所以避免使用。 - 在您的 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