Неправомірний символ шаблону "T" під час аналізу рядка дати на java.util.Date


182

У мене є рядок дати, і я хочу проаналізувати його до звичайної дати, використовуючи API API Java, наступний мій код:

public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    String pattern="yyyy-MM-ddThh:mm:ssZ";
    SimpleDateFormat sdf=new SimpleDateFormat(pattern);
    try {
        Date d=sdf.parse(date);
        System.out.println(d.getYear());
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Однак у мене виняток: java.lang.IllegalArgumentException: Illegal pattern character 'T'

Тож мені цікаво, чи потрібно мені розділити рядок і проаналізувати його вручну?

До речі, я спробував додати один символ цитати в обидві сторони Т:

String pattern="yyyy-MM-dd'T'hh:mm:ssZ";

Це також не працює.


1
чи знаєте ви, що означає це "Т"? Наскільки я думаю, всю дату разом з 'T' слід перетворити на дату (не пропускаючи її через 'Одиночні котирування', але те, що нам не вистачає, - це аналіз синтаксису.
coding_idiot

9
TЧи означає час і є частиною ISO8601 стандарту для міжнародного часу форматування дати.

1
Це дивно. Обгортання Tєдиних лапок працювало для мене (принаймні, з точки зору вирішення помилки розбору).
Агі Хаммертіф

Відповіді:


203

Оновлення для Java 8 та новіших версій

Тепер ви можете просто зробити Instant.parse("2015-04-28T14:23:38.521Z")і отримати правильну річ, тим більше, що ви повинні використовувати Instantзамість того, що ви працюєте java.util.Dateз останніми версіями Java.

Ви також повинні використовувати DateTimeFormatterзамість цього SimpleDateFormatter.

Оригінальний відповідь:

Подане нижче пояснення все ще діє як те, що представляє формат. Але він був написаний ще до того, як Java 8 була всюдисущою, тому вона використовує старі класи, які ви не повинні використовувати, якщо ви використовуєте Java 8 або новіші.

Це працює з введенням із заднім числом, Zяк показано:

У шаблоні Tвиділяється з 'обох боків.

Шаблон для Zкінця насправді такий, XXXяк це зафіксовано в JavaDoc для SimpleDateFormat, просто не дуже зрозуміло, як саме його використовувати, оскільки Zє маркером і для старої TimeZoneінформації.

Q2597083.java

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class Q2597083
{
    /**
     * All Dates are normalized to UTC, it is up the client code to convert to the appropriate TimeZone.
     */
    public static final TimeZone UTC;

    /**
     * @see <a href="http://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations">Combined Date and Time Representations</a>
     */
    public static final String ISO_8601_24H_FULL_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

    /**
     * 0001-01-01T00:00:00.000Z
     */
    public static final Date BEGINNING_OF_TIME;

    /**
     * 292278994-08-17T07:12:55.807Z
     */
    public static final Date END_OF_TIME;

    static
    {
        UTC = TimeZone.getTimeZone("UTC");
        TimeZone.setDefault(UTC);
        final Calendar c = new GregorianCalendar(UTC);
        c.set(1, 0, 1, 0, 0, 0);
        c.set(Calendar.MILLISECOND, 0);
        BEGINNING_OF_TIME = c.getTime();
        c.setTime(new Date(Long.MAX_VALUE));
        END_OF_TIME = c.getTime();
    }

    public static void main(String[] args) throws Exception
    {

        final SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_24H_FULL_FORMAT);
        sdf.setTimeZone(UTC);
        System.out.println("sdf.format(BEGINNING_OF_TIME) = " + sdf.format(BEGINNING_OF_TIME));
        System.out.println("sdf.format(END_OF_TIME) = " + sdf.format(END_OF_TIME));
        System.out.println("sdf.format(new Date()) = " + sdf.format(new Date()));
        System.out.println("sdf.parse(\"2015-04-28T14:23:38.521Z\") = " + sdf.parse("2015-04-28T14:23:38.521Z"));
        System.out.println("sdf.parse(\"0001-01-01T00:00:00.000Z\") = " + sdf.parse("0001-01-01T00:00:00.000Z"));
        System.out.println("sdf.parse(\"292278994-08-17T07:12:55.807Z\") = " + sdf.parse("292278994-08-17T07:12:55.807Z"));
    }
}

Випускає такий вихід:

sdf.format(BEGINNING_OF_TIME) = 0001-01-01T00:00:00.000Z
sdf.format(END_OF_TIME) = 292278994-08-17T07:12:55.807Z
sdf.format(new Date()) = 2015-04-28T14:38:25.956Z
sdf.parse("2015-04-28T14:23:38.521Z") = Tue Apr 28 14:23:38 UTC 2015
sdf.parse("0001-01-01T00:00:00.000Z") = Sat Jan 01 00:00:00 UTC 1
sdf.parse("292278994-08-17T07:12:55.807Z") = Sun Aug 17 07:12:55 UTC 292278994

26

тл; д-р

Використовуйте java.time.Instantклас для розбору тексту у стандартному форматі ISO 8601, що відображає момент у UTC.

Instant.parse( "2010-10-02T12:23:23Z" )

ISO 8601

Цей формат визначається стандартом ISO 8601 для форматів рядків дати-часу.

Обидва:

… Використовувати формати ISO 8601 за замовчуванням для розбору та генерації рядків.

Як правило, слід уникати використання старих класів java.util.Date /.Calendar & java.text.SimpleDateFormat, оскільки вони, як відомо, є клопітливими, заплутаними та хибними. Якщо потрібно для взаємодії, ви можете перетворитись сюди.

java.time

Вбудований в Java 8 і пізніших версій є новою основою java.time . Натхненний Joda-Time , визначений JSR 310 , і розширений проектом ThreeTen-Extra .

Instant instant = Instant.parse( "2010-10-02T12:23:23Z" );  // `Instant` is always in UTC.

Перетворити на старий клас.

java.util.Date date = java.util.Date.from( instant );  // Pass an `Instant` to the `from` method.

Часовий пояс

Якщо потрібно, ви можете призначити часовий пояс.

ZoneId zoneId = ZoneId.of( "America/Montreal" ); // Define a time zone rather than rely implicitly on JVM’s current default time zone.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );  // Assign a time zone adjustment from UTC.

Перетворити.

java.util.Date date = java.util.Date.from( zdt.toInstant() );  // Extract an `Instant` from the `ZonedDateTime` to pass to the `from` method.

Joda-Time

ОНОВЛЕННЯ: Проект Joda-Time зараз знаходиться в режимі обслуговування. Команда радить міграцію до класів java.time .

Ось приклад коду в Joda-Time 2.8.

org.joda.time.DateTime dateTime_Utc = new DateTime( "2010-10-02T12:23:23Z" , DateTimeZone.UTC );  // Specifying a time zone to apply, rather than implicitly assigning the JVM’s current default.

Перетворити на старий клас. Зауважте, що призначений часовий пояс втрачається при перетворенні, оскільки juDate не може бути призначений часовому поясу.

java.util.Date date = dateTime_Utc.toDate(); // The `toDate` method converts to old class.

Часовий пояс

Якщо потрібно, ви можете призначити часовий пояс.

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTime_Montreal = dateTime_Utc.withZone ( zone );

Таблиця типів дат і часу на Java, як сучасна, так і застаріла.


Про java.time

Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date, Calendar, і SimpleDateFormat.

Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти до класів java.time .

Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .

Ви можете обмінюватися об'єктами java.time безпосередньо зі своєю базою даних. Використовуйте драйвер JDBC, сумісний з JDBC 4.2 або пізнішої версії. Немає потреби в струнах, немає потреби в java.sql.*заняттях.

Де отримати класи java.time?

  • Java SE 8 , Java SE 9 та новіших версій
    • Вбудований.
    • Частина стандартного Java API з пакетною реалізацією.
    • Java 9 додає деякі незначні функції та виправлення.
  • Java SE 6 та Java SE 7
    • Значна частина функцій java.time повертається до Java 6 і 7 у ThreeTen-Backport .
  • Android
    • Пізніші версії реалізації пакетів Android для класів java.time.
    • Для більш ранньої Android (<26), то ThreeTenABP проект адаптує ThreeTen-Backport (згаданий вище). Див. Як користуватися ThreeTenABP… .

Таблиця, яку бібліотеку java.time використовувати з якою версією Java чи Android.

Проект ThreeTen-Extra розширює java.time додатковими класами. Цей проект є доказом можливих майбутніх доповнень до java.time. Ви можете знайти деякі корисні класи тут , такі як Interval, YearWeek, YearQuarter, і більш .


1
@AalapPatel Будьте в курсі, що проект Joda-Time зараз знаходиться в режимі обслуговування. Команда радить міграцію до класів java.time. Обидва зусилля веде той самий чоловік, Стівен Колбурн.
Василь Бурк

Дякую, я використовую це для отримання мілісекунд з поверненого часу сервера UTC та / або часу локалі та добре працює для мене в цьому випадку ..
Aalap Patel

Instant.parse потребує min api рівня 26 в андроїді
Naveed Ahmad

1
@NaveedAhmad Дивіться пункти внизу відповіді про проекти ThreeTen-Backport та ThreeTenABP .
Василь Бурк

0

На сьогоднішній день є дві відповіді, і вони обидві довгі (і tl; dr занадто короткий IMHO), тому я пишу резюме зі свого досвіду, починаючи використовувати нову бібліотеку java.time (застосовна, як зазначено в інших відповідях на версію Java 8+). ISO 8601 встановлює стандартний спосіб запису дат: YYYY-MM-DDтаким чином формат дати-часу є лише таким, як показано нижче (може бути 0, 3, 6 або 9 цифр протягом мілісекунд), і не потрібен рядок форматування:

import java.time.Instant;
public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    try {
        Instant myDate = Instant.parse(date);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Мені це не знадобилося, але оскільки отримання року в коді від питання, то:
він складніший, не може бути зроблений Instantбезпосередньо, можна зробити за допомогою Calendarпитань Отримати ціле значення поточного року в Java та Перетворення Java. час до календаря, але IMHO як фіксований формат підрядка простіший у використанні:

myDate.toString().substring(0,4);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.