Вопрос-ответ

Converting ISO 8601-compliant String to java.util.Date

Преобразование строки, соответствующей стандарту 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);
2024-02-28 14:50 java date