JSR 310: преобразование часовых поясов
Попытка использовать JSR 310 для преобразования значений даты и времени в миллисекундах между часовыми поясами. Работа со значениями в миллисекундах необходима для работы с устаревшими API. В моем случае это между локальным и UTC/GMT, но я ожидаю, что это будет точно такой же код, независимо от того, какие именно часовые пояса источника и назначения задействованы. Вот моя маленькая тестовая программа. Он настроен так, что он повторяется в течение нескольких часов прямо перед последним локальным изменением летнего времени. Вывод неверный, и поэтому я предполагаю, что UTCtoLocalMillis неверен, но, возможно, есть и проблема с моей методологией тестирования. Под неправильным выводом я подразумеваю, что для моего часового пояса нужно вычесть часы, чтобы получить UTC, но код фактически добавляет часы. Во-вторых, точка, в которую попадает DST, также отключается на один час.
Сначала я выбираю местный часовой пояс, но затем сбрасываю его в UTC, так что Date(). ToString() не будет выполнять никакого преобразования при создании вывода.
Я хочу создать метод, который занимает время в миллисекундах, исходный ZoneID и целевой ZoneID и возвращает преобразованные миллисекунды с использованием нового API JSR 310.
public class Test {
static int DAYS_OFFSET_TO_BE_BEFORE_DST_CHANGE = -16;
static TimeZone UTC_TZ = TimeZone.getTimeZone("UTC");
static TimeZone LOCAL_TZ = TimeZone.getDefault();
static ZoneId UTC_ID = ZoneId.of(UTC_TZ.getID());
static ZoneId LOCAL_ZONE_ID = ZoneId.of(LOCAL_TZ.getID());
static long UTCtoLocalMillis(final long utcMillis) {
final Instant instant = Instant.ofEpochMilli(utcMillis);
final ZonedDateTime before
= ZonedDateTime.ofInstant(instant, UTC_ID);
final ZonedDateTime after
= before.withZoneSameLocal(LOCAL_ZONE_ID);
return after.toInstant().toEpochMilli();
}
public static void main(final String[] args) {
TimeZone.setDefault(UTC_TZ);
System.out.println("LOCAL_TZ: " + LOCAL_TZ.getDisplayName());
final Calendar cal = Calendar.getInstance(LOCAL_TZ);
cal.add(Calendar.DATE, DAYS_OFFSET_TO_BE_BEFORE_DST_CHANGE);
final Date start = cal.getTime();
// DST Change: Sunday, 31 March 2013 01:59:59 (local time)
final long oneHour = 3600000L;
for (int i = 0; i < 12; i++) {
final Date date = new Date(start.getTime() + i * oneHour);
System.out.println("UTC: " + date + " toLocal: "
+ new Date(UTCtoLocalMillis(date.getTime())));
}
}
}
3 ответа
Я хочу создать метод, который занимает время в миллисекундах, исходный ZoneID и целевой ZoneID и возвращает преобразованные миллисекунды с использованием нового API JSR 310.
Ваше требование кажется очень запутанным. Значение "миллис", обычно упоминаемое во временных API, - это "эпоха-миллис", число миллисекунд с 1970-01-01T00:00Z. Значение в миллисах всегда относительно UTC. Хотя существует понятие "местный миллис", оно никогда не используется. В частности, конструктор java.util.Date принимает эпохи-миллисы.
Таким образом, код new Date(UTCtoLocalMillis(date.getTime()))
в основном бессмысленно.
ТЛ; др
Instant // Represent a moment in UTC with a resolution of nanoseconds.
.ofEpochMilli( utcMillis ) // Parse a count of milliseconds since epoch of 1970-01-01T00:00:00Z.
.atZone( // Adjust from UTC to some time zone.
ZoneId.of( "Pacific/Auckland" )
)
подробности
Ответ от JodaStephen правильный. Нет такой вещи как "LocalMillis". Кажется, у вас есть концептуальное недоразумение. Это понятно, поскольку работа с датой и временем удивительно сложна.
Совет: учитесь думать и работать в UTC. Как программист или администратор, забудьте о своем часовом поясе. Думайте о UTC как об одном истинном времени. Различные часовые пояса являются всего лишь вариациями. Делайте ваши записи, трассировки, хранения данных и обмена данными все в UTC. Применяйте часовой пояс только для презентации пользователю. Переключение между UTC и вашим собственным приходским часовым поясом приведет вас в замешательство. Поставьте на стол вторые часы с надписью UTC; используйте его во время работы.
Еще один совет: сфокусируйтесь концептуально на графике. Есть только один поток времени. Основным способом представления этой временной шкалы является UTC. Различные часовые пояса представляют собой разные представления одних и тех же моментов на одной временной шкале.
Вот пример кода java.time.
занимает время в миллисекундах, исходный ZoneID и целевой ZoneID и возвращает преобразованные миллисекунды с использованием нового API JSR 310.
Часть, которую вы не понимаете, состоит в том, что "sourceID зоны" всегда является UTC в течение количества миллисекунд с начала эпохи. Любой, кто проходит через миллисекунды, но не в UTC, неопытен и напрашивается на неприятности.
Если у вас есть счет в миллисекундах с начала отсчета эпохи первого момента 1970 года в UTC, 1970-01-01T00:00:00Z, то проанализируйте как Instant
, Instant
представляет момент в UTC.
Instant instant = Instant.ofEpochMilli( utcMillis ) ;
Создать текст в String
представляя момент в этом Instant
, По умолчанию классы java.time используют стандартные форматы ISO 8601 при разборе / генерации строк. Z
в конце означает UTC и произносится как "зулу".
String output = instant.toString() ;
2018-01-23T01: 23: 45.123456789Z
Идти в другом направлении...
long millisecondsFromEpoch = instant.toEpochMilli() ;
С Instant
В свою очередь, вы можете захотеть посмотреть на этот момент через призму часов, используемых людьми определенного региона, часового пояса. Под "временем настенных часов" мы понимаем время суток и дату, которые видит человек, который смотрит на часы и календарь, висящие на их стене.
Укажите правильное название часового пояса в формате continent/region
, такие как America/Montreal
, Africa/Casablanca
, или же Pacific/Auckland
, Никогда не используйте 3-4 буквенное сокращение, такое как EST
или же IST
поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
Мы можем вернуться в UTC, извлекая Instant
,
Instant instant = zdt.toInstant() ; // Same moment, different wall-clock time.
Последний совет: никогда не используйте Date
, Calendar
, SimpleDateFormat
или другие связанные классы даты и времени. Они бесконечно сбивают с толку и расстраивают. Используйте только классы java.time.
О java.time
Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые классы даты и времени, такие как java.util.Date
, Calendar
& SimpleDateFormat
,
Проект Joda-Time, находящийся сейчас в режиме обслуживания, рекомендует перейти на классы java.time.
Чтобы узнать больше, смотрите Oracle Tutorial. И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310.
Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии. Нет необходимости в строках, нет необходимости в java.sql.*
классы.
Где взять классы java.time?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11 и более поздние версии - часть стандартного API Java со встроенной реализацией.
- Java 9 добавляет некоторые незначительные функции и исправления.
- Java SE 6 и Java SE 7
- Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport.
- Android
- Более поздние версии Android связывают реализации классов java.time.
- Для более ранних версий Android (<26) проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше). Смотрите Как использовать ThreeTenABP….
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь, такие как Interval
, YearWeek
, YearQuarter
и многое другое.
Документация Java делает это очень ясным.
См. http://docs.oracle.com/javase/7/docs/api/java/lang/System.html
Возвращает:
the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC*.