DateTimeFormatter несовместим с SimpleDateFormat
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
public class Test001 {
public static void main(String[] args) {
Date dt = new Date();
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS zzz").withZone(ZoneId.of("America/New_York"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");
System.out.println(formatter.format(localDateTime));
System.out.println(sdf.format(dt));
}
}
Выход
2016-04-19 11:25:34.917 ET
2016-04-19 11:25:34.637 EDT
Часовой пояс моего ноутбука - "Америка/Нью-Йорк" (т.е. восточное время США/Канада).
Интересно, как получить "EDT" вместо "ET", используя DateTimeFormatter
.
Интересно также, в целом: почему DateTimeFormatter
несовместим с SimpleDataFormat
с точки зрения шаблонов разбора/формата (как показано в этом примере, они несовместимы).
Ответы
Ответ 1
Формат, который вы видите "ET", известен как формат "Общий не-местонахождение" в Язык разметки языка Unicode (LDML), Формат, который вы хотите, "EDT", известен как формат "Специальный формат".
Соответствующий исходный код в DateTimeFormatterBuilder
проверяет, может ли объект даты и времени ChronoField.INSTANT_SECONDS
. Если это возможно, тогда используется формат "конкретного не-местоположения", если не используется формат "общего не-местоположения".
Поскольку вы форматируете LocalDateTime
, который не поддерживает доступ к ChronoField.INSTANT_SECONDS
, вы получаете "общий не-местоположение" ". Чтобы получить желаемый результат, используйте ZonedDateTime
или OffsetDateTime
вместо этого. (Этот момент необходим, чтобы определить, является ли это летнее или зимнее время.)
Обратите внимание, что SimpleDateFormat
и DateTimeFormatter
действительно отличаются друг от друга, и неправильно считать, что шаблоны идентичны. Было принято решение повторно синхронизировать DateTimeFormatter
с спецификацией LDML, которая будет иметь преимущества в будущем.
Этот ответ дает решение и объяснение, однако я должен отметить, что код JDK здесь слишком суровый. Поскольку вы поставляете как LocalDateTime
, так и ZoneId
, код должен быть способен сделать лучше и определить ChronoField.INSTANT_SECONDS
на летать и, таким образом, использовать формат "конкретного не-местоположения". Таким образом, я думаю, что здесь есть граничный случай JDK.
Ответ 2
Вы не должны работать с LocalDateTime
. (Почти) эквивалент Date
равен Instant
. Вы хотите отформатировать Instant
, как если бы он был в вашем часовом поясе. Так
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS zzz").withZone(ZoneId.of("America/New_York"));
Instant now = Instant.now();
System.out.println(formatter.format(now));
который печатает
2016-04-19 12:07:57.684 EDT
В качестве альтернативы вы можете использовать ZonedDateTime
без установки ZoneId
для DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS zzz");
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York")))
System.out.println(formatter.format(now));
который также печатает
2016-04-19 12:11:01.667 EDT
У меня нет документации о том, почему все это работает, но суть в том, что эти временные объекты (объект даты, который вы пытаетесь форматировать) имеют смещения, которые имеют правила зоны, которые могут быть летнее время или нет, Форматтер использует это в своей печати.
A LocalDateTime
не имеет смещения или часового пояса. Ваш форматир переопределяет его, но со смещением, которое не является летнее время.
Ответ 3
Наиболее прямой подход (и, вероятно, также вызывающий некоторые проблемы с летним/зимним временем, выглядит следующим образом:
public static void main(String[] args) {
Date dt = new Date();
LocalDateTime localDateTime = LocalDateTime.now();
String dateFormat;
if (Calendar.getInstance().get(Calendar.DST_OFFSET) != 0) {
dateFormat = "yyyy-MM-dd HH:mm:ss.SSS 'EDT'";
} else {
dateFormat = "yyyy-MM-dd HH:mm:ss.SSS 'EST'";
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat).withZone(ZoneId.of("America/New_York"));
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
System.out.println(formatter.format(localDateTime));
System.out.println(sdf.format(dt));
}