English 中文(简体)
Java/MySQL中没有时间或时区组件的日期
原标题:
  • 时间:2008-11-12 22:08:15
  •  标签:

我需要能够存储一个没有时间组成部分的日期(年/月/日)。这是一个日期的抽象概念,比如生日-我需要表示一年中的某一天,而不是特定的时间点。

我正在使用Java解析来自某些输入文本的日期,并需要存储到MySQL数据库中。无论数据库、应用程序或任何客户端处于哪个时区,它们都应该看到相同的年/月/日。

我的应用程序将在一个具有不同系统时区的机器上运行,而数据库服务器也是如此,我对两者都没有控制权。有没有一个优雅的解决方案来确保我正确存储日期?

我能想出这些解决方案,但似乎都不太好:

  • Query my MySQL connection for its timezone and parse the input date in that timezone
  • Process the date entirely as a string yyyy-MM-dd
最佳回答

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

问题回答

java.time时间

直到JSR-310被纳入Java SE 8之前,这确实是java.util日期时间API的一个问题。 java.util.Date对象不像现代日期时间类型那样是真正的日期时间对象。相反,它表示自“纪元”以来的毫秒数,即 1970年1月1日00:00:00 GMT (或UTC)的标准基准时间。当您打印java.util.Date对象时,它的toString方法返回JVM的时区中的日期时间,从这个毫秒值计算得出。

您所描述的问题已经得到使用现代日期-时间API*解决,其中我们拥有表示日期的LocalDate。在下文中,Oracle教程很好地描述了它的目的:

For example, you might use a LocalDate object to represent a birth date, because most people observe their birthday on the same day, whether they are in their birth city or across the globe on the other side of the international date line.

几页后,它再次描述如下:

A LocalDate represents a year-month-day in the ISO calendar and is useful for representing a date without a time. You might use a LocalDate to track a significant event, such as a birth date or wedding date.

Which datatype should I use for LocalDate?

它与DATE ANSI SQL类型进行映射。在此Oracle文章中,已经将ANSI SQL类型与java.time类型的映射描述如下:

ANSI SQL Java SE 8
DATE LocalDate
TIME LocalTime
TIMESTAMP LocalDateTime
TIME WITH TIMEZONE OffsetTime
TIMESTAMP WITH TIMEZONE OffsetDateTime

How to use it in JDBC?

以下是一个示例代码,用于将 LocalDate 插入 columnfoo (它是 DATE 类型):

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

下面是一个示例代码,用于从columnfoo中检索LocalDate

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

Trail: Date Time 了解关于 现代日期时间 API* 的更多信息。


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

您可以将所有时间/时区设置归零:

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

你不可以在你的表中使用MySQL日期类型然后在插入语句中基本上使用格式化的字符串吗? 我认为这样的做法会避免任何时区调整。

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

由于您指定服务器和数据库位于不同的时区,我认为您存在解释上的问题。如果您将日期归零或按MM-dd-YYYY存储,这意味着您正在存储服务器日期还是数据库日期?如果服务器上是晚上11点,而在数据库上是第二天凌晨1点,哪个日期是“正确”的?在一年后查看日期时,您会记得日期取决于哪台机器的时区吗?

这些考虑可能有点过头了。但为什么不在格林威治标准时间存储日期(如果需要可以将秒清零)?

java.sql.Date类存在的原因就是为了这个,但不幸的是,它非常不方便使用,因为它只是继承了java.util.Date,你必须手动将时间部分设置为零。





相关问题
热门标签