Общая поддержка формата ISO 8601 в Java 6
Java 7 представила поддержку в SimpleDateFormat
класс для формата ISO 8601, через символ X
(вместо нижнего или верхнего регистра Z
). Поддержка таких форматов в Java 6 требует предварительной обработки, поэтому наилучшим подходом является вопрос.
Этот новый формат является расширенным Z
(прописная буква Z), с 2 дополнительными вариантами:
- Поле "минуты" является необязательным (то есть действительны 2-значные вместо 4-значных часовых поясов)
- Символ двоеточия (':') можно использовать для отделения двухзначного поля "часы" от двухзначного поля "минуты").
Итак, как видно из документации по Java 7SimpleDateFormat
следующие 3 формата теперь действительны (вместо только второго Z
в Java 6) и, конечно же, эквивалентны:
- -08
- -0800
- -08: 00
Как обсуждалось в предыдущем вопросе об особом случае поддержки такого "расширенного" формата часового пояса, всегда с разделителем ":", лучший подход для обратной передачи функциональности Java 7 в Java 6 заключается в создании подкласса SimpleDateformat
класс и переопределить его parse()
метод, то есть:
public Date parse(String date, ParsePosition pos)
{
String iso = ... // Replace the X with a Z timezone string, using a regex
if (iso.length() == date.length())
{
return null; // Not an ISO 8601 date
}
Date parsed = super.parse(iso, pos);
if (parsed != null)
{
pos.setIndex(pos.getIndex()+1); // Adjust for ':'
}
return parsed;
}
Обратите внимание, что подкласс SimpleDateFormat
объекты выше должны быть инициализированы с соответствующими Z
на основе шаблона, т.е. если подкласс ExtendedSimpleDateformat
и вы хотите разобрать даты, соответствующие шаблону yyyy-MM-dd'T'HH:mm:ssX
то вы должны использовать объекты, созданные как
new ExtendedSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
В вышеупомянутом предыдущем вопросе регулярное выражение :(?=[0-9]{2}$)
было предложено избавиться от ':' и в подобном вопросе регулярное выражение (?<=[+-]\d{2})$
было предложено добавить "минутное" поле как 00
, если нужно.
Очевидно, что успешное выполнение 2 замен может быть использовано для достижения полной функциональности. Итак iso
локальная переменная в переопределенной parse()
метод будет установлен как
iso = date.replaceFirst(":(?=[0-9]{2}$)","");
или же
iso = iso.replaceFirst("(?<=[+-]\\d{2})$", "00");
с if
проверить, чтобы убедиться, что pos
значение также устанавливается позже, а также для length()
Сравнение ранее.
Вопрос в следующем: можем ли мы использовать одно регулярное выражение для достижения того же эффекта, включая информацию, необходимую для ненужной проверки длины и правильной настройки? pos
несколько строк спустя?
Реализация предназначена для кода, который читает очень большое количество строковых полей, которые могут быть в любом формате (даже полностью без даты), выбирает только те, которые соответствуют формату, и возвращает проанализированный Java Date
объект.
Таким образом, точность и скорость имеют первостепенное значение (т. Е. Если использовать 2 прохода быстрее, такой подход предпочтительнее).
3 ответа
Кажется, что вы можете использовать это:
import java.util.Calendar;
import javax.xml.bind.DatatypeConverter;
public class TestISO8601 {
public static void main(String[] args) {
parse("2012-10-01T19:30:00+02:00"); // UTC+2
parse("2012-10-01T19:30:00Z"); // UTC
parse("2012-10-01T19:30:00"); // Local
}
public static Date parse(final String str) {
Calendar c = DatatypeConverter.parseDateTime(str);
System.out.println(str + "\t" + (c.getTime().getTime()/1000));
return c.getTime();
}
}
Вы можете использовать java.time, современный Java-интерфейс даты и времени, в Java 6. Это может показаться мне хорошим и перспективным решением. Имеет хорошую поддержку ISO 8601.
import org.threeten.bp.OffsetDateTime;
import org.threeten.bp.format.DateTimeFormatter;
public class DemoIso8601Offsets {
public static void main(String[] args) {
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+0200",
DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssXX")));
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02",
DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX")));
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02:00"));
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00Z"));
}
}
Выход из этой программы:
2012-10-01T19:30+02:00 2012-10-01T19:30+02:00 2012-10-01T19:30+02:00 2012-10-01T19:30Z
Требуется добавить библиотеку ThreeTen Backport в настройку проекта.
- В Java 8 и более поздних версиях, а также на более новых устройствах Android (начиная с уровня API 26) встроен современный API.
- В Java 6 и 7 получите ThreeTen Backport, бэкпорт новых классов (ThreeTen для JSR 310; см. Ссылки внизу).
- На (более старой) версии Android используется версия Android ThreeTen Backport. Это называется ThreeTenABP. И убедитесь, что вы импортируете классы даты и времени из
org.threeten.bp
с подпакетами.
Как вы можете видеть из кода, +02
а также +0200
требует форматера, где вы указываете формат смещения, в то время как +02:00
(а также Z
тоже) соответствует формату по умолчанию и не требует указания.
Можем ли мы проанализировать все форматы смещения, используя один и тот же форматер?
При чтении смешанных данных вы не хотите обрабатывать каждый формат смещения специально. Лучше использовать дополнительные части в строке шаблона формата:
DateTimeFormatter allInOne
= DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[XXX][XX][X]");
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+0200", allInOne));
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02", allInOne));
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00+02:00", allInOne));
System.out.println(OffsetDateTime.parse("2012-10-01T19:30:00Z", allInOne));
Выход такой же, как указано выше. Квадратные скобки в [XXX][XX][X]
имею ввиду что либо формат +02:00
, +0200
или же +02
может присутствовать.
связи
- Учебник Oracle: Дата и время, объясняющие, как использовать
java.time
, - Запрос спецификации Java (JSR) 310, где
java.time
был впервые описан. - ThreeTen Backport проект, бэкпорт
java.time
в Java 6 и 7 (ThreeTen для JSR-310). - ThreeTenABP, Android-версия ThreeTen Backport
- Вопрос: Как использовать ThreeTenABP в Android Project, с очень подробным объяснением.
Тот же подход работает для разных миллисекунд и разных смещений:
String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S][XXX][XX][X]";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
Date convertDate(String dateString) {
return Date.from(OffsetDateTime.parse(dateString, formatter).toInstant());
}
Иногда вам нужно иметь два разных шаблона для геттера и сеттера:
String DATE_TIME_PATTERN_SET = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S][XXX][XX][X]";
String DATE_TIME_PATTERN_GET = "yyyy-MM-dd'T'HH:mm:ssXXX";
DateTimeFormatter formatterSet = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_SET);
DateFormat dateFormat = new SimpleDateFormat(DATE_TIME_PATTERN_GET);
Date convertToDate(String dateString) {
return Date.from(OffsetDateTime.parse(dateString, formatterSet).toInstant());
}
String convertToString(Date date) {
dateFormat.setTimeZone(TimeZone.getDefault());
return dateFormat.format(date).replaceAll("Z$", "+00:00");
}