Я хотел бы сделать шаг назад и по-современному взглянуть на этот 10-летний вопрос. Упомянутые классы Date
и XMLGregorianCalendar
уже устарели. Я оспариваю их использование и предлагаю альтернативы.
Date
всегда был плохо спроектирован и ему более 20 лет. Это просто: не используйте его.
XMLGregorianCalendar
тоже стар и имеет старомодный дизайн. Насколько я понимаю, он использовался для создания даты и времени в формате XML для документов XML. Как 2009-05-07T19:05:45.678+02:00
или 2009-05-07T17:05:45.678Z
. Эти форматы достаточно хорошо согласуются с ISO 8601, что классы java.time, современного Java-API даты и времени, могут создавать их, что мы предпочитаем.
Преобразование не требуется
Для многих (большинства?) Целей современная замена Date
будет Instant
. Instant
- это момент времени (как и Date
).
Instant yourInstant = // ...
System.out.println(yourInstant);
Пример вывода из этого фрагмента:
2009-05-07T17: 05: 45.678Z
Это то же самое, что последний из моего примера XMLGregorianCalendar
строк выше. Как большинство из вас знает, это происходит из-за того, что Instant.toString
неявно вызывается System.out.println
. С java.time во многих случаях нам не нужны преобразования, которые в старые времена мы делали между Date
, Calendar
, XMLGregorianCalendar
и другими классами (в некоторых случаях нам нужны преобразования, хотя я покажу вам пару в следующем разделе).
Управление смещением
Ни Date
, ни Instant
не имеют ни часового пояса, ни смещения UTC. Ранее принятый и все еще получивший наибольшее количество голосов ответ Бен Ноланда использует текущий часовой пояс по умолчанию для JVM для выбора смещения XMLGregorianCalendar
. Чтобы включить смещение в современном объекте, мы используем OffsetDateTime
. Например:
ZoneId zone = ZoneId.of("America/Asuncion");
OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
System.out.println(dateTime);
2009-05-07T13: 05: 45.678-04: 00
Опять же, это соответствует формату XML. Если вы хотите снова использовать текущую настройку часового пояса JVM, установите zone
на ZoneId.systemDefault()
.
Что, если мне абсолютно необходим XMLGregorianCalendar?
Есть и другие способы конвертировать Instant
в XMLGregorianCalendar
. Я представлю пару, каждая со своими плюсами и минусами. Во-первых, так же, как XMLGregorianCalendar
создает строку типа 2009-05-07T17:05:45.678Z
, она также может быть построена из такой строки:
String dateTimeString = yourInstant.toString();
XMLGregorianCalendar date2
= DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
System.out.println(date2);
2009-05-07T17: 05: 45.678Z
Pro: это коротко, и я не думаю, что это преподносит какие-либо сюрпризы. Против: Для меня это похоже на пустую трата времени, форматирующее мгновение в строку и анализирующее его обратно.
ZonedDateTime dateTime = yourInstant.atZone(zone);
GregorianCalendar c = GregorianCalendar.from(dateTime);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
System.out.println(date2);
2009-05-07T13: 05: 45.678-04: 00
Pro: это официальное преобразование. Управление смещением происходит естественно. Против: Он проходит больше шагов и поэтому длиннее.
Что если мы получим свидание?
Если вы получили старомодный объект Date
из устаревшего API, который вы не можете себе позволить изменить сейчас, преобразуйте его в Instant
:
Instant i = yourDate.toInstant();
System.out.println(i);
Вывод такой же, как и раньше:
2009-05-07T17: 05: 45.678Z
Если вы хотите управлять смещением, преобразуйте далее в OffsetDateTime
так же, как указано выше.
Если у вас есть старомодный Date
и вам абсолютно необходим старомодный XMLGregorianCalendar
, просто воспользуйтесь ответом Бена Ноланда.
Ссылки