0

Following is the code snippet which is throwing an exception:

SimpleDateformat dateFormatter = new SimpleDateFormat("yyyyMMddHHmm");
Date date = dateFormatter.parse("201710010200");

The code above threw exception for all the dates after 2:00 A.M. It ran well till 01:30 A.M.

DayLight saving time was configured (I'm using Australia/Sydney timezone).

After that, I could see logs of 3:00 A.M. Time between 2:00 A.M. and 3:00 A.M. is not logged too.

Log:

01 Oct 03:02:01 ERROR : Unparseable date: "201710010200"

Caused by: java.text.ParseException: Unparseable date: "201710010200" at java.text.DateFormat.parse(DateFormat.java:357)

What could be the fix of the problem of the date string "201710010200" not getting parsed, with the right date format specified?

  • Well what time zone are you in? If it's one where 2am on October 1st was skipped, that makes sense - that local time didn't exist. – Jon Skeet Oct 05 '17 at 08:51
  • (If these were meant to be timestamps, they should be logged in UTC and then parsed in UTC...) – Jon Skeet Oct 05 '17 at 08:57
  • no there are dates.. its of Sydney(Australia) – Raghav Agarwal Oct 05 '17 at 08:57
  • 1
    I have no idea what you mean by "no there are dates" but as I predicted, the Sydney time zone did indeed go forward at 2am on October 1st: https://www.timeanddate.com/time/zone/australia/sydney. So basically, if you looked at a correct clock in Sydney, you would never see 2am or 2:30am for example. The clock would go 1:58, 1:59, 3:00. – Jon Skeet Oct 05 '17 at 09:00
  • they are date and time values according to format "yyyyMMddHHmm" – Raghav Agarwal Oct 05 '17 at 09:10
  • `201710010200` might be a valid date/time for **most** part of the world, but not in `Australia/Sydney` timezone, due to Daylight Saving Time. When clock reaches 2 AM, it jumps forward directly to 3 AM (actually, it jumps from 1:59 AM to 3 AM, so all the local times between 2 AM and 2:59 AM don't exist in this timezone, at that day). @JonSkeet answer explains that in detail, what else you don't understand? –  Oct 05 '17 at 12:52
  • Maybe [this code](https://ideone.com/E2t3mA) can help you to understand better. –  Oct 05 '17 at 12:58
  • Actually when i am trying to reproduce this issue with jdk1.7 version in India setting time zone to Australia through Java. Its not getting reproduced, its giving 3:30A.M. for 2:30 A.M successfully. – Raghav Agarwal Oct 05 '17 at 13:03
  • It's very unclear what you mean by your comment, but my guess is that you were looking at a fall-back transition instead of a spring-forward transition - they're very different situations. (In a fall-back transition, any given local time can be represented by two different instants, so `parse` returns one of those - it will still format back to the same string. In a spring-forward transition, there *is* no instant that will format back to the original string.) – Jon Skeet Oct 05 '17 at 13:07

2 Answers2

7

You're trying to parse a date/time that didn't occur.

We now know that this was in the Sydney time zone. At 2am on October 1st 2017 in Sydney, the clocks went forward to 3am. If you were looking at a clock every minute you'd see:

  • 01:58
  • 01:59
  • 03:00
  • 03:01

So any date/time between 2am (inclusive) and 3am (exclusive) simply didn't occur in that time zone. We don't know what produced the values you're trying to parse, but:

  • If they're timestamps, it would almost certainly be better to both format and parse in UTC. Keep an offset from UTC and potentially a time zone ID if the time zone in which they were produced is important for future analysis.
  • If they're date/time values which aren't linked to any particular time zone, don't parse them as if they were in a time zone. Ideally, use Java 8's java.time package and parse them as LocalDateTime values
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • they are date and time values according to format "yyyyMMddHHmm" – Raghav Agarwal Oct 05 '17 at 09:10
  • 1
    @RaghavAgarwal: But it represents a date/time value that simply didn't occur in the time zone you're parsing it in. There is no `Date` value it could return that, when formatted back using the same `SimpleDateFormat`, would give the same string. – Jon Skeet Oct 05 '17 at 09:12
  • @RaghavAgarwal: To put it another way: what would you *expect* it to return? Bear in mind that [a `Date` value represents an instant in time](https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/) - which instant are you expecting it to return? – Jon Skeet Oct 05 '17 at 09:14
  • Date date = dateFormatter.parse("201710010200"); – Raghav Agarwal Oct 05 '17 at 09:24
  • 1
    @RaghavAgarwal: That doesn't answer my question at all. That doesn't say what instant the value is meant to represent - it's just repeating the code in the question. – Jon Skeet Oct 05 '17 at 09:24
  • I want to get number of milliseconds represented by this Date object – Raghav Agarwal Oct 05 '17 at 09:31
  • 2
    @RaghavAgarwal: It's not clear to me that you've read *anything* I've written. What do you expect that number of milliseconds to be? That would indicate an instant in time. **There is no instant in time which had a local time of 2am on October 1st 2017 in Sydney.** – Jon Skeet Oct 05 '17 at 09:46
1

tl;dr

LocalDateTime.parse(                               // Parse a string lacking any indicator of time zone or offset-from-UTC into a `LocalDateTime` object.
    "201710010200" ,                               // Parse string in “basic” ISO 8601 format.
    DateTimeFormatter.ofPattern( "uuuuMMddHHmm" )  // Define a formatting pattern to match the input. If you used expanded ISO 8601 format instead of “basic” you would not need to bother with this step.
).atZone(                                          // Place the inexact unzoned value (`LocalDateTime` object) into the context of a time zone. Produces a `ZonedDateTime` object.
    ZoneId.of( "Australia/Sydney" )
).toString()                                       // Generate a string in standard expanded ISO 8601 format. This class extends the standard to wisely append the name of the zone in square brackets.

2017-10-01T03:00+11:00[Australia/Sydney]

If that time-of-day on that date in than zone is invalid, ZonedDateTime class adjusts.

If you fear faulty input, trap for DateTimeParseException.

Details

The Answer by Jon Skeet is correct. Per his comment: There is no instant in time which had a local time of 2am on October 1st 2017 in Sydney.

java.time

The modern solution is the java.time classes that supplant the troublesome old legacy date-time classes.

Parsing

Define a formatting pattern to match your input.

String input = "201710010200" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMddHHmm" ) ;

LocalDateTime

Your input lacks an indicator of offset-from-UTC or time zone. So parse as a LocalDateTime.

LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

ldt.toString(): 2017-10-01T02:00

Without the context of a time zone or offset-from-UTC, this value has no real meaning. It does not represent a moment, a point on the timeline. It is only a vague idea about possible moments over a range of about 26-27 hours.

To determine a moment, we need to place this value within the context of a time zone or offset.

You claim to know that value was intended to represent a moment in Australia/Sydney time zone.

ZoneId z = ZoneId.of( "Australia/Sydney" ) ;

2017-10-01T03:00+11:00[Australia/Sydney]

The result is 3 AM. The offset-from-UTC used by the people of that region changed on that date at that time, a cut-over in Daylight Saving Time (DST) silliness. When the clock in that part of Australia was about to strike 2 AM, the clock jumped to 3 AM. The 2 AM hour never existed in that land. The ZonedDateTime class has a policy for automatically adjusting such invalid moment. Be sure to read the doc to see if you understand enter link description hereand agree with its algorithm.

Because this wall-clock time never existed, I suspect you are incorrect about that log data representing a moment in the zone of Australia/Sydney.

UTC

The bigger solution here is to learn to think, work, log, and exchange data all in UTC. Consider UTC to be the One True Time, and all other zones are but a mere variation of that theme. Forget about your own parochial time zone while at work programming/administrating.

In java.time, the basic class for UTC values is Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

This class can capture the current moment in UTC.

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

ISO 8601

The Instant class also can parse/generate strings in standard ISO 8601 format.

String output = Instant.now().toString() ;

"2018-01-23T12:34:56.734591Z"

The Z on the end is short for Zulu and means UTC. This is the crux of your original problem: You stored date-time values without such an indicator of zone/offset.

Parsing:

Instant instant = Instant.parse( "2018-01-23T12:34:56.734591Z" ) 

I strongly recommending using the ISO 8601 formats for all such storage/exchange of textual date-time values. Be sure to:

  • include offset/zone when storing/exchanging moments, precise points on the timeline.
  • Use expanded formats, rather than these “basic” formats that minimize the use of delimiters. These are hard to read by humans, less obvious as to the type of information, and are not used by default in java.time. The java.time classes use the expanded formats by default.

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.

Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor 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.

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