将日期字符串解析为 java.util.Date 时出现非法模式字符“T”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2597083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:40:52  来源:igfitidea点击:

Illegal pattern character 'T' when parsing a date string to java.util.Date

javadatesimpledateformatiso8601

提问by hguser

I have a date string and I want to parse it to normal date use the java Date API,the following is my code:

我有一个日期字符串,我想使用 java Date API 将其解析为正常日期,以下是我的代码:

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();
    }
}

However I got an exception: java.lang.IllegalArgumentException: Illegal pattern character 'T'

但是我有一个例外: java.lang.IllegalArgumentException: Illegal pattern character 'T'

So I wonder if I have to split the string and parse it manually?

所以我想知道我是否必须拆分字符串并手动解析它?

BTW, I have tried to add a single quote character on either side of the T:

顺便说一句,我试图在 T 的任一侧添加一个单引号字符:

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

It also does not work.

它也不起作用。

采纳答案by hguser

Update for Java 8 and higher

Java 8 及更高版本的更新

You can now simply do Instant.parse("2015-04-28T14:23:38.521Z")and get the correct thing now, especially since you should be using Instantinstead of the broken java.util.Datewith the most recent versions of Java.

您现在可以简单地做Instant.parse("2015-04-28T14:23:38.521Z")并得到正确的东西,特别是因为您应该使用最新版本的 JavaInstant而不是损坏java.util.Date的。

You should be using DateTimeFormatterinstead of SimpleDateFormatteras well.

你应该使用DateTimeFormatter而不是SimpleDateFormatter

Original Answer:

原答案:

The explanation below is still valid as as what the format represents. But it was written before Java 8 was ubiquitous so it uses the old classes that you should not be using if you are using Java 8 or higher.

下面的解释作为格式所代表的内容仍然有效。但它是在 Java 8 普及之前编写的,因此它使用了旧类,如果您使用的是 Java 8 或更高版本,则不应使用这些类。

This works with the input with the trailing Zas demonstrated:

这适用于带有尾随的输入,Z如下所示:

In the pattern the Tis escaped with 'on either side.

The pattern for the Zat the end is actually XXXas documented in the JavaDoc for SimpleDateFormat, it is just not very clear on actually how to use it since Zis the marker for the old TimeZoneinformation as well.

在模式中, 两边T都转义了'

最后的模式Z实际上XXX是在 JavaDoc for 中记录的SimpleDateFormat,实际上如何使用它并不是很清楚,因为它Z也是旧TimeZone信息的标记 。

Q2597083.java

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"));
    }
}

Produces the following output:

产生以下输出:

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

回答by Basil Bourque

tl;dr

tl;博士

Use java.time.Instantclass to parse text in standard ISO 8601 format, representing a moment in UTC.

使用java.time.Instantclass 解析标准 ISO 8601 格式的文本,表示 UTC 中的一个时刻。

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

ISO 8601

ISO 8601

That format is defined by the ISO 8601standard for date-time string formats.

该格式由日期时间字符串格式的ISO 8601标准定义。

Both:

两个都:

…use ISO 8601 formats by default for parsing and generating strings.

...默认使用 ISO 8601 格式来解析和生成字符串。

You should generally avoid using the old java.util.Date/.Calendar & java.text.SimpleDateFormat classes as they are notoriously troublesome, confusing, and flawed. If required for interoperating, you can convert to and fro.

您通常应该避免使用旧的 java.util.Date/.Calendar 和 java.text.SimpleDateFormat 类,因为它们是出了名的麻烦、混乱和有缺陷。如果需要互操作,您可以来回转换。

java.time

时间

Built into Java 8 and later is the new java.timeframework. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extraproject.

Java 8 及更高版本中内置了新的java.time框架。受Joda-Time 的启发,由JSR 310定义,并由ThreeTen-Extra项目扩展。

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

Convert to the old class.

转换为旧类。

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

Time Zone

时区

If needed, you can assign a time zone.

如果需要,您可以分配时区。

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.

Convert.

转变。

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

Joda-Time

乔达时间

UPDATE: The Joda-Time project is now in maintenance mode. The team advises migration to the java.timeclasses.

更新:Joda-Time 项目现在处于维护模式。该团队建议迁移到java.time类。

Here is some example code in Joda-Time 2.8.

这是 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.

Convert to old class. Note that the assigned time zone is lost in conversion, as j.u.Date cannot be assigned a time zone.

转换为旧类。请注意,分配的时区在转换中丢失,因为无法为 juDate 分配时区。

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

Time Zone

时区

If needed, you can assign a time zone.

如果需要,您可以分配时区。

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


Table of date-time types in Java, both modern and legacy.

Java 中的日期时间类型表,包括现代的和传统的。



About java.time

关于java.time

The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat

The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.

现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310

You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*classes.

您可以直接与您的数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*

Where to obtain the java.time classes?

从哪里获得 java.time 类?

Table of which java.time library to use with which version of Java or Android.

哪个 java.time 库与哪个 Java 或 Android 版本一起使用的表。

The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多

回答by Alexei Martianov

There are two answers above up-to-now and they are both long (and tl;drtoo short IMHO), so I write summary from my experience starting to use new java.timelibrary (applicable as noted in other answers to Java version 8+). ISO 8601sets standard way to write dates: YYYY-MM-DDso the format of date-time is only as below (could be 0, 3, 6 or 9 digits for milliseconds) and no formatting string necessary:

到目前为止,上面有两个答案,它们都很长(而且tl;dr太短恕我直言),所以我根据我开始使用新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();
    }
}

I did not need it, but as getting year is in code from the question, then:
it is trickier, cannot be done from Instantdirectly, can be done via Calendarin way of questions Get integer value of the current year in Javaand Converting java.time to Calendarbut IMHO as format is fixed substring is more simple to use:

我不需要它,但是由于从问题的代码中获取年份,那么:
它更棘手,不能直接完成Instant,可以通过Calendar问题的方式完成获取当前年份的整数值在 Java 中转换 java。日历的时间,但恕我直言,因为格式是固定的,子字符串使用起来更简单:

myDate.toString().substring(0,4);