Why does the difference between 30 March and 1 March 2020 erroneously give 28 days instead of 29?

14,567

Solution 1

The problem is that because of Daylight Saving Time shift (on Sunday, March 8, 2020), there are 28 days and 23 hours between those dates. TimeUnit.DAYS.convert(...) truncates the result to 28 days.

To see the problem (I'm in US Eastern time zone):

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

System.out.println(diff);
System.out.println("Days: " + TimeUnit.DAYS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Hours: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Days: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS) / 24.0);

Output

2502000000
Days: 28
Hours: 695
Days: 28.958333333333332

To fix, use a time zone that doesn't have DST, e.g. UTC:

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

Output

2505600000
Days: 29
Hours: 696
Days: 29.0

Solution 2

The cause of this problem is already mentioned in Andreas's answer.

The question is what exactly you want to count. The fact that you state that the actual difference should be 29 instead of 28, and ask whether "location/zone time could be a problem", reveals what you actually want to count. Apparently, you want to get rid of any timezone difference.

I assume you only want to calculate the days, without time and timezone.

Java 8

Below, in the example of how the number of days between could be calculated correctly, I'm using a class that represents exactly that – a date without time and timezone – LocalDate.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MM-yyyy HH:mm:ss");
LocalDate start = LocalDate.parse("1-03-2020 00:00:00", formatter);
LocalDate end = LocalDate.parse("30-03-2020 00:00:00", formatter);

long daysBetween = ChronoUnit.DAYS.between(start, end);

Note that ChronoUnit, DateTimeFormatter and LocalDate require at least Java 8, which is not available to you, according to the tag. However, it perhaps is to future readers.

As mentioned by Ole V.V., there's also the ThreeTen Backport, which backports Java 8 Date and Time API functionality to Java 6 and 7.

Share:
14,567
Joe
Author by

Joe

Updated on June 07, 2022

Comments

  • Joe
    Joe almost 2 years
    TimeUnit.DAYS.convert(
       Math.abs(
          new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("30-03-2020 00:00:00").getTime() - 
          new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("1-03-2020 00:00:00").getTime()
       ),
       TimeUnit.MILLISECONDS)
    

    The result is 28, while it should be 29.

    Could the time zone/location be the problem?

  • Andy Turner
    Andy Turner over 4 years
    "To fix" don't do maths with times. Use a proper date/time library.
  • Andreas
    Andreas over 4 years
    @AndyTurner To fix, using built-in Java 7 APIs. Since it can be fixed, showing how is a valid answer. Forcing someone to include a full library (Joda-Time, ThreeTen, etc.) just to do this one calculation would be overkill. Sure, using a library would be recommended, but is not required.
  • Andy Turner
    Andy Turner over 4 years
    I respectfully disagree. If you want to do a calculation, do it right; pay the cost to do it right.
  • Ole V.V.
    Ole V.V. over 4 years
    My € 0.02: The “right” way to do in Java 7 without any external library would be to use a GregorianCalendar object and add 1 day at a time until the end date is reached. I’d pay a high price to avoid that. And adding the backport of a library that is already part of Java 8, 9, 10, 11, 12, 13, … is no high price. On the contrary, next time you need to do anything with date or time, it will already be a gain.
  • MC Emperor
    MC Emperor over 4 years
    @OleV.V. I know there is ThreeTen, some users may have mentioned it a few times. (I was about to link to a SEDE query returning all posts and comments of user 5772882 containing the text ThreeTen ;-), but unfortunately it is offline at the time of writing.) I will update the post.
  • Affe
    Affe over 4 years
    Even in UTC the last day of June is occasionally one second too short, without any real warning or predictability. Always use a date-time library.
  • Andreas
    Andreas over 4 years
    @Affe None of the Java time APIs handle second adjustments, so that is not an issue. They all define that there are exactly 86400 seconds per day. See e.g. How does the (Oracle) Java JVM know a leap second is occurring?.
  • MC Emperor
    MC Emperor over 4 years
    @OleVV It's not a bad thing at all. I think most people are simply ignorant about java.time, because at school they still use the old classes. But the Java 8 Date and Time API is very well designed – it would be a loss not to use it.
  • Eric Towers
    Eric Towers over 4 years
    @Affe : Although negative leap seconds are potentially allowed, none have ever been applied. To date, leap seconds have been inserted in the last second of June and the last second of December, making the last days of those months one second "too" long. I recall that there is provision for an additional pair of potential insertions in the last day of each of March and September, but I don't recall where I read this.
  • Andreas
    Andreas over 4 years
    @EricTowers The code in the question and in this answer is working with milliseconds-since-epoch. It is impossible for a millis value to represent leap seconds, positive or negative, by the very nature of the value, so the discussion about leap seconds is irrelevant to this answer.
  • Eric Towers
    Eric Towers over 4 years
    @Andreas : Take it up with Affe; I didn't introduce the topic. I merely corrected a sign and month error in his comment.
  • Affe
    Affe over 4 years
    I'm sure the answer correctly solves the specific problem that was asked for this combination of question, language, and runtime. I certainly did not downvote it. I am aware JSR-310 "solved" the problem I brought up for supported versions of java by literally creating a new java-specific definition of what "one second" even means. I will cite the fact they felt the need to do that as compelling evidence to avoid the rabbit hole of mixing "human" date time with "machine" date time :)
  • StackOverthrow
    StackOverthrow over 4 years
    Library bloat was a rookie concern even before there were tree shaking tools like ProGuard. Bloat is the price we pay for rapid development and ease of maintenance.
  • The_Sympathizer
    The_Sympathizer over 4 years
    @AndyTurner: Actually, I would argue there's more here: whether this calculation is "right" or not depends on what semantics you are after. If the semantics is that it should be the physical time interval between the two points which, mind you, are specified down to the second, then the correct answer really is 28 days and 23 hours when it is taken as being in that time zone, rounded however you prefer.
  • The_Sympathizer
    The_Sympathizer over 4 years
    But if the desired semantics is "how many squares on the calendar are the dates [not date/times] away from each other", then yes, it should be 29, and this calculating method is wrong for that purpose.
  • ilkkachu
    ilkkachu over 4 years
    @MichaelHampton, um, I don't think using 12 o'clock here helps? The time interval between the two noons will still be 28 days and 23 hours, which still truncates to 28. You could use noon on the other end, and midnight on the other, of course, but then I think you might as well round the difference instead of truncating it.
  • Toby Speight
    Toby Speight over 4 years
    @Eric, I'm sure that Affe is aware that Java pretends there are always 86400 seconds in the day: that's why it sometimes shows that 30th June or 31st December as one second too short (specifically, when the day has 86401 seconds, but Java ignores 23:59:60).
  • Peter - Reinstate Monica
    Peter - Reinstate Monica over 4 years
    Where is Jon Skeet when you need him? ;-)