Illegal Pattern Character 'T' When Parsing a Date String to Java.Util.Date

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


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

Parsing string to date: Illegal pattern character 'T'.

Given your input of 2014-09-17T12:00:44.0000000Z, it is not sufficient to escape the letter T only. You also have to handle the trailing Z. But be aware, this Z is NOT a literal, but has the meaning of UTC+00:00 timezone offset according to ISO-8601-standard. So escaping Z is NOT correct.

SimpleDateFormat handles this special char Z by pattern symbol X. So the final solution looks like:

 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSX");
Date d = sdf.parse("2014-09-17T12:00:44.0000000Z");
System.out.println(d); // output: Wed Sep 17 14:00:44 CEST 2014

Note that the different clock time is right for timezone CEST (toString() uses system timezone), and that the result is equivalent to UTC-time 12:00:44. Furthermore, I had to insert seven symbols S in order to correctly process your input which pretends to have precision down to 100ns (although Java pre 8 can only process milliseconds).

getting java.lang.IllegalArgumentException: Illegal pattern character 'o'? while parsing java.text.SimpleDateFormat

Try this instead:

DateFormat formatter = new SimpleDateFormat("EEE MMM dd hh:mm:ss zzz yyyy");

E is used for "Day in Week" as text, M is the month name.

SimpleDateFormat Illegal pattern character error with localized pattern

Unfortunately the documentation of how to handle localized patterns is horrible. So I studied the source code and made my own investigations. Result:

The constructor of SimpleDateFormat accepting a pattern string only refers to the unlocalized pattern characters whose definition is documented as given in the javadoc header of class SimpleDateFormat. These unlocalized pattern characters are also defined as constant in DateTimeFormatSymbols:

/**
* Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
* All locales use the same these unlocalized pattern characters.
*/
static final String patternChars = "GyMdkHmsSEDFwWahKzZYuXL";

Three steps are necessary in order to use localized patterns (like "tt.MM.uuuu" what you believe to be German - but is NOT German, it should rather be "TT.MM.JJJJ" - example for wrong JDK resources):

  1. Define the localized pattern characters via DateFormatSymbols.setLocalPatternChars(...).
  2. Use the customized date-format-symbols on your SimpleDateFormat-object.
  3. Apply the localized date-time-pattern via SimpleDateFormat.applyLocalizedPattern(...)

Then the localized pattern will be translated to the internal and official pattern character definition.

Example of usage (using the correct German pattern TT.MM.JJJJ):

SimpleDateFormat sdf = new SimpleDateFormat(); // uses default locale (here for Germany)
System.out.println(sdf.toPattern()); // dd.MM.yy HH:mm
System.out.println(sdf.toLocalizedPattern()); // tt.MM.uu HH:mm

DateFormatSymbols dfs = DateFormatSymbols.getInstance(Locale.GERMANY);
dfs.setLocalPatternChars("GJMTkHmsSEDFwWahKzZYuXL");
sdf.setDateFormatSymbols(dfs);
sdf.applyLocalizedPattern("TT.MM.JJJJ");

System.out.println(sdf.toPattern()); // dd.MM.yyyy
System.out.println(sdf.toLocalizedPattern()); // TT.MM.JJJJ
System.out.println(sdf.format(new Date())); // 20.06.2016

Side note: I have changed the appropriate pattern chars y and d to J and T in the string "GyMdkHmsSEDFwWahKzZYuXL" to make a localized definition.

Unfortunately the JDK resources are obviously not reliable so my personal view is that the whole feature can only be used in an awkward way and is not very useful in practice.

Groovy Date Parsing -- X is an illegal pattern character

The simplest answer I can think of, is just to use 'Z'. The issue here is that -04:00 isn't recognised by the parser. So why not just run a regex prior to trying to convert it, looking for the final : and removing it.

How to check time SImpleDateFormat?

You probably want String#matches here with an appropriate regex pattern:

String dt = "2020-11-23T21:17:03.039023Z";
String regex = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{6}Z";
if (dt.matches(regex)) {
Log.e("TAG", "true");
}
else {
Log.e("TAG", "false");
}

But note that the above does not actually do any validation on the input, but rather just detects the general pattern, and distinguishes from the version with no millisecond/microsecond precision.

Java SimpleDateFormat can not parse date in yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX format pattern

Use java.time:

You can parse this example String without any explicit pattern, keep the precision as desired and, if necessary, format those date and time values in a multitude of custom ways.

Here's a small example:

public static void main(String[] args) {
// example String (of ISO format)
String input = "2021-06-28T07:09:30.463931900Z";
// parse it (using a standard format implicitly)
OffsetDateTime odt = OffsetDateTime.parse(input);
// print the result
System.out.println(odt);

// if you want a different output, define a formatter
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(
// use a desired pattern
"EEE MMM dd HH:mm:ss O uuuu",
// and a desired locale (important for names)
Locale.ENGLISH);
// print that
System.out.println(odt.format(dtf));
}

This code example produces the following output:

2021-06-28T07:09:30.463931900Z
Mon Jun 28 07:09:30 GMT 2021


Related Topics



Leave a reply



Submit