简体   繁体   English

MySql datetime不存储为UTC,而是存储在服务器时区中

[英]MySql datetime is not stored as UTC, but in server time zone

Mysql database: store java Date into database column of datetime type, eg, Mysql数据库:将Java Date存储到datetime类型的数据库列中,例如,

    Table Foo
    id         time
   ---------------------
   bigint(20)   datetime

The date is 日期是

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

which is expected to be converted into 预计将被转换成

01/15/19 18:00:00 UTC

and stored in database. 并存储在数据库中。

MySql time zone is SYSTEM (UTC-6:00). MySql时区是SYSTEM(UTC-6:00)。 JVM time zone is UTC-6:00. JVM时区为UTC-6:00。 Both are running in the same machine. 两者都在同一台计算机上运行。 The value stored is 01/15/19 12:00:00. 存储的值为01/15/19 12:00:00。 Why is it stored in Server time zone (not UTC)? 为什么将其存储在服务器时区(而不是UTC)中?

select time from Foo; 从Foo中选择时间;

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

Changing server time zone did not affect the select value. 更改服务器时区不会影响选择值。

mysql> set time_zone='+8:00';

select time from Foo;

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

tl;dr TL;博士

Use modern java.time classes, never java.util.Date or java.sql.Date or java.sql.Timestamp . 使用现代的java.time类,切勿使用java.util.Datejava.sql.Datejava.sql.Timestamp

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

You are using terrible date-time classes that were supplanted years ago by the java.time classes. 您使用的是可怕的日期时间类,而该类早在几年前就被java.time类所取代。 There is really no point in trying to debug/understand the legacy classes, as they are bloody awful mess. 尝试调试/理解遗留类确实没有任何意义,因为遗留的类很糟糕。

LocalDateTime

Parse your input string as a LocalDateTime as it lacks any indicator of time zone or offset-from-UTC . 将您的输入字符串解析为LocalDateTime因为它缺少任何时区从UTC偏移的指示。

Make a habit of using the standard ISO 8601 formats for date-time text whenever possible. 养成尽可能在日期时间文本中使用标准ISO 8601格式的习惯。 For a date with time-of-day, what would be YYYY-MM-DDTHH:MM:SS where the T separates the date-portion from the time-portion. 对于带有日期的日期,则为YYYY-MM-DDTHH:MM:SS,其中T将日期部分与时间部分分开。

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

ZonedDateTime

The LocalDateTime class too lacks any concept of zone or offset. LocalDateTime类也缺少区域或偏移量的任何概念。 So an object of this class does not represent a moment, is not a point on the timeline. 因此,此类的对象不能表示时刻, 也不是时间轴上的一点。 It represents potential moments along a range of about 26-27 hours, the various time zones around the globe. 它代表了全球各个时区大约26-27小时的潜在时刻。

You seem to know for certain this date and time were meant to represent a moment in a particular time zone. 您似乎肯定知道此日期和时间旨在表示特定时区中的时刻。 So apply a time zone, to give meaning to our ambiguous LocalDateTime object. 因此,请应用一个时区,以使我们含糊的LocalDateTime对象有意义。

Specify a proper time zone name in the format of Continent/Region , such as America/Montreal , Africa/Casablanca , or Pacific/Auckland . Continent/Region的格式指定正确的时区名称 ,例如America/MontrealAfrica/CasablancaPacific/Auckland Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!). 切勿使用2-4字母的缩写,例如ESTIST因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Europe/Paris" ) ;  

If you want to use the JVM's current default time zone, ask for it and pass as an argument. 如果要使用JVM的当前默认时区,请提出要求并作为参数传递。 If omitted, the code becomes ambiguous to read in that we do not know for certain if you intended to use the default or if you, like so many programmers, were unaware of the issue. 如果省略,代码将变得难以理解,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样不知道该问题。

ZoneId z = ZoneId.systemDefault() ;  // Get JVM’s current default time zone.

Now we can apply a ZoneId to get a ZonedDateTime . 现在我们可以应用ZoneId来获取ZonedDateTime

ZonedDateTime zdt = ldt.atZone( z ) ;

OffsetDateTime

Your JDBC may be accept the ZonedDateTime object, but the JDBC 4.2 spec requires that it take an OffsetDateTime . 您的JDBC 可以接受ZonedDateTime对象,但是JDBC 4.2规范要求它采用OffsetDateTime What's the difference? 有什么不同? Both ZonedDateTime and OffsetDateTime represent a moment, a point on the timeline. ZonedDateTimeOffsetDateTime表示时刻,即时间轴上的一点。 An offset is merely a number of hours-minutes-seconds ahead-of or behind UTC. 偏移量只是UTC之前或之后的数小时-数分钟-秒。 A time zone is much more. 时区更多。 A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. 时区是特定区域的人们过去,现在和将来对偏移量的更改的历史记录。 So a time zone is always preferable. 因此,时区始终是可取的。 Except here, where we are using JDBC to exchange a java.time object with the database, we use OffsetDateTime to write standard code. 除了这里,在我们使用JDBC与数据库交换java.time对象的地方,我们使用OffsetDateTime编写标准代码。

OffsetDateTime odt = zdt.toOffsetDateTime() ;  // Convert from moment with time zone to a moment with merely an offset-from-UTC.

Now we can pass this moment to your prepared statement. 现在,我们可以将这一刻传递给您准备好的声明。

myPreparedStatement( … , odt ) ;  // Pass a `OffsetDateTime` moment as the value for a placeholder in the SQL of your prepared statement.

Retrieval. 恢复。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Assign the desired time zone. 分配所需的时区。

ZoneId z = ZoneId.of( "Europe/Paris" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;

Both the odt and zdt seen in the few lines above represent the very same simultaneous moment, the same point on the timeline. 在上面几行中看到的odtzdt都表示相同的同时时刻,时间轴上的相同点。 Only their wall-clock time is different, as most databases store and retrieve UTC while you want to see Paris time. 只有它们的挂钟时间有所不同,因为大多数数据库在您想查看巴黎时间时都存储和检索UTC。


About java.time 关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于Java 8及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat . 这些类取代了麻烦的旧的旧式日期时间类,例如java.util.DateCalendarSimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes. 现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial . 要了解更多信息,请参见Oracle教程 And search Stack Overflow for many examples and explanations. 并在Stack Overflow中搜索许多示例和说明。 Specification is JSR 310 . 规格为JSR 310

You may exchange java.time objects directly with your database. 您可以直接与数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later. 使用与JDBC 4.2或更高版本兼容的JDBC驱动程序 No need for strings, no need for java.sql.* classes. 不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes? 在哪里获取java.time类?

The ThreeTen-Extra project extends java.time with additional classes. ThreeTen-Extra项目使用其他类扩展了java.time。 This project is a proving ground for possible future additions to java.time. 该项目为将来可能在java.time中添加内容提供了一个试验场。 You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more . 您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM