Buộc múi giờ Java là GMT / UTC


95

Tôi cần bắt buộc thực hiện mọi thao tác liên quan đến giờ GMT / UTC, bất kể múi giờ được đặt trên máy. Bất kỳ cách thuận tiện để như vậy trong mã?

Để làm rõ, tôi đang sử dụng thời gian của máy chủ DB cho tất cả các hoạt động, nhưng nó được định dạng theo múi giờ cục bộ.

Cảm ơn!


Thực ra, vấn đề của anh ấy là một tập hợp con của tôi, nhưng tôi đã tìm ra giải pháp.
SyBer

Xem câu hỏi trùng lặp và tìm kiếm "Joda" và "DateTimeZone".
Basil Bourque,

Câu trả lời:


124

OP đã trả lời câu hỏi này để thay đổi múi giờ mặc định cho một phiên bản JVM đang chạy, hãy đặt thuộc tính user.timezonehệ thống:

java -Duser.timezone=GMT ... <main-class>

Nếu bạn cần đặt múi giờ cụ thể khi truy xuất các đối tượng Ngày / Giờ / Dấu thời gian từ cơ sở dữ liệu ResultSet, hãy sử dụng biểu mẫu thứ hai của các getXXXphương thức lấy một Calendarđối tượng:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
ResultSet rs = ...;
while (rs.next()) {
    Date dateValue = rs.getDate("DateColumn", tzCal);
    // Other fields and calculations
}

Hoặc, đặt ngày trong Câu lệnh Chuẩn bị:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement ps = conn.createPreparedStatement("update ...");
ps.setDate("DateColumn", dateValue, tzCal);
// Other assignments
ps.executeUpdate();

Những điều này sẽ đảm bảo rằng giá trị được lưu trữ trong cơ sở dữ liệu là nhất quán khi cột cơ sở dữ liệu không giữ thông tin múi giờ.

Các lớp java.util.Datejava.sql.Datelưu trữ thời gian thực (mili giây) theo giờ UTC. Để định dạng chúng trên đầu ra sang múi giờ khác, hãy sử dụng SimpleDateFormat. Bạn cũng có thể liên kết múi giờ với giá trị bằng đối tượng Lịch:

TimeZone tz = TimeZone.getTimeZone("<local-time-zone>");
//...
Date dateValue = rs.getDate("DateColumn");
Calendar calValue = Calendar.getInstance(tz);
calValue.setTime(dateValue);

Tham khảo hữu ích

https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm#JSTGD377

https://confluence.atlassian.com/kb/setting-the-timezone-for-the-java-enosystem-841187402.html


Một điều cần lưu ý về việc đặt múi giờ cho toàn bộ JVM là nó ảnh hưởng đến mọi thứ, bao gồm cả những thứ như ghi nhật ký. Chỉ cần một số điều cần ghi nhớ nếu đó là hiệu quả mong muốn.
Hermann Hans

Vâng, điều đó tốt. Chỉ cần đề cập một điểm, rằng tôi thực sự đặt user.timezone trực tiếp trong mã, thay vì sau đó thông qua tham số -D.
SyBer

6
@SyBer: Điều đó hoạt động nhưng bạn phải đảm bảo rằng bạn đặt điều này trước khi bất kỳ phương thức nào gọi phương thức TimeZone.getDefault (). Nếu không, bạn sẽ có một số nhầm lẫn vì mặc định được lưu vào bộ nhớ cache sau lần thực thi đầu tiên. Điều này cũng có nghĩa là nó có thể là một vấn đề nếu bạn thực hiện việc này bên trong một API / thư viện (bị ẩn khỏi chế độ xem thường) - hãy nhớ ghi lại đầy đủ những gì bạn đang làm.
Kevin Brock

23

Ngoài ra, nếu bạn có thể đặt múi giờ JVM theo cách này

System.setProperty("user.timezone", "EST");

hoặc -Duser.timezone=GMTtrong nhóm JVM.


System.setProperty("user.timezone" ...)không hoạt động.
wilmol


9

Bạn có thể thay đổi múi giờ bằng TimeZone.setDefault () :

TimeZone.setDefault(TimeZone.getTimeZone("GMT"))

Không có vi phạm, nhưng việc sửa đổi tạm thời một âm thanh trạng thái tĩnh toàn cầu như là một ý tưởng rất xấu ...
G. Demecki

Bạn có thể, nhưng bạn không nên. Đây là lời khuyên cực kỳ tồi tệ. Toàn bộ JVM đã thay đổi múi giờ.
scot

1
Đôi khi nó chính xác là những gì bạn muốn đạt được. Ví dụ. Tôi đã thấy các dịch vụ vi mô dựa trên Java luôn chạy với UTC để tránh các vấn đề về múi giờ. Trong các hệ thống này, phần phụ trợ cơ sở dữ liệu cũng chạy với UTC, do đó, việc "buộc" JVM thành UTC là điều đương nhiên. Điều quan trọng: bạn phải biết những gì bạn đang làm và những gì là những hậu quả ...
snorbi

tuyệt đối không. Nếu bạn muốn toàn bộ hệ thống của mình ở trạng thái UTC (một ý tưởng rất hay), hãy đặt múi giờ hệ thống thành UTC và để riêng múi giờ Java.
scot

3
Ngoại trừ trường hợp bạn có nhiều JVM với các yêu cầu khác nhau. Chúng ta có thể tranh luận mãi mãi nhưng mọi trường hợp đều là duy nhất: đôi khi bạn cần đặt múi giờ của toàn bộ hệ thống, đôi khi bạn chỉ đặt một JVM. Hãy được hạnh phúc mà các hệ thống hiện nay rất linh hoạt mà chúng ta có thể làm cả hai 😉
snorbi

8

đối với tôi, chỉ cần SimpleDateFormat nhanh chóng,

  private static final SimpleDateFormat GMT = new SimpleDateFormat("yyyy-MM-dd");
  private static final SimpleDateFormat SYD = new SimpleDateFormat("yyyy-MM-dd");
  static {
      GMT.setTimeZone(TimeZone.getTimeZone("GMT"));
    SYD.setTimeZone(TimeZone.getTimeZone("Australia/Sydney"));
  }

sau đó định dạng ngày với múi giờ khác nhau.


6

Tôi sẽ truy xuất thời gian từ DB ở dạng thô (dấu thời gian dài hoặc Ngày của java), rồi sử dụng SimpleDateFormat để định dạng nó hoặc Lịch để thao tác với nó. Trong cả hai trường hợp, bạn nên đặt múi giờ của các đối tượng trước khi sử dụng nó.

Xem SimpleDateFormat.setTimeZone(..)Calendar.setTimeZone(..)để biết chi tiết


6

tl; dr

String sql = "SELECT CURRENT_TIMESTAMP ;

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

Tránh phụ thuộc vào hệ điều hành máy chủ hoặc JVM cho múi giờ mặc định

Tôi khuyên bạn nên viết tất cả mã của mình để nêu rõ ràng múi giờ mong muốn / dự kiến. Bạn không cần phụ thuộc vào múi giờ mặc định hiện tại của JVM. Và lưu ý rằng múi giờ mặc định hiện tại của JVM có thể thay đổi bất kỳ lúc nào trong thời gian chạy, với bất kỳ mã nào trong bất kỳ chuỗi nào của bất kỳ cuộc gọi ứng dụng nàoTimeZone.setDefault . Một cuộc gọi như vậy sẽ ảnh hưởng đến tất cả các ứng dụng trong JVM đó ngay lập tức.

java.time

Một số Câu trả lời khác đúng, nhưng hiện đã lỗi thời. Các lớp date-time khủng khiếp đi kèm với các phiên bản Java đầu tiên là thiếu sót, được viết bởi những người không hiểu sự phức tạp và tinh tế của việc xử lý date-time.

Các lớp ngày-giờ kế thừa đã được thay thế bằng các lớp java.time được định nghĩa trong JSR 310.

Để biểu thị một múi giờ, hãy sử dụng ZoneId. Để biểu diễn một offset-from-UTC, hãy sử dụng ZoneOffset. Độ lệch chỉ là một số giờ-phút-giây phía trước hoặc phía sau kinh tuyến gốc. Múi giờ nhiều hơn. Múi giờ là lịch sử của những thay đổi trong quá khứ, hiện tại và tương lai đối với khoảng thời gian được sử dụng bởi người dân của một khu vực cụ thể.

Tôi cần bắt buộc thực hiện các thao tác liên quan đến GMT / UTC bất kỳ lúc nào

Để có khoảng chênh lệch 0 giờ-phút-giây, hãy sử dụng hằng số ZoneOffset.UTC.

Instant

Để ghi lại khoảnh khắc hiện tại theo giờ UTC, hãy sử dụng dấu Instant. Lớp này đại diện cho một thời điểm trong UTC, luôn luôn ở UTC theo định nghĩa.

Instant instant = Instant.now() ;  // Capture current moment in UTC.

ZonedDateTime

Để xem cùng thời điểm đó thông qua thời gian trên đồng hồ treo tường được người dân ở một khu vực cụ thể sử dụng, hãy điều chỉnh thành múi giờ. Cùng một thời điểm, cùng một điểm trên dòng thời gian, thời gian trên đồng hồ treo tường khác nhau.

ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Cơ sở dữ liệu

Bạn đề cập đến một cơ sở dữ liệu.

Để truy xuất một thời điểm từ cơ sở dữ liệu, cột của bạn phải có kiểu dữ liệu giống với chuẩn SQL TIMESTAMP WITH TIME ZONE. Lấy một đối tượng chứ không phải một chuỗi. Trong JDBC 4.2 trở lên, chúng ta có thể trao đổi các đối tượng java.time với cơ sở dữ liệu. Các OffsetDateTimetheo yêu cầu của JDBC, trong khi Instant& ZonedDateTimelà không bắt buộc.

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

Trong hầu hết các cơ sở dữ liệu và trình điều khiển, tôi đoán rằng bạn sẽ có được khoảnh khắc như đã thấy ở UTC. Nhưng nếu không, bạn có thể điều chỉnh theo một trong hai cách:

  • Trích xuất một Instant: odt.toInstant()
  • Điều chỉnh từ độ lệch đã cho thành độ lệch bằng 0: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); '.

Bảng các kiểu ngày-giờ trong Java (cả kế thừa và hiện đại) và trong SQL chuẩn


Giới thiệu về java.time

Các java.time khung được xây dựng vào Java 8 và sau đó. Những lớp học thay thế cái cũ phiền hà di sản lớp học ngày thời gian như java.util.Date, Calendar, &SimpleDateFormat .

Để tìm hiểu thêm, hãy xem Hướng dẫn Oracle . Và tìm kiếm Stack Overflow để có nhiều ví dụ và giải thích. Đặc điểm kỹ thuật là JSR 310 .

Các Joda thời gian dự án, bây giờ trong chế độ bảo trì , khuyên chuyển đổi sang các java.time lớp.

Bạn có thể trao đổi các đối tượng java.time trực tiếp với cơ sở dữ liệu của mình. Sử dụng trình điều khiển JDBC tương thích với JDBC 4.2 trở lên. Không cần chuỗi, không cần java.sql.*lớp.

Lấy các lớp java.time ở đâu?


1

tạo một cặp máy khách / máy chủ để sau khi thực thi, máy chủ khách hàng sẽ gửi ngày và giờ chính xác. Sau đó, khách hàng hỏi máy chủ chiều GMT và máy chủ sẽ gửi lại câu trả lời đúng.


1

Sử dụng thuộc tính hệ thống:

-Duser.timezone=UTC

5
Tại sao người ta nên sử dụng cái đó? Vui lòng thêm một số giải thích cho câu trả lời của bạn như vậy mà những người khác có thể học hỏi từ nó
Nico Haase

1

Thực thi dòng này trong MySQL để đặt lại múi giờ của bạn:

SET @@global.time_zone = '+00:00';

0

Chà. Tôi biết đây là một chủ đề cổ xưa nhưng tất cả những gì tôi có thể nói là không gọi TimeZone.setDefault () trong bất kỳ mã cấp người dùng nào. Điều này luôn đặt Múi giờ cho toàn bộ JVM và gần như luôn là một ý tưởng tồi. Học cách sử dụng thư viện joda.time hoặc lớp DateTime mới trong Java 8 rất giống với thư viện joda.time.


-6

Nếu bạn chỉ muốn xem giờ GMT với intiger: var currentTime = new Date (); var currentYear = '2010' var currentMonth = 10; var currentDay = '30 'var currentHours = '20' var currentMinutes = '20 'var currentSeconds = '00' var currentMilliseconds = '00 '

currentTime.setFullYear(currentYear);
currentTime.setMonth((currentMonth-1)); //0is January
currentTime.setDate(currentDay);  
currentTime.setHours(currentHours);
currentTime.setMinutes(currentMinutes);
currentTime.setSeconds(currentSeconds);
currentTime.setMilliseconds(currentMilliseconds);

var currentTimezone = currentTime.getTimezoneOffset();
currentTimezone = (currentTimezone/60) * -1;
var gmt ="";
if (currentTimezone !== 0) {
  gmt += currentTimezone > 0 ? ' +' : ' ';
  gmt += currentTimezone;
}
alert(gmt)

5
đối với tôi trông giống như JavaScript (! = Java).
Phil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.