Какой шаблон следует использовать для анализа строк даты и времени RFC 3339 в Java

Кажется, это общий вопрос с множеством разных ответов. Прежде чем ответить, я использовал как joda-time, так и atomdate, и они отлично работают. Меня интересует не какая библиотека использовать, а разъяснение того, как шаблон RFC должен быть определен в Java.


Исследование

Из моего понимания и этого ответа RFC 3339 является профилем ISO 8601. PHP четко определяет шаблон даты и времени RFC 3339, который должен быть Y-m-d\TH:i:sP, Если бы мы перенесли это определение в Java 7 (насколько мне известно), мы бы получили следующее (что также упоминается в этом ответе):

// example "2005-08-15T15:52:01+00:00"
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX";

Тем не менее, несколько ответов переполнения стека, такие как этот, указывают на один из них (или оба) как на правильный шаблон для RFC 3339

// example "2016-11-01T20:44:39Z"
pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'";

// example "1937-01-01T12:00:27.87Z"
pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

Чтобы еще больше усложнить ситуацию, в официальной документации RFC 3339 перечислены все эти следующие примеры (я добавил, что, как мне кажется, их соответствующие шаблоны):

// 1996-12-19T16:39:57-08:00
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX";

// 1990-12-31T23:59:60Z
pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'";

// 1990-12-31T15:59:60-08:00
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX";

// 1937-01-01T12:00:27.87+00:20
pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

Примечание: Android не поддерживает XXX шаблон для часовых поясов, но вы можете использовать ZZZZZ вместо этого согласно этому ответу.

Я думаю, что часть того, что меня смущает, это то, что я всегда видел RFC 822 и RFC 2822, на которые конкретно ссылается по одному шаблону каждый, поэтому я предположил, что RFC 3339 также можно свести к одному сопоставлению шаблонов:

static String RFC_822 = "EEE, dd MMM yy HH:mm:ss zzz";
static String RFC_2822 = "EEE, dd MMM yyyy HH:mm:ss zzz";

Мой вывод

В отличие от php, RFC 3339 не может быть представлен в Java, используя только одно соответствующее выражение. Вместо этого все они являются действительными шаблонами RFC 3339 и должны быть проверены при разборе строки datetime через SimpleDateFormat:

static String[] RFC_3339_VARIANTS = {
        "yyyy-MM-dd'T'HH:mm:ss'Z'",
        "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
        "yyyy-MM-dd'T'HH:mm:ssXXX",
        "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
};

Обновить

Чтобы усложнить ситуацию, SimpleDateFormat, похоже, неправильно обрабатывает литерал часового пояса 'Z'. Вместо того, чтобы использовать UTC, как положено, по умолчанию используется либо PST, либо ваше местное время (я не уверен, какое). Это означает, что вам может потребоваться вручную заменить литералы 'Z' на +00:00, чтобы исправить это поведение?


Суть

Как и предполагалось, я создал служебный класс Gist, который включает мой текущий запущенный код. Это должно работать на Android, а также быть совместимым с Java 7+. Пожалуйста, не стесняйтесь задавать любые вопросы или оставлять комментарии. Если есть достаточно интереса, я могу перенести его на Github, чтобы другие люди могли внести свой вклад:

https://gist.github.com/oseparovic/d9ee771927ac5f3aefc8ba0b99c0cf38


Я правильно понимаю, или я полностью отключен? Я был бы очень признателен за любые разъяснения, которые вы, ребята, можете дать о том, как анализировать строки RFC 3339 в Java 7.

1 ответ

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

И именно поэтому Joda и Java 8 имеют специальные парсеры для ISO 8601 (надмножество).

Я знаю, что вам не нужны ссылки на другие библиотеки, но для других, которые используют Java 8 или хотят явно ограничиться RFC 3339 (парсеры joda iso, я полагаю, примут еще больше форматов, чем rfc 3339), есть эта библиотека: https://github.com/ethlo/itu

Другие вопросы по тегам