228

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

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'

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:

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

It also does not work.

hguser
  • 35,079
  • 54
  • 159
  • 293
  • 1
    do you know what does this "T" means ? As far as I think the whole date along with the 'T' should be converted to the date (not skipping it through 'Single Quotes', but the thing we're missing is the parse pattern. – coding_idiot Oct 12 '12 at 19:00
  • 11
    The `T` stands for *time* and is part of the [ISO8601](http://en.wikipedia.org/wiki/ISO_8601) standard for international date time formatting. –  Apr 30 '15 at 03:45
  • 1
    That's odd. Wrapping the `T` in single quotes worked for me (at least in terms of solving the parse error). – Agi Hammerthief Jan 21 '19 at 13:50
  • 1
    For new readers to the question I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use either `OffsetDateTime` or `Instant`. Both are from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Nov 13 '21 at 03:37

4 Answers4

237

Update for Java 8 and higher

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 Instant instead of the broken java.util.Date with the most recent versions of Java.

You should be using DateTimeFormatter instead of SimpleDateFormatter as well.

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.

This works with the input with the trailing Z as demonstrated:

In the pattern the T is escaped with ' on either side.

The pattern for the Z at the end is actually XXX as documented in the JavaDoc for SimpleDateFormat, it is just not very clear on actually how to use it since Z is the marker for the old TimeZone information as well.

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
31

tl;dr

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

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

ISO 8601

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

Both:

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

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.time

Built into Java 8 and later is the new java.time framework. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.

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.time classes.

Here is some example code in 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.

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.


About java.time

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

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

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

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

Where to obtain the java.time classes?

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

The ThreeTen-Extra project 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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
3

You can parse string to Timestamp with the following method:

public static void main(String[] args) {

    String timeStr = "2021-11-11T10:25:35+00:00";
    System.out.println(convertStringToTimestamp(timeStr));

}

static Timestamp convertStringToTimestamp(String dateAsString) {
    OffsetDateTime offsetDateTime = OffsetDateTime.parse(dateAsString);
    final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
    return Optional.ofNullable(offsetDateTime.toString())
            .map(value -> LocalDateTime.parse(value, dateTimeFormatter))
            .map(Timestamp::valueOf)
            .orElse(null);
}
  • Thanks. (1) Avoid `Timestamp` if you can. If for JDBC [use `OffsetDateTime` or `LocalDateTime`](https://stackoverflow.com/a/67505173/5772882). (2) Whether your conversion is correct depends. It also seems a bit more complicated than necessary. – Ole V.V. Nov 13 '21 at 03:44
  • If you do need an old-fashioned `Timestamp` for a legacy API not yet upgraded to java.time: `return Timestamp.from(OffsetDateTime.parse(dateAsString).toInstant());`. A bit simpler, isn’t it? – Ole V.V. Nov 13 '21 at 03:48
  • ['Z' is not the same as Z](https://stackoverflow.com/a/67953075/10819573) – Arvind Kumar Avinash Nov 21 '22 at 18:25
1

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

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 Instant directly, can be done via Calendar in way of questions Get integer value of the current year in Java and Converting java.time to Calendar but IMHO as format is fixed substring is more simple to use:

myDate.toString().substring(0,4);
Alex Martian
  • 3,423
  • 7
  • 36
  • 71