Java SQL日期时间是什么?

16

当我将一个 SQL DateTime 插入数据库时,得到的结果是 2007-02-07 12:00:00.00

但我制作的 Date 对象如下: 2007-02-07 17:29:46.00

如何获取数据库中的秒值。它总是将其更改回 12:00:00.00

date.setYear(Integer.valueOf(parsedDate[2].replaceAll(" ", "")) - 1900);
date.setMonth(Integer.valueOf(parsedDate[0].replaceAll(" ", "")));
date.setDate(Integer.valueOf(parsedDate[1].replaceAll(" ", "")));
...
java.sql.Date sqlDate = new java.sql.Date(date.getTime());

我需要使用任何格式化程序吗?


1
一个“日期”(表示某个时间点的数字)与其格式无关(相同的数字可以表示为“2007-02-07”、“2007-07-02”或“2/7/2007”)。它也与您的时区无关(同样的数字可以在洛杉矶是“2/7/2007”,而在伦敦是“2/8/2007”)。最后,“日期”(例如“2/7/2007”)与日期时间(例如“2/7/2007 09:15am”)具有不同的值。我猜你可能在使用“日期”,但你想使用“日期时间”。 - paulsm4
请注意:java.util.date可以表示日期或日期时间,而java.sql.date仅表示日期。对于像MS Sql Server这样的数据库,“date”或“datetime”与“timestamp”是不同的。https://dev59.com/MHE95IYBdhLWcg3wdtmj - paulsm4
PS:在 MS Sql Server 中(如果您正在使用它),“时间戳”绝对不是日期或日期时间:http://www.sqlteam.com/article/timestamps-vs-datetime-data-types。如果您想要一个日期(无论一天中的时间如何),请将SQL列设置为“日期”。如果您想要一天中的时间(无论日历日期如何),请将其设置为“时间”。绝对不要使用 SQL Server 的“时间戳”来保存逻辑时间/日期列。 - paulsm4
那么我不能将日期和时间保存在上述提到的同一列中,例如2007-02-07 17:29:46.00吗?我必须将它们分成两列吗?顺便说一下,我正在使用MS SQL Server。 - Elbek
不要这样!这意味着,如果你想保存“2007-02-07 17:29”,那么你应该在MSSQL中将列定义为“datetime”。如果你只想保存“2007-02-07”,那么你应该将列定义为MSSQL“date”。Java的“java.sql.Date”只是日期。Java的“java.util.Date”既可以处理日期,也可以处理日期/时间。而MSSQL的“timestamp”既不是日期也不是时间 - 除非你知道你需要它,否则避免使用MSSQL的时间戳。问:你正在使用MS Sql Server吗?还是你正在使用其他数据库(例如DB2或MySQL)? - paulsm4
显示剩余2条评论
2个回答

31

java.sql.Date代表的是日期而非日期和时间。引用文档中的一句话:

为了符合 SQL DATE 的定义,java.sql.Date 实例包装的毫秒值必须通过将时、分、秒和毫秒设置为与实例相关联的特定时区中的零来“规范化”。

如果您想要存储日期和时间,应该寻找其他类型 - 例如java.sql.Timestamp。编辑:这并不建议您使用 TIMESTAMP 列类型 - 如评论中的 paulsm4 所说,那是一种不同的东西。然而,据我所见,JDBC 只支持:

  • Date(没有时间)
  • Time(没有日期)
  • Timestamp(包含日期和时间,但您不需要 TIMESTAMP SQL 语义)

我认为如果使用 Java 中的 Timestamp 类型与 DATETIME 列一起使用,它可以正常运行,尽管精度不如 Timestamp 高。

编辑:经过更多的研究,看起来您可能需要使用 java.sql.Time 类型,但需要特殊的驱动程序参数 - 至少如果您使用的是 Microsoft 驱动程序。请参阅有关配置 JDBC的文档以获取更多信息。


当我使用时间戳时,出现了以下错误:无法将显式值插入时间戳列。请使用带有列列表的INSERT来排除时间戳列,或在时间戳列中插入DEFAULT。 - Elbek
@elbek:你应该尝试使用DATETIME列,但Java类型应该是java.sql.Timestamp。 - Jon Skeet
@elbek:你必须开始更加精准。每当出现错误时,请告诉我们具体是哪个错误。 - Jon Skeet
1
@elbek:“JDBC驱动程序专门设计用于使用SQL Server 2005引入的功能,但它向后兼容SQL Server 2000,包括64位版本。JDBC Driver 3.0支持SQL Server 2008中的新日期和时间类型、大型用户定义类型以及稀疏列。”(来自3.0驱动程序的系统要求。) - Jon Skeet
在使用java.sql.Date对象时,这个技巧对我很有用:new Timestamp(date.getTime())。 - ArcanisCz
显示剩余10条评论

3

简短总结

你可能会感到困惑,因为你不理解java.util.Date是一个带有时间的类型,而它的子类java.sql.Date 假装成为一个仅具有日期的类,但实际上它的时间被设置为零。非常糟糕的设计。完全避免使用这两个类。只使用java.time类。

对于数据库中的仅日期列,请将该列定义为SQL标准的DATE类型。

myPreparedStatement.setObject(
    … ,
    LocalDateTime.parse( "2007-02-07 17:29:46.00".replace( " " , "T" ) )
    .toLocalDate()
)

java.time

现代方法使用Java 8及更高版本中添加的java.time类。

当我将SQL DateTime插入数据库时,我得到2007-02-07 12:00:00.00。

并不存在SQL标准类型为DateTime,在Java中也没有这样的类。所以我不知道您的意图是什么。

至于输入字符串2007-02-07 17:29:46.00,请将其解析为LocalDateTime,因为它没有任何时区指示器或UTC偏移量。

那种SQL样式的格式几乎符合ISO 8601标准。要完全符合,请用T替换中间的空格。java.time类在解析/生成字符串时默认使用ISO 8601格式。

String input = "2007-02-07 17:29:46.00".replace( " " , "T" ) ;

解析。

LocalDateTime ldt = LocalDateTime.parse( input ) ;

LocalDateTime 不代表一个时刻,也不是时间轴上的一个点。它代表了大约26-27小时范围内的潜在时刻。

标准SQL提供了一个数据类型来表示这样的值,TIMESTAMP WITHOUT TIME ZONE

智能对象,而不是愚蠢的字符串

你的整个方法都是错误的,纠缠文本并使用旧的日期时间类。相反,使用 java.time 对象进行交换。

从JDBC 4.2开始,您无需再使用麻烦的旧java.sql类型,如java.sql.Datejava.sql.Timestamp。您可以通过 setObject/getObject 方法直接与数据库交换 java.time 对象。

myPreparedStatement.setObject( … , ldt ) ;

和检索。

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

如果您想使用仅限日期的值,请使用SQL标准类型DATE和Java类LocalDate
LocalDate ld = ldt.toLocalDate() ;
myPreparedStatement.setObject( … , ld ) ;

如何获取数据库中的秒值
不确定您所说的“秒值”是什么意思。
也许您想要从1970年第一时刻(UTC)的纪元参考开始计算秒数。
long secondsSinceEpoch = ldt.toEpochSecond() ;

如果您的目标仅仅是实例化一个java.sql.Date,那就不必费心了。永远不要再使用那个类了。但是,顺便说一句,您特定的问题可能是该类采用可怕设计的副作用。 java.sql.Date类继承自带有时间的日期类型java.util.Datejava.sql.Date假装成为只有日期的值,但实际上将其时间设置为00:00:00。更糟糕的是,文档告诉我们忽略它是子类的事实。不要费心去理解它; 直接使用java.time即可。
如果您想单独处理时间,请提取LocalTime对象。
LocalTime lt = ldt.toLocalTime() ;

如果你想将时间设置为零,则可能需要一个仅包含日期的值。如果是这样,请使用LocalDate类获取不带时间和时区的日期值。
LocalDate ld = ldt.toLocalDate() :

如果你想获取该日期的第一个时刻,调用LocalDate::atStartOfDay
LocalDateTime ldtStart = ldt.toLocalDate().atStartOfDay() ;

注意:如果你想要追踪实际时刻,时间轴上的特定点,则上述所有代码都是错误的。请搜索Stack Overflow了解InstantZoneIdZonedDateTime类。同时搜索Stack Overflow和dba.StackExchange.com了解SQL标准类型TIMESTAMP WITH TIME ZONE


关于 java.time

java.time 框架已内置于 Java 8 及以上版本。这些类取代了旧的 legacy 日期和时间类,如 java.util.DateCalendarSimpleDateFormat 等。

Joda-Time 项目现在处于 维护模式,建议迁移到 java.time 类。

要了解更多信息,请参阅 Oracle 教程。并搜索 Stack Overflow 获取许多示例和解释。规范是 JSR 310

您可以直接与数据库交换 java.time 对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。无需字符串,无需 java.sql.* 类。

从哪里获取 java.time 类?

ThreeTen-Extra项目通过添加额外的类扩展了java.time。该项目是java.time可能未来增加内容的试验场。您可以在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter,以及更多


关于Joda建议迁移到java.time,以下是他们网站上的一句话: “请注意,从Java SE 8开始,用户被要求迁移到java.time(JSR-310)- JDK的核心部分,取代了该项目。” - Danny Bullis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接