Java SimpleDateFormat for time zone with a colon separator?

JavaDateIso8601

Java Problem Overview


I have a date in the following format: 2010-03-01T00:00:00-08:00

I have thrown the following SimpleDateFormats at it to parse it:

private static final SimpleDateFormat[] FORMATS = {
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"), //ISO8601 long RFC822 zone
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"), //ISO8601 long long form zone
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), //ignore timezone
        new SimpleDateFormat("yyyyMMddHHmmssZ"), //ISO8601 short
        new SimpleDateFormat("yyyyMMddHHmm"),
        new SimpleDateFormat("yyyyMMdd"), //birthdate from NIST IHE C32 sample
        new SimpleDateFormat("yyyyMM"),
        new SimpleDateFormat("yyyy") //just the year
    };

I have a convenience method that uses those formats like so:

public static Date figureOutTheDamnDate(String wtf) {
    if (wtf == null) {
        return null;
    }
    Date retval = null;
    for (SimpleDateFormat sdf : FORMATS) {
        try {
            sdf.setLenient(false)
            retval = sdf.parse(wtf);
            System.out.println("Date:" + wtf + " hit on pattern:" + sdf.toPattern());
            break;
        } catch (ParseException ex) {
            retval = null;
            continue;
        }
    }

    return retval;
}

It seems to hit on the pattern yyyyMMddHHmm but returns the date as Thu Dec 03 00:01:00 PST 2009.

What is the correct pattern to parse this date?

UPDATE: I don't NEED the time zone parsing. I don't anticipate having time sensitive issues moving between zones, but how would I get the "-08:00" zone format to parse????

Unit test:

@Test
public void test_date_parser() {
    System.out.println("\ntest_date_parser");
    //month is zero based, are you effing kidding me
    Calendar d = new GregorianCalendar(2000, 3, 6, 13, 00, 00);
    assertEquals(d.getTime(), MyClass.figureOutTheDamnDate("200004061300"));
    assertEquals(new GregorianCalendar(1950, 0, 1).getTime(), MyClass.figureOutTheDamnDate("1950"));
    assertEquals(new GregorianCalendar(1997, 0, 1).getTime(),  MyClass.figureOutTheDamnDate("199701"));
    assertEquals(new GregorianCalendar(2010, 1, 25, 15, 19, 44).getTime(),   MyClass.figureOutTheDamnDate("20100225151944-0800"));

    //my machine happens to be in GMT-0800
    assertEquals(new GregorianCalendar(2010, 1, 15, 13, 15, 00).getTime(),MyClass.figureOutTheDamnDate("2010-02-15T13:15:00-05:00"));
    assertEquals(new GregorianCalendar(2010, 1, 15, 18, 15, 00).getTime(), MyClass.figureOutTheDamnDate("2010-02-15T18:15:00-05:00"));

    assertEquals(new GregorianCalendar(2010, 2, 1).getTime(), MyClass.figureOutTheDamnDate("2010-03-01T00:00:00-08:00"));
    assertEquals(new GregorianCalendar(2010, 2, 1, 17, 0, 0).getTime(), MyClass.figureOutTheDamnDate("2010-03-01T17:00:00-05:00"));
}

Output from unit test:

test_date_parser
Date:200004061300 hit on pattern:yyyyMMddHHmm
Date:1950 hit on pattern:yyyy
Date:199701 hit on pattern:yyyyMM
Date:20100225151944-0800 hit on pattern:yyyyMMddHHmmssZ
Date:2010-02-15T13:15:00-05:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss
Date:2010-02-15T18:15:00-05:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss
Date:2010-03-01T00:00:00-08:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss
Date:2010-03-01T17:00:00-05:00 hit on pattern:yyyy-MM-dd'T'HH:mm:ss

Java Solutions


Solution 1 - Java

JodaTime's DateTimeFormat to rescue:

String dateString = "2010-03-01T00:00:00-08:00";
String pattern = "yyyy-MM-dd'T'HH:mm:ssZ";
DateTimeFormatter dtf = DateTimeFormat.forPattern(pattern);
DateTime dateTime = dtf.parseDateTime(dateString);
System.out.println(dateTime); // 2010-03-01T04:00:00.000-04:00

(time and timezone difference in toString() is just because I'm at GMT-4 and didn't set locale explicitly)

If you want to end up with java.util.Date just use DateTime#toDate():

Date date = dateTime.toDate();

Wait for JDK7 (JSR-310) JSR-310, the referrence implementation is called ThreeTen (hopefully it will make it into Java 8) if you want a better formatter in the standard Java SE API. The current SimpleDateFormat indeed doesn't eat the colon in the timezone notation.

Update: as per the update, you apparently don't need the timezone. This should work with SimpleDateFormat. Just omit it (the Z) in the pattern.

String dateString = "2010-03-01T00:00:00-08:00";
String pattern = "yyyy-MM-dd'T'HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Date date = sdf.parse(dateString);
System.out.println(date); // Mon Mar 01 00:00:00 BOT 2010

(which is correct as per my timezone)

Solution 2 - Java

if you used the java 7, you could have used the following Date Time Pattern. Seems like this pattern is not supported in the Earlier version of java.

String dateTimeString  = "2010-03-01T00:00:00-08:00";
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date = df.parse(dateTimeString);

For More information refer to the SimpleDateFormat documentation.

Solution 3 - Java

Here's a snippet I used - with plain SimpleDateFormat. Hope somebody else may benefit from it:

public static void main(String[] args) {
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") {
	    public StringBuffer format(Date date, StringBuffer toAppendTo, java.text.FieldPosition pos) {
	    	StringBuffer toFix = super.format(date, toAppendTo, pos);
	        return toFix.insert(toFix.length()-2, ':');
	    };
	};
	// Usage:
	System.out.println(dateFormat.format(new Date()));
}

Output:

- Usual Output.........: 2013-06-14T10:54:07-0200
- This snippet's Output: 2013-06-14T10:54:07-02:00

Or... better, use a simpler, different, pattern:

SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
// Usage:
System.out.println(dateFormat2.format(new Date()));

Output:

- This pattern's output: 2013-06-14T10:54:07-02:00

See the docs for that.

Solution 4 - Java

Try this, its work for me:

Date date = javax.xml.bind.DatatypeConverter.parseDateTime("2013-06-01T12:45:01+04:00").getTime();

In Java 8:

OffsetDateTime dt = OffsetDateTime.parse("2010-03-01T00:00:00-08:00");

Solution 5 - Java

If you can use JDK 1.7 or higher, try this:

public class DateUtil {
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
    
    public static String format(Date date) {
        return dateFormat.format(date);
    }
    
    public static Date parse(String dateString) throws AquariusException {
        try {
            return dateFormat.parse(dateString);
        } catch (ParseException e) {
            throw new AquariusException(e);
        }
    }
}

document: https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html which supports a new Time Zone format "XXX" (e.g. -3:00)

While JDK 1.6 only support other formats for Time Zone, which are "z" (e.g. NZST), "zzzz" (e.g. New Zealand Standard Time), "Z" (e.g. +1200), etc.

Solution 6 - Java

tl;dr

OffsetDateTime.parse( "2010-03-01T00:00:00-08:00" )

Details

The answer by BalusC is correct, but now outdated as of Java 8.

java.time

The java.time framework is the successor to both Joda-Time library and the old troublesome date-time classes bundled with the earliest versions of Java (java.util.Date/.Calendar & java.text.SimpleDateFormat).

ISO 8601

Your input data string happens to comply with the ISO 8601 standard.

The java.time classes use ISO 8601 formats by default when parsing/generating textual representations of date-time values. So no need to define a formatting pattern.

OffsetDateTime

The OffsetDateTime class represents a moment on the time line adjusted to some particular offset-from-UTC. In your input, the offset is 8 hours behind UTC, commonly used on much of the west coast of North America.

OffsetDateTime odt = OffsetDateTime.parse( "2010-03-01T00:00:00-08:00" );

You seem to want the date-only, in which case use the LocalDate class. But keep in mind you are discarding data, (a) time-of-day, and (b) the time zone. Really, a date has no meaning without the context of a time zone. For any given moment the date varies around the world. For example, just after midnight in Paris is still “yesterday” in Montréal. So while I suggest sticking with date-time values, you can easily convert to a LocalDate if you insist.

LocalDate localDate = odt.toLocalDate();

Time Zone

If you know the intended time zone, apply it. A time zone is an offset plus the rules to use for handling anomalies such as Daylight Saving Time (DST). Applying a ZoneId gets us a ZonedDateTime object.

ZoneId zoneId = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = odt.atZoneSameInstant( zoneId );

Generating strings

To generate a string in ISO 8601 format, call toString.

String output = odt.toString();

If you need strings in other formats, search Stack Overflow for use of the java.util.format package.

Converting to java.util.Date

Best to avoid java.util.Date, but if you must, you can convert. Call the new methods added to the old classes such as java.util.Date.from where you pass an Instant. An Instant is a moment on the timeline in UTC. We can extract an Instant from our OffsetDateTime.

java.util.Date utilDate = java.util.Date( odt.toInstant() );

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?

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.

Solution 7 - Java

Thanks acdcjunior for your solution. Here's a little optimized version for formatting and parsing :

public static final SimpleDateFormat XML_SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.FRANCE)
{
	private static final long serialVersionUID = -8275126788734707527L;

	public StringBuffer format(Date date, StringBuffer toAppendTo, java.text.FieldPosition pos)
    {            
		final StringBuffer buf = super.format(date, toAppendTo, pos);
		buf.insert(buf.length() - 2, ':');
        return buf;
    };
    
    public Date parse(String source) throws java.text.ParseException {
		final int split = source.length() - 2;
		return super.parse(source.substring(0, split - 1) + source.substring(split)); // replace ":" du TimeZone
    };
};

Solution 8 - Java

You can use X in Java 7.

https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

static final SimpleDateFormat DATE_TIME_FORMAT = 
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

static final SimpleDateFormat JSON_DATE_TIME_FORMAT = 
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");

private String stringDate = "2016-12-01 22:05:30";
private String requiredDate = "2016-12-01T22:05:30+03:00";

@Test
public void parseDateToBinBankFormat() throws ParseException {
    Date date = DATE_TIME_FORMAT.parse(stringDate);
    String jsonDate = JSON_DATE_TIME_FORMAT.format(date);

    System.out.println(jsonDate);
    Assert.assertEquals(jsonDate, requiredDate);
}

Solution 9 - Java

Try setLenient(false).

Addendum: It looks like you're recognizing variously formatted Date strings. If you have to do entry, you might like looking at this example that extends InputVerifier.

Solution 10 - Java

Since an example of Apache FastDateFormat(click for the documentations of versions:2.6and3.5) is missing here, I am adding one for those who may need it. The key here is the pattern ZZ(2 capital Zs).

import java.text.ParseException
import java.util.Date;
import org.apache.commons.lang3.time.FastDateFormat;
public class DateFormatTest throws ParseException {
    public static void main(String[] args) {
        String stringDateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ";
        FastDateFormat fastDateFormat = FastDateFormat.getInstance(stringDateFormat);
        System.out.println("Date formatted into String:");
        System.out.println(fastDateFormat.format(new Date()));
        String stringFormattedDate = "2016-11-22T14:30:14+05:30";
        System.out.println("String parsed into Date:");
        System.out.println(fastDateFormat.parse(stringFormattedDate));
    }
}

Here is the output of the code:

Date formatted into String:
2016-11-22T14:52:17+05:30
String parsed into Date:
Tue Nov 22 14:30:14 IST 2016

Note: The above code is of Apache Commons' lang3. The class org.apache.commons.lang.time.FastDateFormat does not support parsing, and it supports only formatting. For example, the output of the following code:

import java.text.ParseException;
import java.util.Date;
import org.apache.commons.lang.time.FastDateFormat;
public class DateFormatTest {
    public static void main(String[] args) throws ParseException {
        String stringDateFormat = "yyyy-MM-dd'T'HH:mm:ssZZ";
        FastDateFormat fastDateFormat = FastDateFormat.getInstance(stringDateFormat);
        System.out.println("Date formatted into String:");
        System.out.println(fastDateFormat.format(new Date()));
        String stringFormattedDate = "2016-11-22T14:30:14+05:30";
        System.out.println("String parsed into Date:");
        System.out.println(fastDateFormat.parseObject(stringFormattedDate));
    }
}

will be this:

Date formatted into String:
2016-11-22T14:55:56+05:30
String parsed into Date:
Exception in thread "main" java.text.ParseException: Format.parseObject(String) failed
	at java.text.Format.parseObject(Format.java:228)
	at DateFormatTest.main(DateFormatTest.java:12)

Solution 11 - Java

If date string is like 2018-07-20T12:18:29.802Z Use this

SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFreiheitView Question on Stackoverflow
Solution 1 - JavaBalusCView Answer on Stackoverflow
Solution 2 - JavaYouEyeKView Answer on Stackoverflow
Solution 3 - JavaacdcjuniorView Answer on Stackoverflow
Solution 4 - JavaRustamView Answer on Stackoverflow
Solution 5 - JavaWillie ZView Answer on Stackoverflow
Solution 6 - JavaBasil BourqueView Answer on Stackoverflow
Solution 7 - JavaBludwarfView Answer on Stackoverflow
Solution 8 - JavabigspawnView Answer on Stackoverflow
Solution 9 - JavatrashgodView Answer on Stackoverflow
Solution 10 - JavaAbhishek OzaView Answer on Stackoverflow
Solution 11 - JavaHimalayanCoderView Answer on Stackoverflow