`uuuu` versus` yyyy` в шаблонах кода форматирования `DateTimeFormatter` в Java?

Документация класса DateTimeFormatter говорит о его кодах форматирования за год:

год 2004; 04

год 2004 года; 04

...

Год: Количество букв определяет минимальную ширину поля, ниже которой используется дополнение. Если количество букв равно двум, используется сокращенная двухзначная форма. Для печати это выдает самые правые две цифры. Для синтаксического анализа это будет анализироваться с использованием базового значения 2000, в результате чего в течение года в диапазоне от 2000 до 2099 включительно. Если количество букв меньше четырех (но не двух), то знак выводится только на отрицательные годы в соответствии с SignStyle.NORMAL. В противном случае знак выводится, если ширина прокладки превышена, согласно SignStyle.EXCEEDS_PAD.

Никакое другое упоминание о "эре".

В чем разница между этими двумя кодами, u по сравнению с y, year по сравнению с year-of-era?

Когда следует использовать что-то вроде этого шаблона uuuu-MM-dd и когда yyyy-MM-dd при работе с датами в Java?

Кажется, что код примера, написанный теми, кто знает, использует uuuu, но почему?

Другие классы форматирования, такие как устаревшие SimpleDateFormat, имеют только yyyy, поэтому я смущен, почему java.time приносит этот uuuu для "года эры".

Ответ 1

В рамках java.time -package мы можем сказать:

  • Безопаснее использовать "u" вместо "y", потому что DateTimeFormatter противном случае будет настаивать на наличии эры в сочетании с "y" (= год эры). Поэтому использование "u" позволит избежать некоторых возможных неожиданных исключений при строгом форматировании/разборе. Смотрите также этот SO-пост. Другая незначительная вещь, которая улучшена символом "u" -symbol по сравнению с "y", - это печать/анализ отрицательных григорианских лет (в далеком прошлом).

  • В противном случае мы можем четко заявить, что использование "u" вместо "y" разрушает давние привычки в Java-программировании. Также не совсем понятно, что "и" обозначает любой год, потому что а) первая буква английского слова "год" не соответствует этому символу и б) SimpleDateFormat использовал "и" для другой цели, поскольку Java -7 (ISO-день-номер недели). Путаница гарантирована - навсегда?

  • Мы также должны увидеть, что использование eras (символ "G") в контексте ISO в целом опасно, если рассматривать исторические даты. Если "G" используется с "u", то оба поля не связаны друг с другом. И если "G" используется с "y", то средство форматирования удовлетворяется, но все еще использует лёгкий григорианский календарь, когда историческая дата требует разных календарей и обработки даты.

Исходная информация:

При разработке и интеграции JSR-310 (java.time -package s) дизайнеры решили использовать CLDR/LDML-спецификацию в качестве основы символов шаблона в DateTimeFormatter. Символ "u" уже был определен в CLDR как год пролетического григорианского, поэтому это значение было перенесено в новый грядущий JSR-310 (но не в SimpleDateFormat по причинам обратной совместимости).

Однако это решение следовать CLDR было не совсем последовательным, потому что JSR-310 также ввел новые символы шаблона, которых не было и нет в CLDR, см. Также этот старый билет CLDR. Предложенный символ "I" был изменен CLDR на "VV" и, наконец, заменен JSR-310, включая новые символы "x" и "X". Но "n" и "N" все еще не существуют в CLDR, и, поскольку этот старый билет закрыт, вообще не ясно, будет ли CLDR когда-либо поддерживать его в смысле JSR-310. Кроме того, в билете не упоминается символ "p" (инструкция заполнения в JSR-310, но не определена в CLDR). Поэтому у нас до сих пор нет идеального согласия между определениями шаблонов в разных библиотеках и языках.

И по поводу "у": мы также не должны упускать из виду тот факт, что CLDR связывает этот год эры по крайней мере с каким-то смешанным юлианско-григорианским годом, а не с пролептическим григорианским годом, как JSR-310 (оставляя странность отрицательные годы в стороне). Так что здесь нет идеального соглашения между CLDR и JSR-310.

Ответ 2

В разделе javadoc Шаблоны для форматирования и анализа для DateTimeFormatter перечислены следующие 3 соответствующих символа:

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
 d       day-of-month   number        10
 E       day-of-week    text          Tue; Tuesday; T

day-of-year, day-of-month и day-of-week - это, очевидно, день в пределах данной области (год, месяц, неделя).

Итак, year-of-era означает год в пределах данной области (эры), а прямо над ним era отображается с примерным значением AD (другое значение, являющееся, конечно, BC).

year - подписанный год, где год 0 равен 1 BC, год -1 равен 2 BC и т.д.

Чтобы проиллюстрировать: Когда был Убийца Юлиуса Цезаря?

  • 15 марта, 44 года до нашей эры (с использованием шаблона MMMM d, y GG)
  • 15 марта, -43   (используя шаблон MMMM d, u)

Разумеется, различие будет иметь значение только в том случае, если год равен нулю или отрицателен, и, поскольку это редко, большинству людей это не волнует, хотя они и должны.

Вывод: Если вы используете y, вы также должны использовать G. Поскольку G редко используется, правильный символ года u, а не y, иначе неположительный год будет отображаться некорректно.

Это называется защитное программирование:

Оборонительное программирование - это форма защитного дизайна, предназначенная для обеспечения непрерывной функции части программного обеспечения при непредвиденных обстоятельствах.


Обратите внимание, что DateTimeFormatter соответствует SimpleDateFormat:

Letter  Date or Time Component  Presentation  Examples
------  ----------------------  ------------  --------
G       Era designator          Text          AD
y       Year                    Year          1996; 96

Отрицательные годы всегда были проблемой, и теперь они исправили это, добавив u.

Ответ 3

Короче

  1. Для 99% целей вы можете бросить монету, без разницы, используете ли вы yyyy или uuuu (или используете ли вы yy или uu для двухзначного года).
  2. Это зависит от того, что вы хотите, чтобы произошло на год раньше, чем 1 CE (1 н.э.). Дело в том, что в 99% программ такого года никогда не будет.

Два других ответа уже представили факты того, как u и y очень хорошо работаете, но я все еще чувствовал, что чего-то не хватает, поэтому я привожу немного более основанный на мнении ответ.

Для форматирования

Предполагая, что вы не ожидаете, что год до 1 CE будет отформатирован, лучшее, что вы можете сделать, это проверить это предположение и соответствующим образом отреагировать в случае его поломки. Например, в зависимости от обстоятельств и требований вы можете распечатать сообщение об ошибке или выдать исключение. Одним из очень мягких путей отказа может быть использование шаблона с y (год эры) и G (эра) в этом случае и шаблона с u или y в обычном случае текущей эры. Обратите внимание, что если вы печатаете текущую дату или дату, когда ваша программа была скомпилирована, вы можете быть уверены, что она находится в общей эре и может пропустить проверку.

Для разбора

Во многих (большинстве?) Случаях анализ также означает проверку того, что у вас нет никаких гарантий того, как выглядит ваша входная строка. Обычно это исходит от пользователя или из другой системы. Пример: строка даты выглядит как 2018-09-29. Здесь выбор между uuuu и yyyy должен зависеть от того, что вы хотите сделать в случае, если строка содержит год 0 или отрицательный (например, 0000-08-17 или -012-11-13). Предполагая, что это будет ошибкой, немедленный ответ yyyy: используйте yyyy, чтобы в этом случае было yyyy исключение. Еще лучше: используйте uuuu и после анализа выполните проверку диапазона проанализированной даты. Последний подход учитывает как более точную проверку, так и лучшее сообщение об ошибке в случае ошибки проверки.

Особый случай (уже упомянутый Мено Хохшильдом): Если ваш форматировщик использует строгий стиль распознавателя и содержит y без G, синтаксический анализ всегда будет неудачным, потому что строго говоря, год эры неоднозначен без эры: 1950 может означать 1950 г. н.э. или 1950 г. до н.э. (1950 г. до н.э.), Таким образом, в этом случае вам понадобится u (или предоставление эпохи по умолчанию, это возможно через DateTimeFormatterBuilder).

Короче говоря, снова

Явная проверка диапазона ваших дат, особенно ваших лет, лучше, чем полагаться на выбор между uuuu и yyyy для выявления неожиданных очень ранних лет.