How does Calendar class calculate months? - java

I got a really annoying problem with calendar class. I have two JTextFields to enter a period of date (txtStart & txtEnd). If start date begins at the first day of month (01.), I set the end date to "last day of month".
Now the user can change change the period by clicking a plus or minus button, then I want to increase or decrease only the month of start & end date.
Calendar tempStart = Calendar.getInstance();
Calendar tempEnd = Calendar.getInstance();
if (txtStart.getText().trim().startsWith("01.")) {
System.out.println("get dates typed by user, and set \"last day of month\" to txtEnd");
tempStart = convStringToDate(txtStart.getText().trim(), false);
System.out.println(tempStart.getTime() + " #+#+###++ ");
tempEnd = getLastDayOfMonth(txtStart.getText().trim());
System.out.println(tempEnd.getTime() + " #+#+###++ ");
System.out.println(" ");
System.out.println("multi is either +1 or -1, increasing or decreasing only the month !");
tempStart.set(Calendar.MONTH, tempStart.get(Calendar.MONTH) + multi);
System.out.println(tempStart.getTime() + " #+#+###++ ");
tempEnd.set(Calendar.MONTH, tempEnd.get(Calendar.MONTH) + multi);
System.out.println(tempEnd.getTime() + " #+#+###++ ");
System.out.println(" ");
}
My methods are working correctly. Now I got some bewildering output.
If I enter 01.11.2015 at txtStart (dd.MM.yyy) I got following output:
get dates typed by user, and set "last day of month" to txtEnd
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Mon Nov 30 23:59:59 GMT 2015 #+#+###++
multi is either +1 or -1, increasing or decreasing only the month !
Tue Dec 01 00:00:01 GMT 2015 #+#+###++
Wed Dec 30 23:59:59 GMT 2015 #+#+###++
Looks pretty nice and everthing seems to work correctly, but if I enter 01.10.2015 at txtStart (dd.MM.yyy) I got following output:
get dates typed by user, and set "last day of month" to txtEnd
Thu Oct 01 00:00:01 GMT 2015 #+#+###++
Sat Oct 31 23:59:59 GMT 2015 #+#+###++
multi is either +1 or -1, increasing or decreasing only the month !
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Tue Dec 01 23:59:59 GMT 2015 #+#+###++
May anyone have an idea why my end date is wrong at output 2.
EDIT:
multi = +1 or -1 (see in output1 or output2 comment)
private Calendar getLastDayOfMonth(String sDate) {
Calendar cal = convStringToDate(sDate, true);
// passing month-1 because 0-->jan, 1-->feb... 11-->dec
// calendar.set(year, month - 1, 1);
cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE));
cal.set(Calendar.HOUR_OF_DAY, MAX_ZEIT[0]); // 23
cal.set(Calendar.MINUTE, MAX_ZEIT[1]); // 59
cal.set(Calendar.SECOND, MAX_ZEIT[2]); // 59
cal.set(Calendar.MILLISECOND, MAX_ZEIT[3]); // 0
// Time: 23:59:59:0
return cal;
}
############## SOLUTION: ####################.
if (txtStart.getText().trim().startsWith("01.")) {
tempStart = convStringToDate(txtStart.getText().trim(), false);
tempEnd = (Calendar) tempStart.clone(); // set the date somewhere at the same month ( e.g. at start date )
tempStart.set(Calendar.MONTH, tempStart.get(Calendar.MONTH) + multi); // inc- or decrease the month first
tempEnd.set(Calendar.MONTH, tempEnd.get(Calendar.MONTH) + multi); // inc- or decrease the month first ( now there is no overflow due to the 30th or 31th day )
tempEnd = getLastDayOfMonth(df2.format(tempEnd.getTime())); // finally setting the "last day of month"
}
The solution is to do first of all to increase or decrease the month, after that I can set the last day of month without getting any overflow problems.
Output:
get dates typed by user, and set "last day of month" to txtEnd
Thu Oct 01 00:00:01 GMT 2015 #+#+###++
Thu Oct 01 00:00:01 GMT 2015 #+#+###++
multi is either +1 or -1, increasing or decreasing only the month !
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
FINALLY
Sun Nov 01 00:00:01 GMT 2015 #+#+###++
Mon Nov 30 23:59:59 GMT 2015 #+#+###++
I thank you all for your help !!!

The end date is incorrect in the first example, as well. It shows 30/12 whereas the last day of December is the 31st. When you add +1 to the month you don't check whether the following month has the same number of days.
November has 30 days. Therefore, incrementing October 31st gives November "31st" which is actually December 1st.
Lots of programmers need to do arithmetic on dates. That's why java.util.Calendar class has a method add() that you can use that encapsulates all the calculations you need. Check the JavaDocs: http://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html#add(int,%20int)

After you have incremented or decremented the month of the start date, use
int lastDayOfMonth = startDate.getActualMaximum(Calendar.DAY_OF_MONTH);
and use startDate in combination with day-of-month set to this value as the endDate.
Calendar sd = new GregorianCalendar( 2015, 1, 1 );
int last = sd.getActualMaximum(Calendar.DAY_OF_MONTH);
Calendar ed = new GregorianCalendar( sd.get(Calendar.YEAR),
sd.get(Calendar.MONTH),
last );
System.out.println( sd.getTime() + " " + ed.getTime() );
sd.add( Calendar.MONTH, 1 );
last = sd.getActualMaximum(Calendar.DAY_OF_MONTH);
ed = new GregorianCalendar( sd.get(Calendar.YEAR),
sd.get(Calendar.MONTH),
last );
System.out.println( sd.getTime() + " " + ed.getTime() );

Related

Why Calendar.after always retun true?

Hi can any one explain why such abnormal behavior from Calendar .after method
Calendar cal = Calendar.getInstance();
Calendar cal1 = Calendar.getInstance();
cal.set(Calendar.HOUR, 14);
cal1.set(Calendar.HOUR, 13);
System.out.println(cal.after(cal1)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
cal.set(Calendar.DATE, Calendar.getInstance().get(Calendar.DATE));
System.out.println(cal.after(cal1)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
Output:
true Cal Tue Oct 01 02:55:16 IST 2019 cal1 Tue Oct 01 01:55:16 IST 2019
true Cal Wed Oct 30 02:55:16 IST 2019 cal1 Tue Oct 01 01:55:16 IST 2019
but I did not get why cal is after cal1 even if i have set cal1's date to current date,
so if cal is today, I assigned the time as 14 hr it mover to next date then i set the date as current date in cal while for cal1 i did not.
So why still cal.after(cal1) is showing true in second syso while my cal1 is clearly 1 day greater than cal?
any suggestion to resolve such problem? Use java.time
I agree with you that there are some surprises in your code. You may have intended this:
ZoneId zone = ZoneId.of("Asia/Kolkata");
ZonedDateTime zdt = ZonedDateTime.now(zone);
ZonedDateTime zdt1 = ZonedDateTime.now(zone);
zdt = zdt.withHour(14);
zdt1 = zdt1.withHour(13);
System.out.println(zdt.isAfter(zdt1) + " zdt " + zdt + " zdt1 " + zdt1);
zdt = zdt.withDayOfMonth(ZonedDateTime.now(zone).getDayOfMonth());
System.out.println(zdt.isAfter(zdt1) + " zdt " + zdt + " zdt1 " + zdt1);
Output when I ran the code just now:
true zdt 2019-09-30T14:44:13.630029+05:30[Asia/Kolkata] zdt1 2019-09-30T13:44:13.630362+05:30[Asia/Kolkata]
true zdt 2019-09-30T14:44:13.630029+05:30[Asia/Kolkata] zdt1 2019-09-30T13:44:13.630362+05:30[Asia/Kolkata]
I get true both times just as you got from your code, which shouldn’t be surprising. As others have said, 02:55h is after 01:55h, and Oct 30 is after Oct 01. Also, referring to my result, 14:44 is after 13:44. In both lines these two times are compared.
I am using java.time, the modern Java date and time API.
What went wrong in your code?
The surprises in your code stem from the Calendar class being poorly designed and often behaving differently from what we would immediately expect.
Why when you set the hour to 14, you get Tue Oct 01 02:55:16 IST 2019? The date has changed into the following month, and the hour of day is 2, not 14.
Why when you set the date to today’s date, September 30, you get October 30?
For 1., Calendar.HOUR refers to hour within AM or PM from 0 through 11. So setting it to 14 we should have expected an exception. A Calendar with standard settings doesn’t care. Since the time was already in PM, it extrapolates, so 14 PM becomes 2 AM on the next day. Since today is the last day of September, you get October 1.
For 2., Calendar.DATE doesn’t refer to the full date, but to the day of month. Since current day of month is 30 and we already had October1, we get October 30.
Long story short: Avoid the Calendar class. Use ZonedDateTime and/or other classes form java.time, the modern Java date and time API. They are so much nicer to work with and give far fewer surprises like the ones you experienced.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Java docs for - Calendar.after()
public boolean after(Object when) {
return when instanceof Calendar && compareTo((Calendar)when) > 0;
}
//By definition :- true if the time of this Calendar is after the time represented by when; false otherwise.
System.out.println(cal.after(cal1)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
//true Cal Tue Oct 01 02:17:46 IST 2019 cal1 Tue Oct 01 01:17:46 IST 2019
cal.set(Calendar.HOUR, 13);
cal1.set(Calendar.HOUR, 14);
System.out.println(cal.after(cal1)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
//false Cal Tue Oct 01 01:16:55 IST 2019 cal1 Tue Oct 01 02:16:55 IST 2019
cal.set(Calendar.DATE, Calendar.getInstance().get(Calendar.DATE));
System.out.println(cal.after(cal1)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
//true Cal Wed Oct 30 13:17:46 IST 2019 cal1 Tue Oct 01 14:17:46 IST 2019
System.out.println(cal.after(cal1)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
//true Cal Tue Oct 01 02:25:19 MMT 2019 cal1 Tue Oct 01 01:25:19 MMT 2019
cal.set(Calendar.DATE, Calendar.getInstance().get(Calendar.DATE));
System.out.println(cal1.after(cal)+" Cal "+cal.getTime()+" cal1 "+cal1.getTime());
//false Cal Wed Oct 30 02:25:19 MMT 2019 cal1 Tue Oct 01 01:25:19 MMT 2019
Using
cal.setTime((Calendar.getInstance()).getTime());
instead of
cal.set(Calendar.DATE, Calendar.getInstance().get(Calendar.DATE));
gives the desired result.

Java simpledateformat.format is giving incorrect date for last week of year [duplicate]

This question already has answers here:
Java Date year calculation is off by year for two days
(5 answers)
Closed 6 years ago.
This is actually a duplicate of Y returns 2012 while y returns 2011 in SimpleDateFormat I'll take the ding for duplicate on this one not the one listed below. I read the answer on that and I still think there is an error there. The first day of the week in my system is Sunday so by the description from the java.doc This should have been the 53rd week of 2017 not the 1st week of 2018, if I was going to get an error January 1st through the 6th should have returned 2017.
This is not a duplicate of Calendar return incorrect date, the calendar is working fine. see additional information below I was trying to simplify my code for display purposes at EOD as I was hopeing to get out of the door
When using SimpleDateFormat.Format to convert a Calendar date of 12/31/2017 (2017,11,31,0,0) (Sunday first day of the week, last week of the year) SimpleDateFormat returns 12/31/2018. I have tried this with java 6,7,8 and get the same results, I tried it on two different versions of eclipse I tried it on another developers computer all this the same result. This is pulled from my actual junit code.
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
public class SimpleDateTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Calendar t_calendar = Calendar.getInstance();
Calendar m_calc_calendar = Calendar.getInstance();
int rc=0;
int testDays=20;
Date rDate;
SimpleDateFormat ssaDateFormat= new SimpleDateFormat("YYMMdd");
SimpleDateFormat logDateFormat= new SimpleDateFormat("E MM/dd/YYYY");
ArrayList<Date> testDates = new ArrayList<Date>();
t_calendar.set(2017,11,20,0,0); // start date
//t_calendar.set(2018,11,20,0,0); // start date
t_calendar.setLenient(false);
// builds 2600 dates for test run reason for Date (24 bytes) vs Calendar (423 bytes)
for(int days=0; days<testDays;days++){
t_calendar.add(Calendar.DATE, 1);
testDates.add(t_calendar.getTime());
}
for(Date testDate: testDates){
String test = testDate.toString();
System.out.println("toString: " + test);
t_calendar.setTimeInMillis(testDate.getTime());
test = ssaDateFormat.format(t_calendar.getTime());
System.out.println(" Cal.year " + t_calendar.get(Calendar.YEAR) + " Cal.month " + t_calendar.get(Calendar.MONTH) + " Cal.day " + t_calendar.get(Calendar.DATE) + " Cal.week " + t_calendar.get(Calendar.WEEK_OF_YEAR) );
System.out.println("ssa conversion: " + test);
test = logDateFormat.format(t_calendar.getTime());
System.out.println("log conversion: " + test);
System.out.println(t_calendar.toString());
System.out.println("");
// call to method under test
//rDate=rsl.calculateSSATransDate(ssaDateFormat.format(testDate),new BigDecimal(1.0));
// result validation.
}
}
Test Results
toString: Sat Dec 29 00:00:53 PST 2018 <-- input date
Cal.year 2018 Cal.month 11 Cal.day 29 Cal.week 52
ssa conversion: 181229
log conversion: Sat 12/29/2018 <-- result is good
java.util.GregorianCalendar[time=1546070453716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=52,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=363,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
toString: Sun Dec 30 00:00:53 PST 2018 <-- Input date
Cal.year 2018 Cal.month 11 Cal.day 30 Cal.week 1
ssa conversion: 191230 <-- Result is bad
log conversion: Sun 12/30/2019
java.util.GregorianCalendar[time=1546156853716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH=6,DAY_OF_MONTH=30,DAY_OF_YEAR=364,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
toString: Mon Dec 31 00:00:53 PST 2018 <-- Input date
Cal.year 2018 Cal.month 11 Cal.day 31 Cal.week 1
ssa conversion: 191231
log conversion: Mon 12/31/2019 <-- Result is bad
java.util.GregorianCalendar[time=1546243253716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2018,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH=6,DAY_OF_MONTH=31,DAY_OF_YEAR=365,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=5,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
toString: Tue Jan 01 00:00:53 PST 2019 <-- input date
Cal.year 2019 Cal.month 0 Cal.day 1 Cal.week 1
ssa conversion: 190101
log conversion: Tue 01/01/2019 <-- Result is good
java.util.GregorianCalendar[time=1546329653716,areFieldsSet=true,areAllFieldsSet=true,lenient=false,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=0,WEEK_OF_YEAR=1,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=1,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=53,MILLISECOND=716,ZONE_OFFSET=-28800000,DST_OFFSET=0]
As I look forward to the dates I see the issue repeat in this pattern
- in 2018 12/30 (Sun) and 12/31(Mon) in 2018,
- in 2019 I have issues with 12/29(Sun), 12/30(Mon) and 12/31 (Tues)
- in 2020 12/27(Sun), 12/28(Mon), 12/29(Tues), 12/30 (Wed) 12/31(Thurs)
- in 2021 12/26(Sun) - 12/31(Fri)
- 2022 is correct
- 2023 restarts the loop.
It has to do with the last incomplete week of the year but I haven't been able to solve the issue.
Months are 0 based i.e. January is 0 December is 11
try
c.set(2017, 11, 31, 0, 0);

Setting Minutes in Calendar differs depending on TimeZone

I just found a strange behavior with the JAVA Calendar Class.
When I set the minutes to 0 the result differs depending on the used TimeZone.
Does anyone know why?
Example Code
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class CalendarTest {
public static void main(String[] args) {
Date date = new Timestamp(1477780200000L);
System.out.println(date); // 2016-10-30 00:30:00.0
testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("CET"))); // Sun Oct 30 02:00:00 CET 2016
testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("GMT+1"))); // Sun Oct 30 02:00:00 CEST 2016
}
private static void testMethod(Date date, Calendar c) {
c.setTime(date);
c.add(Calendar.HOUR_OF_DAY, 2);
c.set(Calendar.MINUTE, 0);
System.out.println(c.getTime());
}
}
edit (to make my question more understandable):
changed testMethod
this changed Method calculates in BOTH(!) cases Sun Oct 30 02:30:00 CEST 2016.
private static void testMethod(Date date, Calendar c) {
c.setTime(date);
c.add(Calendar.HOUR_OF_DAY, 2);
System.out.println(c.getTime());
}
You can see in the example code below that displayed date is rather different than real time (in milliseconds). The problem is that Sun Oct 30 02:00:00 CET 2016 exists both in CET and CEST time zone. When switching CEST => CET you are going back one hour (producing hour overlap). For Calendar object, this is very tricky because you are expressing time change in the selected time zone. When you want to reset minutes in this very specific, Calendar have to discover in which time zone you are expressing the change.
I'm not 100% how Calendar is handling this but in first example CET, you specifically suggesting that you want to use CET. Therefore, when overlapping takes place Calendar can select your suggestion (CET). When you are using GMT to express it then Calendar have to pick one of the time zones CET or CEST and is selecting CEST.
public static void main(String[] args) {
Date date = new Timestamp(1477780200000L);
System.out.println(date); // 2016-10-30 00:30:00.0
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
calendar.setTime(date);
print(1, calendar); // 1: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(2, calendar); // 2: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
print(3, calendar); // 3: Sun Oct 30 02:00:00 CET 2016 | 1477789200000
System.out.println("--");
calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
calendar.setTime(date);
print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
print(6, calendar); // 6: Sun Oct 30 02:00:00 CET 2016 | 1477789200000
System.out.println("--");
calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+1"));
calendar.setTime(date);
print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in GMT
print(6, calendar); // 6: Sun Oct 30 02:00:00 CEST 2016 | 1477785600000
}
private static void print(int prefix, Calendar calendar) {
System.out.println(prefix + ": " + calendar.getTime() + " | " + calendar.getTimeInMillis());
}
Update
What is more interesting is the Calendar method getTimeInMillis:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
As you can see time is updated when you are getting it! There is a flag isTimeSet which is set to false every time you use set method like calendar.set(Calendar.MINUTE, 0). It means that your time becomes invalid in terms of right timezone/epoch etc. This method just sets the given calendar field to the given value and that's it. Moreover, this method is not doing any additional checks if your setting is valid. add method, on the other hand, respects calendar rules and move your date gracefully.
To summarize. You are setting minutes to 0 which forces calendar to recalculate date. You were right, the set is the problematic one.

DateUtils.addDays() class issue in daylight saving time

Recently New Zealand observed daylight saving on 27 sept 15.
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
sd.setTimeZone(TimeZone.getTimeZone("Pacific/Auckland"));
Date dateValue = sd.parse("2015-09-30");
System.out.println(dateValue); // prints "Tue Sep 29 07:00:00 EDT 2015" My local system timzone in EDT
dateValue = DateUtils.addDays(dateValue, -6); // 6 days back 24 Sep of Pacific/Auckland
System.out.println(dateValue); // prints "Tue Sep 23 07:00:00 EDT 2015"
The second print statement should print Tue Sep 29 08:00:00 EDT 2015, as Daylight Saving not is in effect.
The issue is before 27 Sep 15 NZ = UTC+12
and after NZ = UTC +13
So on date of 23 Sep It should have time 08:00:00 not 07:00:00
The problem is within DateUtils.addDays from Apache Commons: it is using a Calendar with the default timezone to add and subtract days instead of using a user-supplied timezone. You can see this in the source code of the method add: it calls Calendar.getInstance() and not Calendar.getInstance(someTimezone)
If you construct yourself the Calendar and set the correct timezone, the problem disappears:
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
sd.setTimeZone(TimeZone.getTimeZone("Pacific/Auckland"));
Date dateValue = sd.parse("2015-09-30");
System.out.println(dateValue); // prints "Tue Sep 29 13:00:00 CEST 2015"
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Pacific/Auckland")); // set correct timezone to calendar
calendar.setTime(dateValue);
calendar.add(Calendar.DAY_OF_MONTH, -6);
dateValue = calendar.getTime();
System.out.println(dateValue); // prints "Wed Sep 23 14:00:00 CEST 2015"
also i have used joda api to resolved this timezone issue.
org.joda.time.DateTimeZone timeZone = org.joda.time.DateTimeZone.forID( "Pacific/Auckland" );
DateTime currentDate= new DateTime( new Date(), timeZone );
DateTime dateValue = now.plusDays( -6 ); // prints Tue Sep 29 08:00:00 EDT 2015

Months elapsed function not giving proper result

I have two dates: (YYYY-dd-mm)
1)1960-11-11
2)1986-04-02
I have the following function which intends to calculate the months elapsed since arbitary dates, the above two are my test conditions. This function calculates months without considering the current month. That is a month is only added once its a complete month.
The function is:
public static final int months(Date date1) {// tricky
Calendar calFirst = Calendar.getInstance();
calFirst.setTime(date1);
System.out.println(""+calFirst.getTime());
Calendar calNow = Calendar.getInstance();
calNow.setTime(getCurrentDate());
System.out.println(calNow.getTime());
int m2 = calNow.get(Calendar.YEAR) * 12 + calNow.get((Calendar.MONTH));
System.out.println(calNow.get(Calendar.YEAR) * 12 );
System.out.println(calNow.get(Calendar.MONTH));
//23832
int m1 = calFirst.get(Calendar.YEAR) * 12 + (calFirst.get(Calendar.MONTH));
System.out.println(calFirst.get(Calendar.YEAR) * 12 );
System.out.println(calFirst.get(Calendar.MONTH));
//24168
return Math.abs(m2 - (m1));
}
Result for 1986-04-02:
Wed Apr 02 00:00:00 IST 1986
Wed Jan 08 00:00:00 IST 2014
24168
0
23832
3
333
Which is correct
Result for 1960-11-11:
Fri Nov 11 00:00:00 IST 1960
Wed Jan 08 00:00:00 IST 2014
24168
0
23520
10
638
Which is wrong.
Any hints where I am going wrong?
Benchmark
I think that 333 and 638 are both good answers...
Let me explain :
April included to December 1986 = 9 months + 27 full years + 0 months in 2014 = 333.
Same thinking applied to the other :
November included to december 1960 = 2 months + 53 full years + 0 months in 2014 = 638.
Both numbers are right from the point of view : first month included, last month excluded
Why not use Joda Period instead as discussed here:
Period p = new Period(from, to, PeriodType.months().withDaysRemoved());
int months = p.getMonths() + 1;

Categories