Преобразование строки, соответствующей стандарту ISO 8601, в java.util.Date
Я пытаюсь преобразовать строку в формате ISO 8601 в java.util.Date
.
Я обнаружил, что шаблон yyyy-MM-dd'T'HH:mm:ssZ
соответствует стандарту ISO8601, если используется с языковым стандартом (сравните образец).
Однако, используя java.text.SimpleDateFormat
, я не могу преобразовать строку в правильном формате 2010-01-01T12:00:00+01:00
. Сначала я должен преобразовать ее в 2010-01-01T12:00:00+0100
, без двоеточия.
Итак, текущее решение таково
SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.GERMANY);
String date = "2010-01-01T12:00:00+01:00".replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
System.out.println(ISO8601DATEFORMAT.parse(date));
что, очевидно, не очень приятно. Я что-то упускаю или есть решение получше?
Ответ
Благодаря комментарию ХуанЗе я нашел Joda-Time волшебство, оно также описано здесь .
Итак, решение таково
DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate = "2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));
Или, проще говоря, используйте анализатор по умолчанию через конструктор:
DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;
Для меня это приятно.
Переведено автоматически
Ответ 1
К сожалению, форматы часовых поясов, доступные в SimpleDateFormat (Java 6 и более ранних версий), не соответствуют стандарту ISO 8601. SimpleDateFormat понимает строки часового пояса, такие как "GMT + 01:00" или "+ 0100", последнее в соответствии с RFC # 822.
Даже если Java 7 добавила поддержку дескрипторов часовых поясов в соответствии с ISO 8601, SimpleDateFormat по-прежнему не может должным образом проанализировать полную строку даты, поскольку она не поддерживает необязательные части.
Переформатирование входной строки с помощью регулярного выражения, безусловно, является одной из возможностей, но правила замены не так просты, как в вашем вопросе:
- Некоторые часовые пояса отключены не на все часы UTC, поэтому строка не обязательно заканчивается на ":00".
- ISO8601 позволяет включать в часовой пояс только количество часов, поэтому "+ 01" эквивалентно "+01:00"
- ISO8601 позволяет использовать "Z" для обозначения UTC вместо "+00:00".
Возможно, более простым решением является использование преобразователя типов данных в JAXB, поскольку JAXB должен уметь анализировать строку даты ISO8601 в соответствии со спецификацией XML-схемы. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z")
даст вам Calendar
объект, и вы можете просто использовать getTime() для него, если вам нужен Date
объект.
Вероятно, вы могли бы также использовать Joda-Time, но я не знаю, почему вы должны беспокоиться об этом (обновление 2022; возможно, потому, что весь javax.xml.bind
раздел отсутствует в javax.xml
пакете Android).
Ответ 2
Способ, который благословлен документацией по Java 7:
DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
String string1 = "2001-07-04T12:08:56.235-0700";
Date result1 = df1.parse(string1);
DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String string2 = "2001-07-04T12:08:56.235-07:00";
Date result2 = df2.parse(string2);
Вы можете найти больше примеров в разделе Примеры на SimpleDateFormat javadoc.
ОБНОВЛЕНИЕ 13.02.2020: В Java 8 есть совершенно новый способ сделать это
Ответ 3
Хорошо, на этот вопрос уже дан ответ, но я все равно оставлю свой ответ. Возможно, кому-то это поможет.
Я искал решение для Android (API 7).
- О Joda не могло быть и речи - она огромна и страдает от медленной инициализации. Это также казалось серьезным излишеством для этой конкретной цели.
- Ответы, связанные с
javax.xml
, не будут работать в Android API 7.
В итоге был реализован этот простой класс. Он охватывает только наиболее распространенную форму строк ISO 8601, но в некоторых случаях этого должно быть достаточно (когда вы совершенно уверены, что входные данные будут в этом формате).
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* Helper class for handling a most common subset of ISO 8601 strings
* (in the following format: "2008-03-01T13:00:00+01:00"). It supports
* parsing the "Z" timezone, but many other less-used features are
* missing.
*/
public final class ISO8601 {
/** Transform Calendar to ISO 8601 string. */
public static String fromCalendar(final Calendar calendar) {
Date date = calendar.getTime();
String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.format(date);
return formatted.substring(0, 22) + ":" + formatted.substring(22);
}
/** Get current date and time formatted as ISO 8601 string. */
public static String now() {
return fromCalendar(GregorianCalendar.getInstance());
}
/** Transform ISO 8601 string to Calendar. */
public static Calendar toCalendar(final String iso8601string)
throws ParseException {
Calendar calendar = GregorianCalendar.getInstance();
String s = iso8601string.replace("Z", "+00:00");
try {
s = s.substring(0, 22) + s.substring(23); // to get rid of the ":"
} catch (IndexOutOfBoundsException e) {
throw new ParseException("Invalid length", 0);
}
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(s);
calendar.setTime(date);
return calendar;
}
}
Примечание по производительности: Я каждый раз создаю новый SimpleDateFormat, чтобы избежать ошибки в Android 2.1. Если вы так же удивлены, как и я, посмотрите на эту загадку. Для других движков Java вы можете кэшировать экземпляр в частном статическом поле (используя ThreadLocal для потокобезопасности).
Ответ 4
java.time
java.time API (встроенный в Java 8 и более поздние версии) немного упрощает эту задачу.
Если вы знаете, что входные данные указаны в UTC, например, в Z
(для Zulu) в конце, Instant
класс может выполнить синтаксический анализ.
Date date = Date.from(Instant.parse("2014-12-12T10:39:40Z"));
Если ваши входные данные могут представлять собой другие значения с смещением от UTC, а не с UTC, обозначенные Z
(Zulu) в конце, используйте OffsetDateTime
класс для синтаксического анализа.
OffsetDateTime odt = OffsetDateTime.parse("2010-01-01T12:00:00+01:00");
Затем извлеките Instant
и преобразуйте в java.util.Date
путем вызова from
.
Instant instant = odt.toInstant(); // Instant is always in UTC.
Date date = Date.from(instant);