簡體   English   中英

MySql datetime不存儲為UTC,而是存儲在服務器時區中

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

Mysql數據庫:將Java Date存儲到datetime類型的數據庫列中,例如,

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

日期是

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

預計將被轉換成

01/15/19 18:00:00 UTC

並存儲在數據庫中。

MySql時區是SYSTEM(UTC-6:00)。 JVM時區為UTC-6:00。 兩者都在同一台計算機上運行。 存儲的值為01/15/19 12:00:00。 為什么將其存儲在服務器時區(而不是UTC)中?

從Foo中選擇時間;

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

更改服務器時區不會影響選擇值。

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

select time from Foo;

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

TL;博士

使用現代的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類所取代。 嘗試調試/理解遺留類確實沒有任何意義,因為遺留的類很糟糕。

LocalDateTime

將您的輸入字符串解析為LocalDateTime因為它缺少任何時區從UTC偏移的指示。

養成盡可能在日期時間文本中使用標准ISO 8601格式的習慣。 對於帶有日期的日期,則為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

LocalDateTime類也缺少區域或偏移量的任何概念。 因此,此類的對象不能表示時刻, 也不是時間軸上的一點。 它代表了全球各個時區大約26-27小時的潛在時刻。

您似乎肯定知道此日期和時間旨在表示特定時區中的時刻。 因此,請應用一個時區,以使我們含糊的LocalDateTime對象有意義。

Continent/Region的格式指定正確的時區名稱 ,例如America/MontrealAfrica/CasablancaPacific/Auckland 切勿使用2-4字母的縮寫,例如ESTIST因為它們不是真實的時區,不是標准化的,甚至不是唯一的(!)。

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

如果要使用JVM的當前默認時區,請提出要求並作為參數傳遞。 如果省略,代碼將變得難以理解,因為我們不確定您是否打算使用默認值,或者您是否像許多程序員一樣不知道該問題。

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

現在我們可以應用ZoneId來獲取ZonedDateTime

ZonedDateTime zdt = ldt.atZone( z ) ;

OffsetDateTime

您的JDBC 可以接受ZonedDateTime對象,但是JDBC 4.2規范要求它采用OffsetDateTime 有什么不同? ZonedDateTimeOffsetDateTime表示時刻,即時間軸上的一點。 偏移量只是UTC之前或之后的數小時-數分鍾-秒。 時區更多。 時區是特定區域的人們過去,現在和將來對偏移量的更改的歷史記錄。 因此,時區始終是可取的。 除了這里,在我們使用JDBC與數據庫交換java.time對象的地方,我們使用OffsetDateTime編寫標准代碼。

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

現在,我們可以將這一刻傳遞給您准備好的聲明。

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

恢復。

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

分配所需的時區。

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

在上面幾行中看到的odtzdt都表示相同的同時時刻,時間軸上的相同點。 只有它們的掛鍾時間有所不同,因為大多數數據庫在您想查看巴黎時間時都存儲和檢索UTC。


關於java.time

java.time框架內置於Java 8及更高版本中。 這些類取代了麻煩的舊的舊式日期時間類,例如java.util.DateCalendarSimpleDateFormat

現在處於維護模式Joda-Time項目建議遷移到java.time類。

要了解更多信息,請參見Oracle教程 並在Stack Overflow中搜索許多示例和說明。 規格為JSR 310

您可以直接與數據庫交換java.time對象。 使用與JDBC 4.2或更高版本兼容的JDBC驅動程序 不需要字符串,不需要java.sql.*類。

在哪里獲取java.time類?

ThreeTen-Extra項目使用其他類擴展了java.time。 該項目為將來可能在java.time中添加內容提供了一個試驗場。 您可以在這里找到一些有用的類,比如IntervalYearWeekYearQuarter ,和更多

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM