首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MySQL日期时间不是以UTC的形式存储,而是存储在服务器时区中。

MySQL日期时间不是以UTC的形式存储,而是存储在服务器时区中。
EN

Stack Overflow用户
提问于 2019-01-15 19:06:11
回答 1查看 2.2K关注 0票数 2

MySQL数据库:将Java存储到DATETIME类型的数据库列中,例如,

代码语言:javascript
复制
   Table Foo
   -----------------------
   id         |  time
   -----------------------
   bigint(20)   datetime

日期是

代码语言:javascript
复制
01/15/19 19:00:00 (Time Zone: UTC+1:00),

DateFormat df = new SimpleDateFormat('MM/dd/yy HH:mm:ss');
df.setTimeZone("Europe/Paris");   // UTC+1:00
Date date = df.parse("01/15/19 19:00:00");

PreparedStatement st = new PreparedStatement("insert into Foo (id, time) values (?, ?)";
st.setObject(1, 1);
st.setObject(2, date);
st.executeUpdate();  

它将被转换成

代码语言:javascript
复制
01/15/19 18:00:00 UTC

并存储在数据库中。

MySQL时区为SYSTEM (UTC-6:00)。JVM时区是UTC-6:00。两者都在同一台机器上运行。存储的值是01/15/19 12:00:00。为什么它存储在服务器时区(而不是UTC)?

SQL查询SELECT time FROM Foo;提供以下结果:

代码语言:javascript
复制
time
-------------------
2019-01-15 12:00:00

更改服务器时区不影响选择值。

代码语言:javascript
复制
mysql> set time_zone='+8:00';

select time from Foo;

    time
    -------------------
    2019-01-15 12:00:00
EN

回答 1

Stack Overflow用户

发布于 2019-01-15 20:47:58

tl;dr

使用现代的java.time类,不要使用java.util.Datejava.sql.Datejava.sql.Timestamp

代码语言:javascript
复制
myPreparedStatement                   // With JDBC 4.2, wa can directly exchange java.time objects with the database.
.setObject(                           // Pass a `OffsetDateTime` object representing the moment we want stored in the database.
    LocalDateTime.parse(              // Parse the input string lacking an indicator of offset or zone.
        "2019-01-19T19:00"            // When using strings in standard ISO 8601 format, no need to specify a formatting pattern.
    )                                 // Returns a `LocalDateTime`, an ambiguous value without real meaning.
    .atZone(                          // Apply a time zone to give meaning to the `LocalDateTime`. 
        ZoneId.of( "Europe/Paris" )   // Here we are saying "the 7 PM on that date as seen on the clock on the wall of someone standing in Paris France".
    )                                 // Returns a `ZonedDateTime` object.
    .toOffsetDateTime()               // Returns an `OffsetDateTime` object as demanded by the JDBC spec, stripping off the time zone to leave on the hours-minutes-seconds from UTC.
)

java.time

您使用的是几年前被java.time类取代的糟糕的日期时间类。尝试调试/理解遗留类是没有意义的,因为它们非常混乱。

LocalDateTime

将输入字符串解析为LocalDateTime,因为它缺少时区与世界协调时相抵的任何指示符。

尽可能地对日期时间文本使用标准的ISO 8601格式。对于有时间的日期,什么是YYYY DDTHH:MM:SS,其中T将日期部分与时间部分分开.

代码语言:javascript
复制
String input = "2019-01-19T19:00" ;
LocalDateTime ldt = LocalDateTime.parse( input ) ; // No need to specify formatting pattern when using standard ISO 8601 formats.

ZonedDateTime

LocalDateTime类也缺乏任何区域或偏移量的概念。所以这个类的对象不代表瞬间,也不是时间线上的一个点。它代表了全球不同时区大约26-27小时的潜在时刻。

您似乎知道这个日期和时间表示特定时区中的某个时刻。所以,应用一个时区,给我们模糊的LocalDateTime对象赋予意义。

Continent/Region格式指定Continent/Region,如America/MontrealAfrica/CasablancaPacific/Auckland。不要使用2-4字母的缩写,如ESTIST,因为它们不是真正的时区,不标准化,甚至不是唯一的(!)。

代码语言:javascript
复制
ZoneId z = ZoneId.of( "Europe/Paris" ) ;  

如果您想使用JVM当前的默认时区,请请求它并作为参数传递。如果省略,代码读起来就会变得模棱两可,我们不知道您是否打算使用默认值,或者您是否像许多程序员一样不知道这个问题。

代码语言:javascript
复制
ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

现在我们可以应用一个ZoneId来获得一个ZonedDateTime

代码语言:javascript
复制
ZonedDateTime zdt = ldt.atZone( z ) ;

OffsetDateTime

您的JDBC可能接受ZonedDateTime对象,但是JDBC4.2规范要求它接受一个OffsetDateTime。有什么关系呢?ZonedDateTimeOffsetDateTime都代表时间线上的某个时刻。一个抵消仅仅是几个小时-分钟-秒-或世界协调时后面。时区更重要。时区是过去、现在和未来对某一特定地区人民使用的偏移量变化的历史。所以时区总是比较好的。除了这里我们使用JDBC与数据库交换java.time对象之外,我们使用OffsetDateTime编写标准代码。

代码语言:javascript
复制
OffsetDateTime odt = zdt.toOffsetDateTime() ;  // Convert from moment with time zone to a moment with merely an offset-from-UTC.

现在我们可以把这一刻传递给你准备好的声明。

代码语言:javascript
复制
myPreparedStatement( … , odt ) ;  // Pass a `OffsetDateTime` moment as the value for a placeholder in the SQL of your prepared statement.

检索。

代码语言:javascript
复制
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

指定所需的时区。

代码语言:javascript
复制
ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;

在上面的几行中看到的odtzdt都代表着完全相同的同时时刻,时间线上的相同点。只有它们的挂钟时间是不同的,因为大多数数据库存储和检索UTC,而您希望看到巴黎时间。

关于java.time

http://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html框架内置到Java8和更高版本中。这些类取代了麻烦的旧遗赠日期时间类,如java.util.DateCalendarSimpleDateFormat

http://www.joda.org/joda-time/项目现在在维护模式中,建议迁移到java.time类。

要了解更多信息,请参见http://docs.oracle.com/javase/tutorial/datetime/TOC.html。并搜索堆栈溢出以获得许多示例和解释。规范是JSR 310

您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC 4.2。不需要字符串,也不需要java.sql.*类。

在哪里获得java.time类?

  • Java 8Java 9Java 10Java 11和更高版本--捆绑实现的标准Java的一部分。
    • Java 9添加了一些次要的特性和修复。

  • Java 6Java 7
    • 大多数java.time功能都是在http://www.threeten.org/threetenbp/中移植到Java6&7中的。

  • 安卓
    • 较晚版本的Android实现的java.time类。
    • 对于早期的Android (<26),https://github.com/JakeWharton/ThreeTenABP项目采用了http://www.threeten.org/threetenbp/ (上面提到的)。见http://stackoverflow.com/q/38922754/642706

三次-额外项目使用其他类扩展java.time。这个项目是将来可能加入java.time的试验场。您可以在这里找到一些有用的类,如IntervalYearWeekYearQuarter更多

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54205320

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档