Как я могу анализировать / форматировать даты с помощью LocalDateTime? (Java 8)
В Java 8 добавлен новый java.time API для работы с датами и временем (JSR 310).
У меня есть дата и время в виде строки (например, "2014-04-08 12:30"
). Как я могу получить LocalDateTime
экземпляр из данной строки?
После того, как я закончил работу с LocalDateTime
объектом: как я могу затем преобразовать LocalDateTime
экземпляр обратно в строку с тем же форматом, что показан выше?
Переведено автоматически
Ответ 1
Анализ даты и времени
Чтобы создать LocalDateTime
объект из строки, вы можете использовать статический LocalDateTime.parse()
метод. Он принимает строку и DateTimeFormatter
в качестве параметра. DateTimeFormatter
используется для указания шаблона даты / времени.
String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);
Форматирование даты и времени
Чтобы создать форматированную строку из LocalDateTime
объекта, вы можете использовать format()
метод.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"
Обратите внимание, что в DateTimeFormatter
есть некоторые часто используемые форматы даты / времени, предопределенные в качестве констант. Например: использование DateTimeFormatter.ISO_DATE_TIME
для форматирования LocalDateTime
экземпляра выше приведет к получению строки "1986-04-08T12:30:00"
.
Методы parse()
и format()
доступны для всех объектов, связанных с датой / временем (например, LocalDate
или ZonedDateTime
)
Ответ 2
Вы также можете использовать LocalDate.parse()
or LocalDateTime.parse()
для String
без предоставления шаблона, если String
находится в формате ISO 8601.
Например,
String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);
String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);
Вывод,
Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30
И используйте DateTimeFormatter
только в том случае, если вам приходится иметь дело с другими шаблонами дат.
Например, в следующем примере, dd MMM uuuu представляет день месяца (две цифры), три буквы названия месяца (январь, февраль, март, ...) и четырехзначный год:
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);
Вывод
04 Aug 2015 parses to 2015-08-04
также помните, что DateTimeFormatter
объект является двунаправленным; он может как анализировать входные данные, так и форматировать выходные данные.
String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));
Вывод
2015-08-04 formats as 04 Aug 2015
(Смотрите Полный список шаблонов для форматирования и синтаксического анализа DateFormatter.)
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
Ответ 3
Оба ответа Суфияна Гори и миши очень хорошо объясняют вопрос, касающийся строковых шаблонов. Однако, на всякий случай, если вы работаете с ISO 8601, нет необходимости применять DateTimeFormatter
поскольку LocalDateTime уже подготовлен для этого:
Преобразуйте LocalDateTime в строку ISO 8601 часового пояса
LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); // You might use a different zone
String iso8601 = zdt.toString();
Преобразовать строку ISO8601 обратно в LocalDateTime
String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();
Ответ 4
Разбор строки с датой и временем в определенный момент времени (Java называет это "Instant
") довольно сложен. Java решала это в нескольких итерациях. Последняя версия, java.time
и java.time.chrono
, покрывает почти все потребности (за исключением замедления времени :) ).
Однако такая сложность вносит много путаницы.
Ключ к пониманию анализа дат - это:
LocalDateTime
, ZonedDateTime
и др. такой сложныйСуществуют часовые пояса. Часовой пояс - это, по сути, "полоса" * [3] поверхности Земли, власти которой следуют тем же правилам, когда и какое смещение по времени. Сюда входят правила летнего времени.
Часовые пояса меняются со временем для разных областей, в основном в зависимости от того, кто кого завоевывает. И правила одного часового пояса меняются со временем также.
Существуют смещения по времени. Это не то же самое, что часовые пояса, потому что часовым поясом может быть, например, "Прага", но у него смещение по летнему и зимнему времени.
Если вы получаете временную метку с часовым поясом, смещение может варьироваться в зависимости от того, в какой части года это происходит. Во время високосного часа временная метка может означать два разных времени, поэтому без дополнительной информации ее невозможно надежно преобразовать.
Примечание: Под меткой времени я подразумеваю "строку, содержащую дату и / или время, необязательно с часовым поясом и / или смещением по времени".
Несколько часовых поясов могут использовать одинаковое смещение времени для определенных периодов. Например, часовой пояс GMT / UTC совпадает с часовым поясом "Лондон", когда летнее смещение времени не действует.
Чтобы немного усложнить (но это не слишком важно для вашего варианта использования):
Ученые наблюдают за динамикой Земли, которая меняется с течением времени; основываясь на этом, они добавляют секунды в конце отдельных лет. (Таким образом, 24:00:00 2040-12-31 может быть допустимой датой-временем.) Для этого требуется регулярное обновление метаданных, которые системы используют для правильного преобразования дат. Например, в Linux вы получаете регулярные обновления пакетов Java, включая эти новые данные.
Обновления не всегда сохраняют предыдущее поведение как для исторических, так и для будущих временных меток. Таким образом, может случиться так, что анализ двух временных меток с учетом изменения некоторого часового пояса, сравнивая их, может дать разные результаты при запуске в разных версиях программного обеспечения. Это также относится к сравнению между затронутым часовым поясом и другим часовым поясом.
Если это вызовет ошибку в вашем программном обеспечении, рассмотрите возможность использования какой-либо временной метки, в которой нет таких сложных правил, как временная метка Unix.
Из-за версии 7 для будущих дат мы не можем точно преобразовать даты. Так, например, текущий синтаксический анализ 8524-02-17 12:00:00 может быть отключен через пару секунд после будущего синтаксического анализа.
java.util.Date
который имел немного наивный подход, предполагающий, что есть только год, месяц, день и время. Этого быстро оказалось недостаточно.java.sql.Date
со своими собственными ограничениями.Calendar
API.java.time
Когда вы используете строку временной метки, вам нужно знать, какую информацию она содержит. Это ключевой момент. Если вы не сделаете это правильно, в итоге вы получите загадочные исключения, такие как "Невозможно создать Instant", "Отсутствует смещение зоны", "неизвестный идентификатор зоны" и т.д.
Содержит ли он дату и время?
Есть ли у него смещение по времени? Смещение по времени - это часть + чч: мм. Иногда +00:00 может быть заменен на Z как "время Зулу", UTC
как всемирное координированное время или GMT как среднее время по Гринвичу. Они также устанавливают часовой пояс. Для этих временных меток вы используете OffsetDateTime
.
Есть ли у него часовой пояс? Для этих временных меток вы используете ZonedDateTime
. Зона указывается либо с помощью
Список часовых поясов составляется "базой данных TZ", поддерживаемой ICAAN.
Согласно ZoneId
javadoc, идентификатор зоны также может быть каким-то образом указан как Z и offset . Я не уверен, как это соотносится с реальными зонами.
Если временная метка, которая имеет только TZ , попадает в високосный час изменения временного смещения, то это неоднозначно, и интерпретация является предметом ResolverStyle
, см. Ниже.
Если у него нет ни того, ни другого, то предполагается отсутствующий контекст или им пренебрегают. И решать должен потребитель. Поэтому его необходимо проанализировать как LocalDateTime
и преобразовать в OffsetDateTime
, добавив недостающую информацию:
Duration
), или когда вы не знаете, и это действительно не имеет значения (например, расписание местного автобуса).Частичная информация о времени
LocalDate
, LocalTime
, OffsetTime
MonthDay
, Year
YearMonth
,,, или,, из нее.Если у вас есть полная информация, вы можете получить java.time.Instant
. Это также используется внутри для преобразования между OffsetDateTime
и ZonedDateTime
.
Существует обширная документация по DateTimeFormatter
, которая может как анализировать строку метки времени, так и форматировать в строку.
Предварительно созданные DateTimeFormatter
s должны охватывать более или менее все стандартные форматы временных меток. Например, ISO_INSTANT
can parse 2011-12-03T10:15:30.123457Z
.
Если у вас есть какой-то специальный формат, то вы можете создать свой собственный DateTimeFormatter (который также является анализатором).
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
Я рекомендую взглянуть на исходный код DateTimeFormatter
и черпать вдохновение в том, как создать его с помощью DateTimeFormatterBuilder
. Пока вы там, также взгляните на ResolverStyle
который определяет, является ли анализатор МЯГКИМ, ИНТЕЛЛЕКТУАЛЬНЫМ или СТРОГИМ для форматов и неоднозначной информации.
Итак, частой ошибкой является углубляться в сложность TemporalAccessor
. Это связано с тем, как разработчики привыкли работать с SimpleDateFormatter.parse(String)
. Правильно, DateTimeFormatter.parse("...")
дает вам TemporalAccessor
.
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
Но, обладая знаниями из предыдущего раздела, вы можете удобно выполнить синтаксический анализ до нужного вам типа:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
На самом деле вам это тоже не нужно DateTimeFormatter
. Типы, которые вы хотите проанализировать, имеют parse(String)
методы.
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
Что касается TemporalAccessor
, вы можете использовать это, если у вас есть смутное представление о том, какая информация содержится в строке, и вы хотите принять решение во время выполнения.
Я надеюсь, что пролил свет понимания на вашу душу :)
Примечание: Есть бэкпорт из java.time
в Java 6 и 7: ThreeTen-Backport. Для Android у него есть ThreeTenABP.
[3] Дело не только в том, что они не являются полосами, но и в некоторых странных крайностях. Например, некоторые соседние острова Тихого океана имеют часовые пояса +14:00 и -11:00. Это означает, что хотя на одном острове 1 мая в 3 часа дня, на другом острове не так далеко, все еще 12 часов 30 апреля (если я правильно посчитал :))