Примусити часовий пояс Java як GMT / UTC


95

Мені потрібно примусити будь-які операції, пов’язані з часом, до GMT / UTC, незалежно від часового поясу, встановленого на машині. Будь-який зручний спосіб зробити так у коді?

Для уточнення, я використовую час сервера DB для всіх операцій, але він виходить відформатованим відповідно до місцевого часового поясу.

Дякую!


Насправді його проблема - це моя підгрупа, але я знайшов рішення.
SyBer

Подивіться на дублікат запитання та знайдіть "Joda" та "DateTimeZone".
Василь Бурк

Відповіді:


124

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, це те, що це впливає на все, включаючи такі речі, як реєстрація. Просто щось слід пам’ятати, якщо це бажаний ефект.
Герман Ганс

Так, це чудово. Тільки один момент, щоб згадати, що я фактично встановив 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 для нової дати ();

-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


9

Ви можете змінити часовий пояс за допомогою TimeZone.setDefault () :

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

Не ображайтесь, але тимчасове змінення глобального статичного стану звучить як дуже погана ідея ...
Г. Демецький,

Ви могли, але не повинні. Це надзвичайно погана порада. У цілому JVM змінено часовий пояс.
безкарним

1
Іноді це саме те, чого ви хочете досягти. Напр. Я бачив мікросервіси на основі Java, які працюють завжди з UTC, щоб уникнути проблем з часовим поясом. У цих системах база даних бази даних також працює з UTC, тому природно також "примусити" JVM до UTC. Що важливо: ви повинні знати, що робите, і які наслідки ...
snorbi

абсолютно не. Якщо вам потрібна вся ваша система в UTC (дуже гарна ідея), встановіть системний часовий пояс на UTC і залиште часовий пояс Java у спокої.
безкарні

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

потім відформатуйте дату з іншим часовим поясом.


6

Я б отримував час із БД у необробленому вигляді (довга мітка часу або дата Java), а потім використовував SimpleDateFormat для форматування або Календар для маніпулювання ним. В обох випадках перед використанням слід встановити часовий пояс об’єктів.

Дивіться SimpleDateFormat.setTimeZone(..)та Calendar.setTimeZone(..)докладніше


6

тл; д-р

String sql = "SELECT CURRENT_TIMESTAMP ;

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

Уникайте залежності від хост-ОС чи JVM для часового поясу за замовчуванням

Я рекомендую вам написати весь свій код, щоб чітко вказати бажаний / очікуваний часовий пояс. Вам не потрібно залежати від поточного часового поясу JVM за замовчуванням. І майте на увазі, що поточний часовий пояс JVM за замовчуванням може змінюватися в будь-який момент під час виконання, з будь-яким кодом у будь-якому потоці будь-якого виклику програми 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 ) ;

База даних

Ви згадали базу даних.

Щоб отримати момент із бази даних, ваш стовпець повинен мати тип даних, подібний до стандарту SQL TIMESTAMP WITH TIME ZONE. Отримати об’єкт, а не рядок. У JDBC 4.2 і пізніших версіях ми можемо обмінюватися об'єктами java.time з базою даних. OffsetDateTimeПотрібно JDBC, в той час як Instantі НЕ ZonedDateTimeє обов'язковими.

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

У більшості баз даних та драйверів я гадаю, що ви отримаєте момент, як видно в UTC. Але якщо ні, ви можете скоригуватися одним із двох способів:

  • Витяг Instant: odt.toInstant()
  • Налаштуйте від заданого зміщення до зміщення нуля: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); `.

Таблиця типів дати та часу в Java (як застарілій, так і сучасній) та в стандартному SQL


Про java.time

Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date, Calendar, і SimpleDateFormat.

Щоб дізнатися більше, зверніться до Oracle Підручник . І шукайте в Stack Overflow багато прикладів та пояснень. Специфікація - JSR 310 .

Проект Joda-Time , який зараз перебуває в режимі обслуговування , радить перейти на класи java.time .

Ви можете обмінювати об'єкти java.time безпосередньо зі своєю базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або новішою. Не потрібні рядки, не потрібні java.sql.*класи.

Де отримати класи java.time?


1

створити пару клієнт / сервер, щоб після виконання клієнтський сервер надсилав правильний час і дату. Потім клієнт запитує сервер pm GMT, і сервер відправляє відповідь правильно.


1

Використовувати системну властивість:

-Duser.timezone=UTC

5
Чому це потрібно використовувати? Будь ласка, додайте до своєї відповіді якесь пояснення, щоб інші могли навчитися з цього
Ніко Хаасе,

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
це для мене виглядає як JavaScript (! = Java).
Філ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.