简体   繁体   中英

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

Mysql database: store java Date into database column of datetime type, eg,

    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). JVM time zone is UTC-6:00. Both are running in the same machine. The value stored is 01/15/19 12:00:00. Why is it stored in Server time zone (not UTC)?

select time from 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

Use modern java.time classes, never java.util.Date or java.sql.Date or java.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

You are using terrible date-time classes that were supplanted years ago by the java.time classes. 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 .

Make a habit of using the standard ISO 8601 formats for date-time text whenever possible. 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.

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. 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.

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.

Specify a proper time zone name in the format of Continent/Region , such as America/Montreal , Africa/Casablanca , or Pacific/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(!).

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. 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 .

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 . What's the difference? Both ZonedDateTime and OffsetDateTime represent a moment, a point on the timeline. An offset is merely a number of hours-minutes-seconds ahead-of or behind 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.

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. Only their wall-clock time is different, as most databases store and retrieve UTC while you want to see Paris time.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat .

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.

To learn more, see the Oracle Tutorial . And search Stack Overflow for many examples and explanations. Specification is JSR 310 .

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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