User3
User3

Reputation: 2535

Elapsed Months calculation not giving correct result for dates with current month and day but a different year

I am trying to calculate months elapsed since a particular date, the function works fine albeit it gives me a one months difference (Less than one month) if I put a date of today with a different year of the past.

Say for all dates the function works well, except:

If it is "2014-03-06" (YYYY,MM,DD) today and I process a date as "2006-03-06" to the function, the result is one month shorter, however if I use the same date tomorrow, the result is just fine. WOnder where I am going wrong. Below is the code:

public static int months(String d) {

        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
        Date d1 = null;
        try {
            d1 = f.parse(d);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //   Date d2 = (getCurrentDate());
        Date d2 = getCurrentDate(); 
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);
        int diff = 0;
        if (c2.after(c1)) {
            while (c2.after(c1)) {
                c1.add(Calendar.MONTH, 1);
                if (c2.after(c1)) {
                    diff++;
                }
            }
        } else if (c2.before(c1)) {
            while (c2.before(c1)) {
                c1.add(Calendar.MONTH, -1);
                if (c1.before(c2)) {
                    diff--;
                }
            }
        }
        System.out.println(diff);
        return diff;
    }

Result: 95 months.

Ideally it should be 96 months.

Please dont consider the static nature of functions, I have isolated this code in a different project for test purpose.

This is the value of c1 and c2:

java.util.GregorianCalendar[time=1141583400000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Kolkata",offset=19800000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2006,MONTH=2,WEEK_OF_YEAR=10,WEEK_OF_MONTH=2,DAY_OF_MONTH=6,DAY_OF_YEAR=65,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=19800000,DST_OFFSET=0] and c2 is: java.util.GregorianCalendar[time=1394044200000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Kolkata",offset=19800000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2014,MONTH=2,WEEK_OF_YEAR=10,WEEK_OF_MONTH=2,DAY_OF_MONTH=6,DAY_OF_YEAR=65,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=19800000,DST_OFFSET=0]
95

My bad here is the getCurrentDate():

public static Date getCurrentDate() {
        String DateStr = new SimpleDateFormat("yyyy-MM-dd"/*,
                current*/).format(Calendar.getInstance().getTime());
        //  String DateStr = formattedDate;
        Date date = null;
        try {
            date = new SimpleDateFormat("yyyy-MM-dd"/*, current*/).parse(DateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        java.sql.Date dx = new java.sql.Date(date.getTime());
        return dx;
    }

Upvotes: 0

Views: 154

Answers (5)

Basil Bourque
Basil Bourque

Reputation: 339382

As the correct answer by Deinlandel points out, this date-time work is much easier with the Joda-Time library.

Here's my take on some example code.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime dateTimeStart = new DateTime( "2006-03-06", timeZone );
DateTime dateTimeStop = new DateTime( "2014-03-06", timeZone );
int months = Months.monthsBetween( dateTimeStart, dateTimeStop ).getMonths();

Dump to console…

System.out.println( "dateTimeStart: " + dateTimeStart );
System.out.println( "dateTimeStop: " + dateTimeStop );
System.out.println( "months: " + months );

When run…

dateTimeStart: 2006-03-06T00:00:00.000+01:00
dateTimeStop: 2014-03-06T00:00:00.000+01:00
months: 96

Upvotes: 1

Abiel Paltao
Abiel Paltao

Reputation: 315

Change this:

Date d2 = getCurrentDate();

to this:

Calendar now = Calendar.getInstance();
Date d2 =  now.getTime();

It returns 96 to me.

Edited---

If you're using your function getCurrentDate(), i suggest you replace it with this:

public static Date getCurrentDate() {
    Calendar now = Calendar.getInstance();
    Date d2 =  now.getTime();
    return d2
 }

Upvotes: 1

Deinlandel
Deinlandel

Reputation: 1033

I know it's not always possible and desirable to add new dependencies to the project, but have you considered using JodaTime library for date/time calculations? It has much cleaner api, and it follows all calendar/timezone restricitions very strictly.

Here is example for your dates (date parsing from strings is omitted).

    DateTime from = new DateTime(2006, 3, 6, 0, 0); //2006-03-06
    DateTime to = new DateTime(2014, 3, 6, 0, 0);   //2014-03-06
    System.out.println(new Interval(from, to).toPeriod(PeriodType.months()));

It prints "P96M", that is, 96 months.

Note that this example uses default (system) timezone.

Upvotes: 2

MadProgrammer
MadProgrammer

Reputation: 347314

The comparisons of the Calendar after and before also include the time information, this could be throwing of the calculation towards the end, cause the comparison to fail short...

Try adjusting the values to the day boundaries...for example...

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

public class TestMonths {

    public static final String TEST1 = "2006-03-06";
    public static final String TEST2 = "2014-03-06";

    public static void main(String[] args) {
        System.out.println(months(TEST2));
    }

    public static Date getCurrentDate() throws ParseException {

        return new SimpleDateFormat("yyyy-MM-dd").parse(TEST1);

    }

    public static int months(String d) {

        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
        Date d1 = null;
        Date d2 = null;
        try {
            d1 = f.parse(d);
            d2 = (getCurrentDate());
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);
        int diff = 0;
        if (c2.after(c1)) {
            System.out.println("After");
            setStartOfDay(c1);
            setEndOfDay(c2);
            while (c2.after(c1)) {
                c1.add(Calendar.MONTH, 1);
                if (c2.after(c1)) {
                    diff++;
                }
            }
        } else if (c2.before(c1)) {
            System.out.println("Before");
            setStartOfDay(c2);
            setEndOfDay(c1);
            while (c2.before(c1)) {
                c1.add(Calendar.MONTH, -1);
                if (c2.before(c1)) {
                    diff--;
                }
            }
        }
        System.out.println(diff);
        return diff;
    }

    public static void setStartOfDay(Calendar cal) {
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
    }

    public static void setEndOfDay(Calendar cal) {
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 99);
    }
}

ps- I also corrected the "before" branch (where c2 is "before" c1) as there was a logic error...

Upvotes: 2

Rahul
Rahul

Reputation: 3509

Check your getCurrentDate() method. I have replaced with new Date() and output is 96 as you said.

Upvotes: 1

Related Questions