开发者

Bug in Java Calendar / Date for 2nd October 2010?

开发者 https://www.devze.com 2023-01-31 14:34 出处:网络
I\'m not sure what I\'m doing wrong, but I\'ve got a piece of code which calculates the number of days between two dates, and which looks something like the following:

I'm not sure what I'm doing wrong, but I've got a piece of code which calculates the number of days between two dates, and which looks something like the following:

final Calendar first = new GregorianCalendar(2010, Calendar.OCTOBER, 1);
final Calendar last = new GregorianCalendar(2010, Calendar.NOVEMBER, 1);

final long difference = last.getTimeInMillis() - first.getTimeInMillis();
final long days = difference / (1000 * 60 * 60 * 24);

System.out.println("difference: " + difference);
System.out.println("days: " + days);

To summarise, the code block above calculates the number of days between 1st October 2010 and 1 November 2010. I'm expecting to see it return 31 days (seeing as there's 31 days in October)

difference: xxxx
days: 31

but instead it's showing 30 days in October!

difference: 2674800000
days: 30

I've managed to narrow it down to between the the dates 2 October 2010 and 3 October 2010, which seems to only have 82800000 milliseconds, instead of a full 86400000 milliseconds (exactly one hour 开发者_如何学Pythonmissing).

Does anyone have any ideas what I'm doing wrong? Or is the 2nd October a special date which has one minute less than a regular day?


(86400000 - 82800000)/1000 = 3600, which is one hour. You're seeing daylight savings time, combined with the rounding of integer math

You could get around it by doing the calculations with floating point numbers and rounding at the end, or you could check out a library like Joda time which offers much better date math than what's built in.


You may be better off comparing the year and day or year instead of the milliseconds that pass in a day.

 int lastYear= last.get(Calendar.YEAR);
 int firstYear= first.get(Calendar.YEAR);
 int lastDayofYear = last.get(Calendar.DAY_OF_YEAR);
 int firstDayofYear = first.get(Calendar.DAY_OF_YEAR);
 int nDaysElapse = lastDayofYear - firstDayofYear;
 int nYearsElapse = lastYear- firstYear;
 int days = (nYearsElapse*365)+nDaysElapse;


You should read this post to get a better understanding of how Calendar is interrelated with date/time stamps.

Having read that site, my initial questions were:

What do you mean by days? Do you mean '24-hour blocks' or do you mean calendar days? In the same vein, do you care if you are off slightly due to daylight savings etc?

If you mean Calendar days, your best bet is probably to:

final Calendar first = new GregorianCalendar(2010, 9, 1);
final Calendar last = new GregorianCalendar(2010, 10, 1);

Calendar difference = Calendar.getInstance();
difference.setTimeInMillis(last.getTimeInMillis() - first.getTimeInMillis());

int numDays = difference.get(Calendar.DAY_OF_YEAR) - difference.getMinimum(Calendar.DAY_OF_YEAR);

Of course, the above code will only work if the number of days < 365. You will need to create a rolling calculation e.g.

int yearDiff = last.get(Calendar.YEAR) - first.get(Calendar.YEAR);
Calendar tmp = new GregorianCalendar();
tmp.setTimeInMillis(first.getTimeInMillis());
for(int i = 0; i < yearDiff; i++) {
  numDays += tmp.getActualMaximum(Calendar.DAY_OF_YEAR);
  i++;
  tmp.add(Calendar.YEAR, 1);
}

This should allow you to get the number of days in a correct and consistent manner, without worrying about Daylight Savings, Leap Years etc.

That said, JodaTime probably has this functionality built in.


The answer by Brad Mace is correct.

Use a Library

This question is a good example of why you should use a good date-time library wither than roll your own. For Java that means using either Joda-Time or the new java.time package in Java 8.

Joda-Time

Example code in Joda-Time.

DateTimeZone timeZone = DateTimeZone.forID( "Australia/Melbourne" );
DateTime theFirst = new DateTime( 2014, DateTimeConstants.OCTOBER, 1, 0, 0, 0, timeZone ).withTimeAtStartOfDay();
DateTime nextMonth = theFirst.plusMonths( 1 ).withTimeAtStartOfDay();
int days = Days.daysBetween( theFirst, nextMonth ).getDays();

Or if you don't care about time-of-day, use the LocalDate class.

java.time

Java 8 and later comes with a new java.time framework to supplant the old java.util.Date/.Calendar classes. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.

Example code using Java 8. Note that the enum ChronoUnit returns a 64-bit long rather than int.

LocalDate firstOfOctober = LocalDate.of( 2010 , java.time.Month.OCTOBER , 1 );
LocalDate nextMonth = firstOfOctober.plusMonths( 1 );
long daysInMonth  = ChronoUnit.DAYS.between( firstOfOctober , nextMonth );


The code you put in your post is calculating the time between September 1 and October 1, not October 1 and November 1. The output is correct for the code you posted.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号