Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional parts in SimpleDateFormat

I'm reading in date strings that could be with or without a time zone adjustment: yyyyMMddHHmmssz or yyyyMMddHHmmss. When a string is missing a zone, I'll treat it as GMT. I'm not seeing any way to create optional sections in a SimpleDateFormat, but maybe I'm missing something. Is there a way to do this with a SimpleDateFormat, or should I just write a new concrete DateFormat to handle this?

like image 314
traffichazard Avatar asked May 05 '11 11:05

traffichazard


People also ask

What can I use instead of SimpleDateFormat?

DateTimeFormatter is a replacement for the old SimpleDateFormat that is thread-safe and provides additional functionality.

What is the format of SimpleDateFormat?

Class SimpleDateFormat. SimpleDateFormat is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for formatting (date -> text), parsing (text -> date), and normalization. SimpleDateFormat allows you to start by choosing any user-defined patterns for date-time formatting.

Is SimpleDateFormat deprecated?

Class SimpleDateFormat. Deprecated. A class for parsing and formatting dates with a given pattern, compatible with the Java 6 API.

Why is SimpleDateFormat not thread-safe?

2.2.Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally. So SimpleDateFormat instances are not thread-safe, and we should use them carefully in concurrent environments.


2 Answers

JSR-310 has been delivered with Java 8 which provides enhanced support for parsing temporal values where components may now be optional. Not only can you make the zone optional, but you can also make the time component optional and return the correct temporal unit for the given string.

Consider the following test cases.

public class DateFormatTest {      private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(             "yyyy-MM-dd[[ ]['T']HH:mm[:ss][XXX]]");      private TemporalAccessor parse(String v) {         return formatter.parseBest(v,                                    ZonedDateTime::from,                                    LocalDateTime::from,                                    LocalDate::from);     }      @Test public void testDateTime1() {         assertEquals(LocalDateTime.of(2014, 9, 23, 14, 20, 59),                      parse("2014-09-23T14:20:59"));     }      @Test public void testDateTime2() {         assertEquals(LocalDateTime.of(2014, 9, 23, 14, 20),                      parse("2014-09-23 14:20"));     }      @Test public void testDateOnly() {         assertEquals(LocalDate.of(2014, 9, 23), parse("2014-09-23"));     }      @Test public void testZonedDateTime() {         assertEquals(ZonedDateTime.of(2014, 9, 23, 14, 20, 59, 0,                                       ZoneOffset.ofHoursMinutes(10, 30)),                      parse("2014-09-23T14:20:59+10:30"));     }  } 

Here the DateTimeFormatter pattern of "yyyy-MM-dd[[ ]['T']HH:mm[:ss][XXX]]" allows optionals within the square parentheses which can also be nested. Patterns can also be constructed from a DateTimeFormatterBuilder, which the above pattern is demonstrated here:

private final DateTimeFormatter formatter = new DateTimeFormatterBuilder()         .parseCaseInsensitive()         .append(DateTimeFormatter.ISO_LOCAL_DATE)         .optionalStart()         .optionalStart()         .appendLiteral(' ')         .optionalEnd()         .optionalStart()         .appendLiteral('T')         .optionalEnd()         .appendOptional(DateTimeFormatter.ISO_TIME)         .toFormatter(); 

This would translate to an expression which looks like the following:

yyyy-MM-dd[[' ']['T']HH:mm[':'ss[.SSS]]]. 

Optional values can be nested and are also auto closed at the end if still open. Note however that there is no way to provide an exclusive OR on optional parts, thus the above format would actually parse the following value quite fine:

2018-03-08 T11:12 

Note the really neat capability that we can reuse existing formatter's as parts of our current format.

like image 145
Brett Ryan Avatar answered Sep 22 '22 05:09

Brett Ryan


I know this is an old post but just for the record...

Apache DateUtils class can help you with that.

String[] acceptedFormats = {"dd/MM/yyyy","dd/MM/yyyy HH:mm","dd/MM/yyyy HH:mm:ss"}; Date date1 = DateUtils.parseDate("12/07/2012", acceptedFormats); Date date2 = DateUtils.parseDate("12/07/2012 23:59:59", acceptedFormats); 

Link to the Apache Commons Lang library in Maven repository, you might want to check what's the latest version:
http://mvnrepository.com/artifact/org.apache.commons/commons-lang3

Maven v3.4

<dependency>     <groupId>org.apache.commons</groupId>     <artifactId>commons-lang3</artifactId>     <version>3.4</version> </dependency> 

Gradle v3.4

'org.apache.commons:commons-lang3:3.4' 
like image 42
tbraun Avatar answered Sep 22 '22 05:09

tbraun