บังคับให้เขตเวลา Java เป็น GMT / UTC


101

ฉันจำเป็นต้องบังคับให้การดำเนินการที่เกี่ยวข้องกับเวลาเป็น GMT / UTC โดยไม่คำนึงถึงเขตเวลาที่ตั้งไว้บนเครื่อง วิธีใดที่สะดวกในการเขียนโค้ด

เพื่อชี้แจงฉันใช้เวลาเซิร์ฟเวอร์ DB สำหรับการดำเนินการทั้งหมด แต่ออกมาในรูปแบบตามเขตเวลาท้องถิ่น

ขอบคุณ!


อันที่จริงปัญหาของเขาเป็นส่วนย่อยของฉัน แต่ฉันพบวิธีแก้ปัญหา
SyBer

ดูคำถามที่ซ้ำกันแล้วค้นหา "Joda" และ "DateTimeZone"
Basil Bourque

คำตอบ:


127

OP ตอบคำถามนี้เพื่อเปลี่ยนเขตเวลาเริ่มต้นสำหรับอินสแตนซ์เดียวของ JVM ที่รันตั้งค่าuser.timezoneคุณสมบัติระบบ:

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

หากคุณต้องการตั้งค่าโซนเวลาเฉพาะเมื่อดึงอ็อบเจ็กต์ Date / Time / Timestamp จากฐานข้อมูลResultSetให้ใช้รูปแบบที่สองของgetXXXวิธีการที่ใช้Calendarวัตถุ:

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

หรือตั้งวันที่ใน PreparedStatement:

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

สิ่งเหล่านี้จะช่วยให้แน่ใจว่าค่าที่จัดเก็บในฐานข้อมูลมีความสอดคล้องกันเมื่อคอลัมน์ฐานข้อมูลไม่เก็บข้อมูลเขตเวลา

java.util.Dateและjava.sql.Dateเรียนเก็บในเวลาจริง (มิลลิวินาที) ในเวลา UTC SimpleDateFormatการจัดรูปแบบเหล่านี้ในการส่งออกไปยังเขตอื่นใช้ คุณยังสามารถเชื่อมโยงเขตเวลากับค่าโดยใช้วัตถุปฏิทิน:

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

ข้อมูลอ้างอิงที่เป็นประโยชน์

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

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


สิ่งหนึ่งที่ควรทราบเกี่ยวกับการตั้งเขตเวลาสำหรับ JVM ทั้งหมดคือมันมีผลต่อทุกสิ่งรวมถึงสิ่งต่างๆเช่นการบันทึก สิ่งที่ควรทราบหากนั่นคือเอฟเฟกต์ที่ต้องการ
Hermann Hans

ใช่ดี มีเพียงประเด็นเดียวที่จะพูดถึงนั่นคือฉันตั้งค่า user.timezone โดยตรงในโค้ดแทนที่จะเป็นพารามิเตอร์ -D
SyBer

6
@SyBer: ใช้งานได้ แต่คุณต้องแน่ใจว่าคุณตั้งค่านี้ก่อนที่วิธีใด ๆ จะเรียกใช้เมธอด TimeZone.getDefault () ถ้าไม่คุณจะมีความสับสนเนื่องจากค่าเริ่มต้นถูกแคชไว้หลังจากการดำเนินการครั้งแรก นอกจากนี้ยังหมายความว่าอาจเป็นปัญหาได้หากคุณทำสิ่งนี้ภายใน API / ไลบรารี (ซ่อนจากมุมมองธรรมดา) อย่าลืมบันทึกสิ่งที่คุณกำลังทำ
Kevin Brock

23

นอกจากนี้หากคุณสามารถกำหนดเขตเวลา JVM ด้วยวิธีนี้

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

หรือ-Duser.timezone=GMTในอาร์กิวเมนต์ JVM


System.setProperty("user.timezone" ...)ไม่ทำงาน
wilmol

13

ฉันต้องตั้งค่าเขตเวลา JVM สำหรับ Windows 2003 Server เพราะมันส่งคืน GMT สำหรับ Date () ใหม่เสมอ

-Duser.timezone=America/Los_Angeles

หรือเขตเวลาที่เหมาะสมของคุณ การค้นหารายการเขตเวลาพิสูจน์แล้วว่าเป็นเรื่องที่ท้าทายเช่นกัน ...

นี่คือสองรายการ;

http://wrapper.tanukisoftware.com/doc/english/prop-timezone.html

http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzatz%2F51%2Fadmin%2Freftz.htm


11

คุณสามารถเปลี่ยนเขตเวลาโดยใช้TimeZone.setDefault () :

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

ไม่มีความผิด แต่การปรับเปลี่ยนสภาวะคงที่ทั่วโลกเป็นการชั่วคราวดูเหมือนเป็นความคิดที่แย่มาก ...
G. Demecki

คุณทำได้ แต่ไม่ควรทำ นี่เป็นคำแนะนำที่ไม่ดีเป็นพิเศษ JVM ทั้งหมดมีการเปลี่ยนแปลงเขตเวลา
scot

1
บางครั้งก็เป็นสิ่งที่คุณต้องการบรรลุ เช่น. ฉันเห็นไมโครเซอร์วิสที่ใช้ Java ทำงานร่วมกับ UTC เสมอเพื่อหลีกเลี่ยงปัญหาโซนเวลา ในระบบเหล่านี้แบ็กเอนด์ฐานข้อมูลจะทำงานร่วมกับ UTC ด้วยดังนั้นจึงเป็นเรื่องธรรมดาที่จะ "บังคับ" JVM เป็น UTC ด้วยเช่นกัน อะไรสำคัญ: คุณต้องรู้ว่าคุณกำลังทำอะไรและผลที่ตามมาคืออะไร ...
snorbi

ไม่ได้อย่างแน่นอน. หากคุณต้องการให้ระบบทั้งหมดของคุณเป็น UTC (เป็นความคิดที่ดีมาก) ให้ตั้งเขตเวลาของระบบเป็น UTC และปล่อยให้เขตเวลา Java อยู่คนเดียว
scot

3
ยกเว้นในกรณีที่คุณมี JVM หลายรายการที่มีข้อกำหนดที่แตกต่างกัน เราสามารถโต้แย้งได้ตลอดไป แต่ทุกกรณีจะไม่ซ้ำกัน: บางครั้งคุณต้องกำหนดเขตเวลาของระบบทั้งหมดบางครั้งคุณตั้งค่า JVM เพียงรายการเดียว ขอให้มีความสุขที่ระบบทุกวันนี้มีความยืดหยุ่นมากจนเราสามารถทำได้ทั้งสองอย่าง😉
snorbi

8

สำหรับฉันเพียงแค่ SimpleDateFormat อย่างรวดเร็ว

  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"));
  }

จากนั้นจัดรูปแบบวันที่ด้วยเขตเวลาที่ต่างกัน


7

tl; dr

String sql = "SELECT CURRENT_TIMESTAMP ;
…
OffsetDateTime odt = myResultSet.getObject( 1 , OffsetDateTime.class ) ;

หลีกเลี่ยงการขึ้นอยู่กับโฮสต์ OS หรือ JVM สำหรับเขตเวลาเริ่มต้น

ขอแนะนำให้คุณเขียนโค้ดทั้งหมดของคุณเพื่อระบุโซนเวลาที่ต้องการ / คาดหวังอย่างชัดเจน คุณไม่จำเป็นต้องขึ้นอยู่กับเขตเวลาเริ่มต้นปัจจุบันของ JVM และทราบว่าเขตเวลาเริ่มต้น JVM ปัจจุบันสามารถเปลี่ยนในช่วงเวลาใดช่วง Runtime ด้วยรหัสใด ๆ TimeZone.setDefaultในหัวข้อของการโทร การโทรดังกล่าวส่งผลกระทบต่อแอปทั้งหมดภายใน JVM นั้นทันที

java.time

คำตอบอื่น ๆ บางคำตอบถูก แต่ตอนนี้ล้าสมัยแล้ว คลาสวันที่และเวลาที่แย่มากที่มาพร้อมกับ Java เวอร์ชันแรกสุดนั้นมีข้อบกพร่องซึ่งเขียนขึ้นโดยผู้ที่ไม่เข้าใจความซับซ้อนและรายละเอียดปลีกย่อยของการจัดการวันที่และเวลา

คลาสวันที่และเวลาดั้งเดิมถูกแทนที่ด้วยคลาสjava.time ที่กำหนดไว้ใน JSR 310

ZoneIdเพื่อเป็นตัวแทนของโซนเวลาการใช้งาน เพื่อเป็นตัวแทนชดเชยจากที่ UTC ZoneOffsetใช้ ค่าชดเชยเป็นเพียงจำนวนชั่วโมงนาทีวินาทีข้างหน้าหรือหลังเส้นเมริเดียนไพรม์ เขตเวลามีมากขึ้น เขตเวลาคือประวัติศาสตร์ของการเปลี่ยนแปลงในอดีตปัจจุบันและอนาคตของค่าชดเชยที่ผู้คนในภูมิภาคหนึ่ง ๆ ใช้

ฉันต้องการบังคับให้การดำเนินการที่เกี่ยวข้องกับเวลา GMT / UTC

ZoneOffset.UTCสำหรับการชดเชยของศูนย์ชั่วโมงนาทีวินาทีใช้อย่างต่อเนื่อง

Instant

ในการจับภาพช่วงเวลาปัจจุบันใน UTC ให้ใช้Instantไฟล์. คลาสนี้แสดงช่วงเวลาใน UTC โดยจะเป็น UTC ตามคำจำกัดความเสมอ

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

ZonedDateTime

หากต้องการดูช่วงเวลาเดียวกันผ่านเวลานาฬิกาแขวนที่ผู้คนในภูมิภาคใดภูมิภาคหนึ่งใช้ให้ปรับเป็นเขตเวลา ช่วงเวลาเดียวกันจุดเดียวกันบนไทม์ไลน์เวลานาฬิกาแขวนที่ต่างกัน

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

ฐานข้อมูล

คุณพูดถึงฐานข้อมูล

ช่วงเวลาในการดึงข้อมูลจากฐานข้อมูลที่เป็นคอลัมน์ของคุณควรจะเป็นชนิดข้อมูลที่คล้ายกับ TIMESTAMP WITH TIME ZONESQL ดึงข้อมูลวัตถุแทนที่จะเป็นสตริง ใน JDBC 4.2 และใหม่กว่าเราสามารถแลกเปลี่ยนวัตถุjava.timeกับฐานข้อมูลได้ OffsetDateTimeถูกต้องตาม JDBC ในขณะที่InstantและZonedDateTimeเป็นตัวเลือก

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

ในฐานข้อมูลและไดรเวอร์ส่วนใหญ่ฉันเดาว่าคุณจะได้รับช่วงเวลาดังที่เห็นใน UTC แต่ถ้าไม่คุณสามารถปรับได้สองวิธี:

  • ดึงข้อมูล a Instant: odt.toInstant()
  • ปรับจากออฟเซ็ตที่กำหนดเป็นออฟเซ็ตของศูนย์: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); ".

ตารางประเภทวันที่ - เวลาใน Java (ทั้งแบบเดิมและแบบสมัยใหม่) และใน SQL มาตรฐาน


เกี่ยวกับjava.time

java.timeกรอบถูกสร้างขึ้นใน Java 8 และต่อมา ชั้นเรียนเหล่านี้แย่งลำบากเก่ามรดกเรียนวันที่เวลาเช่นjava.util.Date, และCalendarSimpleDateFormat

ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย สเปกJSR 310

โครงการJoda-Timeขณะนี้อยู่ในโหมดการบำรุงรักษาแนะนำให้ย้ายไปยังคลาสjava.time

คุณสามารถแลกเปลี่ยนวัตถุjava.timeโดยตรงกับฐานข้อมูลของคุณ ใช้ไดรเวอร์ JDBC ที่สอดคล้องกับJDBC 4.2หรือใหม่กว่า ไม่จำเป็นต้องมีสตริงไม่จำเป็นต้องมีjava.sql.*คลาส

จะหาคลาส java.time ได้ที่ไหน?

  • Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11และใหม่กว่า - เป็นส่วนหนึ่งของ Java API มาตรฐานพร้อมการใช้งานแบบรวม
    • Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
  • Java SE 6และ Java SE 7
    • ส่วนใหญ่ของjava.timeการทำงานจะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
  • Android

6

ฉันจะดึงเวลาจาก DB ในรูปแบบดิบ (การประทับเวลาแบบยาวหรือวันที่ของจาวา) จากนั้นใช้ SimpleDateFormat เพื่อจัดรูปแบบหรือปฏิทินเพื่อจัดการ ในทั้งสองกรณีคุณควรกำหนดเขตเวลาของวัตถุก่อนใช้งาน

ดูSimpleDateFormat.setTimeZone(..)และCalendar.setTimeZone(..)รายละเอียด


2

ใช้คุณสมบัติของระบบ:

-Duser.timezone=UTC

5
ทำไมจึงควรใช้? โปรดเพิ่มคำอธิบายให้กับคำตอบของคุณเพื่อให้คนอื่นสามารถเรียนรู้ได้
Nico Haase

อันนี้ใช้ได้กับฉันขอบคุณมาก !!
Carlos Saltos

1

สร้างคู่ของไคลเอนต์ / เซิร์ฟเวอร์เพื่อให้หลังจากการดำเนินการไคลเอนต์เซิร์ฟเวอร์จะส่งเวลาและวันที่ที่ถูกต้อง จากนั้นไคลเอนต์จะถามเซิร์ฟเวอร์ pm GMT และเซิร์ฟเวอร์จะส่งคำตอบกลับไปที่ถูกต้อง


1

ดำเนินการตามบรรทัดนี้ใน MySQL เพื่อรีเซ็ตเขตเวลาของคุณ:

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

0

ว้าว. ฉันรู้ว่านี่เป็นเธรดโบราณ แต่ทั้งหมดที่ฉันพูดได้คืออย่าเรียก TimeZone.setDefault () ในรหัสระดับผู้ใช้ใด ๆ สิ่งนี้จะกำหนดเขตเวลาสำหรับ JVM ทั้งหมดเสมอและเกือบจะเป็นความคิดที่แย่มาก เรียนรู้การใช้ไลบรารี joda.time หรือคลาส DateTime ใหม่ใน Java 8 ซึ่งคล้ายกับไลบรารี joda.time มาก



-6

หากคุณต้องการรับเวลา GMT ด้วย 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
ที่ดูเหมือนจาวาสคริปต์ (! = Java) สำหรับฉัน
ฟิลิป
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.