Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Date year calculation is off by year for two days

This caused a Y2K-style bug in my software if you can imagine. Strange thing is the off-by-one year calculation only occurs for two days in the year, which I'm less sure how to troubleshoot.

The output:

03-Jan-2013
02-Jan-2013
01-Jan-2013
31-Dec-2013 ** strange
30-Dec-2013 ** strange
29-Dec-2012
28-Dec-2012
27-Dec-2012
26-Dec-2012
25-Dec-2012

I am not sure which part of the Java date utilities could cause such an error.

The code (since the test is so small I included a complete working program):

import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;

public class DateT {

        private static String getFormattedBackscanStartTime(int days) {

                SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-YYYY");
                Calendar workingDate = Calendar.getInstance();
                workingDate.add(Calendar.DATE, -1 * days);

                String formattedStartTime = dateFormat.format(workingDate.getTime());
                return formattedStartTime;
        }

        public static void main(String args[]) {

                for(int i = 35; i < 45; i++) {
                        System.out.println(getFormattedBackscanStartTime(i));
                }
        }
}
like image 716
djechlin Avatar asked Feb 07 '13 16:02

djechlin


People also ask

How do I calculate between two dates in Java?

Find the time difference between two dates in millisecondes by using the method getTime() in Java as d2. getTime() – d1. getTime(). Use date-time mathematical formula to find the difference between two dates.

Which string method is used to calculate date and time?

String dateStart = "11/03/14 09:29:58"; String dateStop = "11/03/14 09:33:43"; // Custom date format SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss"); Date d1 = null; Date d2 = null; try { d1 = format.


5 Answers

This is the problem:

"dd-MMM-YYYY"

YYYY is the week-year, not the calendar year. You want yyyy instead.

The last two days of calendar year 2012 were in the first week of week-year 2013. You should normally only use the week year in conjunction with the "week of year" specifier (w).

like image 113
Jon Skeet Avatar answered Oct 21 '22 08:10

Jon Skeet


I am assuming you are using java 1.7.

The code snippet above will not work with java 1.6 as SimpleDateFormat("dd-MMM-YYYY") will raise an java.lang.IllegalArgumentException (YYYY is not available in java 1.6)

You need to use yyyy instead of YYYY.

Y -> week-year
y -> year

here

EDIT

Works great with yyyy:

$ java DateT
03-Jan-2013
02-Jan-2013
01-Jan-2013
31-Dec-2012
30-Dec-2012
29-Dec-2012
28-Dec-2012
27-Dec-2012
26-Dec-2012
25-Dec-2012
like image 25
aayoubi Avatar answered Oct 21 '22 07:10

aayoubi


The problem lies in your date format string - year should be yyyy not YYYY.

If you print the value of workingDate.getTime() in each iteration of the loop, you'll see it has the expected values:

Thu Jan 03 11:19:33 EST 2013
Wed Jan 02 11:19:33 EST 2013
Tue Jan 01 11:19:33 EST 2013
Mon Dec 31 11:19:33 EST 2012
Sun Dec 30 11:19:33 EST 2012
Sat Dec 29 11:19:33 EST 2012
Fri Dec 28 11:19:33 EST 2012
Thu Dec 27 11:19:33 EST 2012
Wed Dec 26 11:19:33 EST 2012
Tue Dec 25 11:19:33 EST 2012

Therefore the problem lies in the SimpleDateFormat usage.

like image 40
matt b Avatar answered Oct 21 '22 08:10

matt b


For the sake of completeness, here’s the modern answer using LocalDate (as recommended by Basil Bourque in a comment).

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class DateT {

    private static DateTimeFormatter dateFormatter 
            = DateTimeFormatter.ofPattern("dd-MMM-uuuu", Locale.US);

    private static String getFormattedBackscanStartTime(int days) {
        return LocalDate.now(ZoneId.systemDefault()).minusDays(days).format(dateFormatter);
    }

    public static void main(String args[]) {
        for(int i = 155; i < 165; i++) {
            System.out.println(getFormattedBackscanStartTime(i));
        }
    }
}

Running this today I got

04-Jan-2017
03-Jan-2017
02-Jan-2017
01-Jan-2017
31-Dec-2016
30-Dec-2016
29-Dec-2016
28-Dec-2016
27-Dec-2016
26-Dec-2016

A few things to note:

  • Give an explicit locale to your formatter to control the langauge of your output. Even if you just pass Locale.getDefault() you are telling the reader that you have thought about locale and made a decision.
  • Similarly give an explicit time zone to LocalDate.now() to tell the reader you’ve made a decision (for example ZoneId.of("America/New_York") for a specific time zone; ZoneId.systemDefault() for the JVM’s current time zone setting).
  • I find the code simpler and more straightforward than the code using the oldfashioned Calendar class. This is typical for the newer classes.
  • I have used uuuu for year. yyyy (lowercase) works too, there will only be a difference for years before the common era (AKA BC).
like image 38
Ole V.V. Avatar answered Oct 21 '22 08:10

Ole V.V.


You need to use lower case y for the year. Try this:

   SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
like image 31
Vincent Ramdhanie Avatar answered Oct 21 '22 08:10

Vincent Ramdhanie