J Freebird
J Freebird

Reputation: 3910

Java SimpleDateFormat doesn't Give Constant Result

I need to format the Java Date object into a String like yyyyMMdd (round to day). For e.g, 20180129. I have the following implementation:

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
sdf.format(roundedDate);

The code works most the time, but sometimes it'll generate results like 2018129, which is not what I want. So I'll have both 20180129 and 2018129 in my database.

The app consumes messages from a MQ and ummarshalls the timestamp in the message into a Java Date object. And it formats the date into a the above String.

The issue is that I cannot reproduce the issue in debug mode. It always produces the expected results in the debugger. But after I ran it on a server (in Docker) for some time, I see such corrupted data.

I wonder why the SimpleDateFormat could have such undetermined behavior given a valid Date object? Any idea will be appreciated.

Upvotes: 0

Views: 843

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338604

tl;dr

Use the thread-safe java.time classes instead.

Specifically, use LocalDate and DateTimeFormatter.BASIC_ISO_DATE.

LocalDate.parse(
    "2018129" ,
    DateTimeFormatter.BASIC_ISO_DATE
)

2018-01-29

LocalDate.now()
    .format( DateTimeFormatter.BASIC_ISO_DATE )

20180129

Thread-safety

You do not provide enough information to diagnose your problem. I would guess either:

  • You are using those legacy date-time objects across threads, and they were not designed to be thread-safe. Instead use the java.time classes which are designed to be thread-safe by design via immutable objects pattern.
  • Something is going wrong during whatever you are doing in this mysterious “date rounding” which you mention but neglect to explain.

Wrong data type

timestamp in the message into a Java Date object.

You are putting a date-only value into a date-with-time-of-day type. Square peg, round hole.

Instead, use a date-only type for a date-only value: LocalDate.

ISO 8601

Your desired format YYYYMMDD happens to be defined in the ISO 8601 standard, as the “basic” variant where the use of delimiters is minimized.

Java provides a DateTimeFormatter object for this purpose: DateTimeFormatter.BASIC_ISO_DATE. So no need to define a formatting pattern.

String input = "2018129" ;
LocalDate ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE ) ;

To generate such a string, use the same formatter.

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
String output = today.format( DateTimeFormatter.BASIC_ISO_DATE ) ;

By the way, I recommend using the full-length versions of ISO 8601 formats rather than the compact “basic” variants. The few bytes saved are not worth giving up the readability and reduced ambiguity, in my experience. Plus, the java.time classes use the full-length ISO 8601 formats by default when parsing/generating String objects, so you can dispense with DateTimeFormatter objects entirely.


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.

Upvotes: 1

jihor
jihor

Reputation: 2599

SimpleDateFormat is not thread-safe, see this excellent article.

java.time.format.DateTimeFormatter is the modern thread-safe implementation of this functionality in the core Java.

Upvotes: 4

Related Questions