2008-11-21 24 views
14

Tôi đã sử dụng đối tượng Date() mới để điền vào một trường trong một DB MySQL, nhưng giá trị thực được lưu trữ trong trường đó nằm trong múi giờ địa phương của tôi.Làm thế nào để lưu trữ một java.util.Date vào một lĩnh vực dấu thời gian MySQL trong múi giờ UTC/GMT?

Làm cách nào để định cấu hình MySQL để lưu trữ trong múi giờ UTC/GMT?

Tôi nghĩ, việc định cấu hình chuỗi kết nối sẽ giúp ích nhưng tôi không biết cách thực hiện. Có rất nhiều thuộc tính trong chuỗi kết nối như useTimezone, serverTimzone, useGmtMillisForDatetimes, useLegacyDatetimeCode, ...

+0

Bạn đang sử dụng Hibernate hoặc tương tự như giao diện cơ sở dữ liệu? –

Trả lời

13

Câu trả lời ngắn gọn là:

  • add "mặc định-múi giờ = utc" để my.cnf
  • trong mã của bạn, luôn luôn "nghĩ" tại UTC, trừ khi hiển thị ngày cho người dùng của bạn
  • khi nhận/thiết lập ngày tháng hoặc timestamps với JDBC, luôn luôn sử dụng các tham số Lịch, đặt theo UTC:

    resultset.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC"))) ;

  • hoặc đồng bộ hóa máy chủ của bạn với NTP hoặc chỉ dựa trên máy chủ cơ sở dữ liệu để cho bạn biết thời gian là gì.

Câu trả lời dài là thế này:

Khi giao dịch với số ngày và múi giờ trong bất kỳ cơ sở dữ liệu và với bất kỳ mã khách hàng, tôi thường khuyên các chính sách sau đây:

  1. Configure cơ sở dữ liệu của bạn sử dụng múi giờ UTC, thay vì sử dụng múi giờ địa phương của máy chủ (trừ khi đó là UTC của khóa học).

    • Cách thực hiện tùy thuộc vào máy chủ cơ sở dữ liệu của bạn. Hướng dẫn cho MySQL có thể được tìm thấy ở đây: http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html. Về cơ bản, bạn cần phải viết điều này trong my.cnf: default-time-zone = utc

    • Bằng cách này bạn có thể lưu trữ máy chủ cơ sở dữ liệu của mình mọi nơi, thay đổi vị trí lưu trữ dễ dàng và thường sử dụng ngày tháng trên máy chủ của bạn .

    • Nếu bạn thực sự thích sử dụng múi giờ địa phương, tôi khuyên bạn nên tắt Giờ tiết kiệm ánh sáng ban ngày, vì ngày mơ hồ trong cơ sở dữ liệu của bạn có thể là một cơn ác mộng thực sự.
      • Ví dụ: nếu bạn đang xây dựng dịch vụ điện thoại và đang sử dụng Giờ tiết kiệm ánh sáng trên máy chủ cơ sở dữ liệu của mình thì bạn sẽ gặp rắc rối: sẽ không có cách nào để biết khách hàng đã gọi từ "2008- 10-26 02:30:00 "đến" 2008-10-26 02:35:00 "thực sự được gọi trong 5 phút hoặc trong 1 giờ và 5 phút (giả sử tiết kiệm ánh sáng ban ngày xảy ra vào ngày 26 tháng 10 lúc 3 giờ sáng)!
  2. Bên trong mã ứng dụng của bạn, luôn luôn sử dụng UTC ngày, trừ khi hiển thị ngày để người dùng của bạn.

    • Trong Java, khi đọc từ cơ sở dữ liệu, luôn luôn sử dụng:

    Timestamp mydate = resultSet.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC"))) ;

    • Nếu bạn không làm điều này, dấu thời gian sẽ được giả định là trong múi giờ địa phương của bạn, thay vì UTC.
  3. Đồng bộ hóa máy chủ của bạn hoặc chỉ dựa vào thời gian máy chủ cơ sở dữ liệu của

    • Nếu bạn có máy chủ web của bạn trên một máy chủ (hoặc nhiều hơn) và máy chủ cơ sở dữ liệu của bạn trên một số máy chủ khác, sau đó Tôi khuyên bạn nên đồng bộ hóa đồng hồ của họ với NTP.

    • HOẶC, chỉ dựa vào một máy chủ để cho bạn biết thời gian là gì. Thông thường, máy chủ cơ sở dữ liệu là máy chủ tốt nhất để yêu cầu thời gian. Nói cách khác, tránh mã như thế này:

    PrepareStatement = connection.prepareStatement ("UPDATE my_table SET my_time =? WHERE [...]");
    java.util.Date now = new java.util.Date(); // giờ địa phương! :-(
    preparedStatement.setTimestamp (1, mới Timestamp (now.getTime()));
    int result = preparedStatement.execute();

    • Thay vào đó, dựa vào thời gian máy chủ cơ sở dữ liệu của:

    PreparedStatement = connection.prepareStatement ("UPDATE my_table SET my_time = NOW() WHERE [...]");
    int result = preparedStatement.execute();

Hy vọng điều này sẽ hữu ích! :-)

+0

Hai bổ sung: 1. Bạn nên sử dụng "múi giờ mặc định = GMT" (phân biệt chữ hoa chữ thường) vì MySQL không đủ thông minh để ánh xạ "utc" vào "GMT" của riêng nó và Java chỉ nhận ra cái sau. 2. Bạn nên vượt qua trình điều khiển "useLegacyDatetimeCode = false" để sửa lỗi này: http://bugs.mysql.com/bug.php?id=15604 – Gili

+0

các điểm rất tốt! Bug Tôi có một số điểm khác để nâng cao. Bạn không xem xét tùy chọn đặt múi giờ phiên trên mỗi kết nối thành db, làm điều đó bạn không cần phải thay đổi múi giờ mà MySQL của bạn đã bắt đầu. Tôi đang gặp rất nhiều vấn đề trong khi lưu trữ 'java.util.Date' mới trên db. Các giải pháp nhanh chóng được thiết lập JVM của tôi để bắt đầu trên UTC quá (-Duser.timezone = UTC). Những điểm này sẽ là tốt để thêm vào bài viết của bạn! –

+0

Theo tài liệu MySQL trên biến hệ thống [time_zone] (http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_time_zone), tên biến my.cnf là default_time_zone (gạch dưới như trái ngược với dấu gạch ngang được hiển thị trong bài đăng của bạn). –

1

Vâng, nếu chúng ta đang nói về việc sử dụng PreparedStatement s, có một form of setDate nơi bạn có thể vượt qua trong một Lịch thiết lập để một đặc biệt Múi giờ.

Ví dụ, nếu bạn có một PreparedStatement tên stmt, một ngày được đặt tên ngày, và giả sử ngày là tham số thứ hai:

stmt.setDate(2, date, Calendar.getInstance(TimeZone.getTimeZone("GMT"))); 

Phần tốt nhất là, ngay cả khi giờ là một tên múi giờ hợp lệ , nó vẫn trả về múi giờ GMT vì hành vi mặc định của getTimeZone.

+0

Tôi luôn thích sử dụng UTC thay vì GMT. –

+1

Theo mặc định, đối tượng Lịch bị bỏ qua hoàn toàn trừ khi bạn sử dụng tùy chọn kết nối useLegacyDatetimeCode = false – nogridbag

1

Ngày lập trình java là thời gian bất khả tri. Nó luôn luôn đại diện cho một ngày trong GMD (UTC) trong mili giây từ Epoch.

Thời gian CHỈ là múi giờ có liên quan là khi bạn phát ra ngày làm chuỗi hoặc phân tích chuỗi dữ liệu thành đối tượng ngày.

2

MiniQuark đưa ra một số câu trả lời tốt cho cơ sở dữ liệu nói chung, nhưng có một số quirks cụ MySql để xem xét ...

Cấu hình cơ sở dữ liệu của bạn để sử dụng múi giờ UTC

Đó thực sự sẽ không đủ để khắc phục vấn đề. Nếu bạn vượt qua một java.util.Date để MySql như OP đã yêu cầu, trình điều khiển MySql sẽ thay đổi giá trị để làm cho nó trông giống như giờ địa phương giống nhau trong múi giờ của cơ sở dữ liệu.

Ví dụ: Cơ sở dữ liệu của bạn nếu được định cấu hình thành UTC. Đơn đăng ký của bạn là EST. Bạn chuyển đối tượng java.util.Date cho 5:00 (EST). Cơ sở dữ liệu sẽ chuyển đổi nó thành 5:00 UTC và lưu trữ nó. Tuyệt vời.

Bạn sẽ phải điều chỉnh thời gian trước khi bạn chuyển dữ liệu để "hoàn tác" điều chỉnh tự động này. Một cái gì đó giống như ...

long originalTime = originalDate.getTime(); 
Date newDate = new Date(originalTime - TimeZone.getDefault().getOffset(originalTime)); 
ps.setDate(1, newDate); 

Đọc dữ liệu trở lại ra đòi hỏi một sự chuyển đổi tương tự ..

long dbTime = rs.getTimestamp(1).getTime(); 
Date originalDate = new Date(dbTime + TimeZone.getDefault().getOffset(dbTime)); 

Đây là một điều không minh bạch niềm vui ...

Trong Java, khi đọc từ cơ sở dữ liệu, luôn sử dụng: Dấu thời gian myDate = resultSet.getTimestamp ("my_date", Calendar.getInstance (TimeZone.getTimeZone ("UTC")));

MySql thực sự bỏ qua thông số Lịch đó. Điều này trả về cùng một giá trị bất kể bạn đã vượt qua lịch nào.

2

Tôi gặp vấn đề tương tự và phải mất gần một ngày để theo dõi. Tôi đang lưu trữ các cột DateTime trong MySQL. Ví dụ RDS, chạy trong Đám mây của Amazon, được đặt chính xác để có dấu thời gian UTC theo mặc định.

Các Buggy Mã là:

String startTime = "2013-02-01T04:00:00.000Z"; 
    DateTime dt = ISODateTimeFormat.dateTimeParser().parseDateTime(startTime);    

    PreparedStatement stmt = connection.prepareStatement(insertStatementTemplate); 

    Timestamp ts = new Timestamp(dt.getMillis()); 
    stmt.setTimestamp(1, ts, Calendar.getInstance(TimeZone.getTimeZone("UTC"))); 

Trong đoạn mã trên, các ".setTimestamp" cuộc gọi sẽ KHÔNG mất ngày như một ngày UTC!

Sau nhiều giờ điều tra, điều này hóa ra lại là lỗi đã biết trong Trình điều khiển Java/MySQL. Các cuộc gọi đến setTimestamp listerally chỉ bỏ qua các tham số Lịch.

Để khắc phục điều này, hãy thêm "useLegacyDatetimeCode = false" vào URI cơ sở dữ liệu của bạn.

private final static String DatabaseName = 
    "jdbc:mysql://foo/?useLegacyDatetimeCode=false"; 

Ngay sau khi tôi làm điều đó, ngày được lưu trữ trong cơ sở dữ liệu MySQL ở dạng UTC đúng, thay vì trong múi giờ của máy trạm cục bộ của tôi.

Các vấn đề liên quan