我需要能够将从存储在"datetime"字段中的MySQL数据库获取的数据转换为JavaZonedDateTime
对象。
ZonedDateTime dt = ZonedDateTime.ofInstant(rs.getTimestamp("Start").toInstant(), UTC_ZONE_ID)
我遇到的问题是toInstant()
将本地时间偏移量添加到我不需要的Timestamp
对象,因为日期时间已经以 UTC 格式存储在数据库中。 因此,当我运行以下代码时:
ZonedDateTime startDT =
ZonedDateTime.ofInstant(rs.getTimestamp("Start").toInstant(),Globals.LOCALZONEID);
System.out.println(rs.getTimestamp("start"));
System.out.println(rs.getTimestamp("start").toInstant());
我得到:
2017-06-08 13:15:00.0
2017-06-08T17:15:00Z
我需要时间部分保持不变。
我无法找到解决问题的任何明显解决方案,所以我在这里错过了一些东西吗?
Timestamp
&Instant
始终采用 UTC 格式
我遇到的问题是 .toInstant() 将本地时间偏移量添加到时间戳对象
不,它没有。
- 根据定义,
java.sql.Timestamp
采用 UTC
。 - 根据定义,
Instant
采用 UTC
。
两者都不能分配任何其他区域。
不要将代码堆积在一行中。将每个步骤分成单独的行,以便调试其值。
java.sql.Timestamp ts = rs.getTimestamp("Start") ; // Actually in UTC, but it's `toString` method applies JVM’s current default time zone while generating string.
Instant instant = ts.toInstant() ; // Same moment, also in UTC.
ZoneId z = ZoneId.of( "America/Montreal" ) ; // Or call your global var: `Globals.LOCALZONEID`.
ZonedDateTime zdt = instant.atZone( z ); // Same moment, same point on timeline, but with wall-clock time seen in a particular zone.
之后,您可能会看到问题(或非问题)。如果没有,请编辑您的问题以显示每个变量的调试值。
不要相信Timestamp::toString
重要提示:java.sql.Timestamp::toString
方法。该方法在生成字符串时应用 JVM 的当前默认时区。实际值始终采用 UTC 格式。避免这些麻烦的遗留类的众多原因之一。在您自己的计算机上运行以下代码示例,以查看默认时区对Timestamp
的文本表示形式的影响。
让我们对在 IdeOne.com 中实时运行的代码进行模拟。IdeOne.com 的 JVM 默认为 UTC/GMT,因此我们通过任意指定默认值Pacific/Auckland
来覆盖默认值。
Instant now = Instant.now() ; // Simulating fetching a `Timestamp` from database by using current moment in UTC.
TimeZone.setDefault( TimeZone.getTimeZone( "Pacific/Auckland" ) ) ;
ZoneId zoneIdDefault = ZoneId.systemDefault() ;
ZoneOffset zoneOffset = zoneIdDefault.getRules().getOffset( now ) ;
java.sql.Timestamp ts = java.sql.Timestamp.from( now ) ; // Actually in UTC, but it's `toString` method applies JVM’s current default time zone while generating string.
Instant instant = ts.toInstant() ; // Same moment, also in UTC.
ZoneId z = ZoneId.of( "America/Montreal" ) ; // Or call your global var: `Globals.LOCALZONEID`.
ZonedDateTime zdt = instant.atZone( z ); // Same moment, same point on timeline, but with wall-clock time seen in a particular zone.
当前默认时区:太平洋/奥克兰
当前默认偏移量从 UTC:太平洋/奥克兰 | 总秒数:43200
now.toString(): 2017-06-09T04:41:10.750Z
ts.toString(): 2017-06-09 16:41:10.75
instant.toString(): 2017-06-09T04:41:10.750Z
z.toString(): 美国/蒙特利尔
zdt.toString(): 2017-06-09T00:41:10.750-04:00[美国/蒙特利尔]
避免使用传统的日期时间课程
在java.time包之外找到的旧的日期时间类很麻烦,令人困惑,设计糟糕且有缺陷。尽可能避免使用它们。这包括java.sql.Timestamp
.
符合 JDBC 4.2 的驱动程序可以通过调用PreparedStatement::setObject
和ResultSet::getObject
直接寻址 java.time 类型。
myPreparedStatement.setObject( … , instant ) ;
。和。。。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
如果使用尚未更新到 JDBC 4.2 和 java.time 的 JDBC 驱动程序,请使用添加到旧类的新方法短暂转换为java.sql.Timestamp
:from ( Instant )
、toInstant() 等。但是,除了与数据库交换数据之外,还要在java.time对象中完成所有实际工作(业务逻辑)。
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
。和。。。
Instant instant = myResultSet.getTimestamp( … ).toInstant() ;
关于java.time
java.time框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧传统日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
Joda-Time项目现在处于维护模式,建议迁移到 java.time 类。
要了解更多信息,请参阅Oracle 教程。并搜索堆栈溢出以获取许多示例和解释。规范为 JSR 310。
您可以直接与数据库交换java.time对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。不需要字符串,不需要java.sql.*
类。
从哪里获得java.time类?
Java SE- 8、Java SE 9及更高版本
- 内置。
- 具有捆绑实现的标准 Java API 的一部分。
- Java 9添加了一些小功能和修复。
Java SE 6 和 Java SE 7 大部分 - java.time 功能在ThreeTen-Backport中向后移植到 Java 6 和 7。
Android- 更高版本的 java.time 类的 Android 捆绑实现。
- 对于早期的Android(<26),ThreeTenABP项目适应了ThreeTen-Backport(如上所述)。请参阅如何使用ThreeTenABP...。
ThreeTen-Extra项目通过额外的类扩展了java.time。这个项目是未来可能添加到java.time的试验场。你可以在这里找到一些有用的类,如Interval
、YearWeek
、YearQuarter
等。
像java.sql.Timestamp
这样的旧类在设计上有很多问题。经常引起混淆的一件事是,Timestamp.toString()
以 JVM 的时区打印时间,即使其中Timestamp
的时间只包含一个没有时区的时间点。具体来说,当一个Timestamp
等于 UTC 中的 2017-06-08T17:15:00Z 并且您在运行匹兹堡时间的计算机上打印它(每年的这个时候与 UTC 的偏移量为 -4:00)时,隐式调用Timestamp.toString()
,它读取 JVM 的时区并将时间打印为13:15:00.0
,只是因为宾夕法尼亚州的这个时间等于Timestamp
中的 UTC 时间。
因此,长话短说,不要担心,您的Timestamp
和Instant
都是正确的。
我认为这个代码片段回答了你的问题。 这将接收本地时区中的字符串,将其转换为 UTC,并将其存储在数据库中。
//Getting the LocalDateTime Objects from String values
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd kk:mm");
String txtStartTime = "2017-03-29 12:00";
LocalDateTime ldtStart = LocalDateTime.parse(txtStartTime, df);
//Convert to a ZonedDate Time in UTC
ZoneId zid = ZoneId.systemDefault();
ZonedDateTime zdtStart = ldtStart.atZone(zid);
System.out.println("Local Time: " + zdtStart);
ZonedDateTime utcStart = zdtStart.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println("Zoned time: " + utcStart);
ldtStart = utcStart.toLocalDateTime();
System.out.println("Zoned time with zone stripped:" + ldtStart);
//Create Timestamp values from Instants to update database
Timestamp startsqlts = Timestamp.valueOf(ldtStart); //this value can be inserted into database
System.out.println("Timestamp to be inserted: " +startsqlts);
//insertDB(startsqlts);