-3

I am trying to convert ISO 8601 date string to epoch time. How do I handle negative dates? Is the below code correct? Should I use something else instead of simple date format library? Negative dates are for BC.

String formatString = "yyyy-MM-dd'T'hh:mm:ssX";
SimpleDateFormat formatter = new SimpleDateFormat(formatString);
Date date = formatter.parse("-2017-01-04T12:30:00+05:00");
System.out.println(date.getTime()/1000);

Answer: -125818806600L
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Zack
  • 2,078
  • 10
  • 33
  • 58

1 Answers1

6

TL;DR: No, your code is not correct. Yes, I recommend using the modern Java date and time API instead of SimpleDateFormat.

Your first issue is defining correctness for years before the common era (BCE, “before Christ”).

As I read Wikipedia, ISO 8601 does not define clearly how to interpret a date in that range. The year itself poses no great problem: “year 0000 being equal to 1 BCE”, so -1 is 2 BCE and -2017 is 2018 BCE. You may use the proleptic Gregorian calendar, produced by extending the Gregorian calendar backward to dates preceding its official introduction in 1582, but beware that this disagrees with the Julian calendar traditionally used, so when your date-time string says January 4th this is not the same day as January 4th in the history books. Also the use of negative years and the proplectic Gregorian calendar is not a requirement by ISO 8601, it is only by agreement between the parties. Reservation: I don’t know whether there is any definition of January 4, 2018 BCE in the history books; we’re way back before the introduction of the Julian calendar too (proposed by Julius Caesar in 46 BCE).

The documentation of SimpleDateFormat does not state how it handles dates before the introduction of the Gregorian calendar. It seems to depend on a Calendar object associated with the date/time formatter. Such a Calendar object would be a GregorianCalendar on most computers and JVMs, but not always. So I take it that the output from your code is not guaranteed to be the same on all computers. And a GregorianCalendar can and usually does handle dates from before pope Gregor in the Julian calendar, so I will expect that the result you got does agree with the history books, but not with ISO 8601, when it comes to establishing which day was January 4, 2018 BCE. So on these grounds I suspect that your result is not correct.

As a test I compared the output from your code with the output from a similar use of the Java date and time API. Running your code I too got -125818806600. So I tried:

    System.out.println(OffsetDateTime.parse("-2017-01-04T12:30:00+05:00")
            .toInstant()
            .getEpochSecond());

These classes should be ISO 8601 compliant, so I would prefer this code over yours (it’s also a bit simpler). I got

-125817294600

It’s not the same, so another sign that your code does not give the correct result. The difference is 1512000 seconds, the same as 17 days 12 hours. Let me start by admitting I don’t understand. I would readily think that the difference between Julian and Gregorian calendar could account for a difference in the range of 17 or 18 days. But the 12 hours confuse me.

Edit: The 12 hours come from your use of lowercase hh in your format pattern string. Since you don’t have an AM/PM marker, you should use uppercase HH. Correcting this error, the output from your code is

-125818763400

Now the difference between your code and mine is 1468800 seconds or precisely 17 days.

hh is for hours within AM or PM in the range 1–12. Uppercase HH is for hour in day, 0–23. It’s a very common mistake with SimpleDateFormat (not with the modern classes, they catch it so you correct it). It goes unnoticed scaringly often because for most hours the result is the same; SimpleDateFormat is happy to use AM as default and parse for example 14:30 and understand it as 2:30 PM. But since the hours in your string happen to be 12, there is a difference: 12:30 AM means 0:30 in the day, where in ISO 12:30 means 12:30 PM. Hence the 12 hours error.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 3
    FYI: a) `SimpleDateFormat` handles dates before 1582-10-15 as Julian calendar dates (a very simplified historic model). b) The often seen mistake using h instead of H (where the hour of day is intended) can also happen with Java-8-`DateTimeFormatter` when printing. c) If the input is intended as ISO-8601-format then the interpretation should indeed be the proleptic gregorian calendar (as done in Java-8). – Meno Hochschild Jun 23 '17 at 12:04