在Java / MySQL中没有时间或时区组件的日期

时间:2008-11-12 22:08:15

标签: java sql timezone

我需要能够存储没有时间组件的日期(年/月/日)。这是一个日期的抽象概念,例如生日 - 我需要代表一年中的日期而不是特定的时刻。

我使用Java从一些输入文本中解析日期,并且需要存储在MySQL数据库中。无论数据库,应用程序或任何客户端处于什么时区,他们都应该看到相同的年/月/日。

我的应用程序将在具有与数据库服务器不同的系统时区的计算机上运行,​​而我无法控制。有没有人有一个优雅的解决方案来确保我正确存储日期?

我可以想到这些解决方案,这些解决方案看起来都不是很好:

  • 查询我的MySQL连接的时区并解析该时区的输入日期
  • 完全以字符串yyyy-MM-dd
  • 处理日期

6 个答案:

答案 0 :(得分:5)

java.time

在 JSR-310 并入 Java SE 8 之前,这确实是 java.util 日期时间 API 的问题。java.util.Date 对象不是像 {{3} };相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即 January 1, 1970, 00:00:00 GMT(或 UTC)。当您打印 java.util.Date 的对象时,它的 toString 方法返回 JVM 时区中的日期时间,根据该毫秒值计算得出。

您描述的问题已通过 modern date-time types* 解决,其中 LocalDate 仅代表一个日期。在下面的句子中,modern date-time API 很好地描述了它的用途:

<块引用>

例如,您可以使用 LocalDate 对象来表示出生 日期,因为大多数人在同一天庆祝他们的生日, 无论他们是在他们的出生城市还是在地球另一端 国际日期变更线的一侧。

Oracle tutorial,它再次描述如下:

<块引用>

LocalDate 代表 ISO 日历中的年-月-日,并且是 用于表示没有时间的日期。你可能会使用一个 LocalDate 跟踪重要事件,例如出生日期或 结婚日期。

我应该为 LocalDate 使用哪种数据类型?

它使用 DATE ANSI SQL 类型映射。 ANSI SQL 类型与 java.time 类型的映射在 A couple of pages later 中描述如下:

<头>
ANSI SQL Java SE 8
日期 本地日期
时间 本地时间
时间戳 本地日期时间
TIME WITH TIMEZONE 偏移时间
带时区的时间戳 偏移日期时间

在JDBC中如何使用?

下面给出了将 LocalDate 插入 columnfooDATE 类型)的示例代码:

LocalDate localDate = LocalDate.now();
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, localDate);
st.executeUpdate();
st.close();

下面给出了从 LocalDate 检索 columnfoo 的示例代码:

Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE <some condition>");
while (rs.next()) {
    // Assuming the column index of columnfoo is 1
    LocalDate localDate = rs.getObject(1, LocalDate.class));
    System.out.println(localDate);
}
rs.close();
st.close();

this Oracle's article 了解有关 modern date-time API* 的更多信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 Trail: Date Time,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 ThreeTen-BackportJava 8+ APIs available through desugaring

答案 1 :(得分:2)

您可以将所有时间/时区内容归零:

public static Date truncateDate(Date date)
    {
        GregorianCalendar cal = getGregorianCalendar();
        cal.set(Calendar.ZONE_OFFSET, 0); // UTC
        cal.set(Calendar.DST_OFFSET, 0); // We don't want DST to get in the way.

        cal.setTime(date);
        cal.set(Calendar.MILLISECOND, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.AM_PM, Calendar.AM);

        return cal.getTime();
    }

答案 2 :(得分:2)

我得出结论,我当前应用程序中的最佳方法(直接使用jdbc的简单实用程序)是直接插入字符串。对于更大的Hibernate应用程序,我可能懒得编写自己的用户类型。不能相信某人还没有在一些公开的代码中解决这个问题,但是......

答案 3 :(得分:0)

难道你不能在表中使用MySQL DATE类型,然后在insert语句中使用格式化的字符串吗?我认为这样的事情会避免任何时区调整。

INSERT INTO time_table(dt) VALUES('2008-12-31')

答案 4 :(得分:0)

由于您指定服务器和数据库位于不同的时区,因此在我看来您的解释存在问题。如果您将日期清零,或存储为MM-dd-YYYY,这相同,那么您是存储服务器日期还是数据库日期?如果是服务器上的11:00 PM和db上的第二天凌晨1点,哪个日期是“正确的”?当您查看一年后的日期时,您会记得日期所依赖的机器时区吗?

这些考虑因素可能有些过分。但是为什么不把日期存储在GMT中(如果你愿意,可以将秒数归零)?

答案 5 :(得分:0)

由于这个原因,类java.sql.Date完全存在,遗憾的是它使用起来非常不方便,因为它只是扩展java.util.Date并且您手动必须将时间部分设置为零。