我需要能够转换从MySQL数据库获取的数据,该数据库存储在" datetime"将字段转换为Java ZonedDateTime
对象。
ZonedDateTime dt = ZonedDateTime.ofInstant(rs.getTimestamp("Start").toInstant(), UTC_ZONE_ID)
我遇到的问题是,toInstant()
将{i} 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
我需要时间组件保持不变。
我无法找到问题的任何明显解决方案,所以我在这里遗漏了什么?
答案 0 :(得分:13)
Timestamp
& Instant
始终为UTC 我遇到的问题是.toInstant()向Timestamp对象添加本地时间偏移量
不,不。
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
的文本表示的影响。
让我们对code running live in 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():America / Montreal
zdt.toString():2017-06-09T00:41:10.750-04:00 [美国/蒙特利尔]
在java.time包之外找到的旧日期时间类很麻烦,令人困惑,设计糟糕,有缺陷。尽可能避免使用它们。这包括java.sql.Timestamp
。
您的JDBC 4.2兼容driver可以通过调用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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:2)
像java.sql.Timestamp
这样的旧类在设计上存在很多问题。经常引起混淆的事情之一是Timestamp.toString()
在JVM的时区中打印时间,即使其中的时间Timestamp
只保留一个没有时区的时间点。具体来说,当Timestamp
等于2017-06-08T17:15:00Z(UTC)时,您将在运行匹兹堡时间的计算机上打印(在一年的这个时候,它的偏移量为-4:00)从UTC),隐式调用Timestamp.toString()
,它读取JVM的时区并将时间打印为13:15:00.0
,因为宾夕法尼亚州的这个时间等于Timestamp
中的UTC时间。
所以简而言之,请不要担心,Timestamp
和Instant
都是正确的。
答案 2 :(得分:0)
我认为此代码段可以回答您的问题。这将在本地时区中接收String,将其转换为UTC,并将其存储在db中。
//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);