Ошибка в Java-календаре / дате 2 октября 2010? - PullRequest
8 голосов
/ 17 декабря 2010

Я не уверен, что делаю неправильно, но у меня есть фрагмент кода, который вычисляет количество дней между двумя датами и выглядит примерно так:

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);

Подводя итог, приведенный выше блок кода вычисляет количество дней с 1 октября 2010 года по 1 ноября 2010 года. Я ожидаю, что он вернется через 31 день (учитывая, что в октябре 31 день)

difference: xxxx
days: 31

но вместо этого он показывает 30 дней в октябре!

difference: 2674800000
days: 30

Мне удалось сузить интервал между датами 2 октября 2010 г. и 3 октября 2010 г., который, кажется, имеет всего 82800000 миллисекунд вместо полных 86400000 миллисекунд (пропущен ровно один час).

У кого-нибудь есть идеи, что я делаю не так? Или 2 октября - особая дата, которая на одну минуту меньше обычного дня?

Ответы [ 5 ]

10 голосов
/ 17 декабря 2010

(86400000 - 82800000)/1000 = 3600, что составляет один час. Вы видите переход на летнее время в сочетании с округлением целых чисел

Вы можете обойти это, выполнив вычисления с числами с плавающей запятой и округляя в конце, или вы можете проверить библиотеку типа Время Joda , которая предлагает гораздо лучшую математику даты, чем встроенная. 1006 *

0 голосов
/ 12 июня 2014

Ответ Брэда Мейса правильный.

Использовать библиотеку

Этот вопрос является хорошим примером того, почему вы должны использовать хорошую библиотеку даты и времени, а не свою собственную. Для Java это означает использование либо Joda-Time, либо нового пакета java.time в Java 8.

Joda-Time

Пример кода в 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();

Или, если вам нет дела до времени суток, используйте класс LocalDate.

java.time

Java 8 и более поздние версии поставляются с новым фреймворком java.time для замены старых классов java.util.Date/.Calendar. Вдохновленный Joda-Time, определенным JSR 310 и расширенным проектом ThreeTen-Extra.

Пример кода с использованием Java 8. Обратите внимание, что enum ChronoUnit возвращает 64-битный long вместо int.

LocalDate firstOfOctober = LocalDate.of( 2010 , java.time.Month.OCTOBER , 1 );
LocalDate nextMonth = firstOfOctober.plusMonths( 1 );
long daysInMonth  = ChronoUnit.DAYS.between( firstOfOctober , nextMonth );
0 голосов
/ 17 декабря 2010

Вы должны прочитать этот пост , чтобы лучше понять, как Календарь взаимосвязан с метками даты / времени.

Прочитав этот сайт, мои первые вопросы были:

Что вы подразумеваете под днями? Вы имеете в виду «24-часовые блоки» или календарные дни? В том же духе, вам небезразлично, если у вас мало времени из-за летнего времени и т. Д.?

Если вы имеете в виду календарные дни, вам лучше всего:

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);

Конечно, приведенный выше код будет работать только в том случае, если количество дней <365. Вам нужно будет создать скользящий расчет, например, </p>

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);
}

Это должно позволить вам правильно и последовательно определить количество дней, не беспокоясь о переходе на летнее время, високосных годах и т. Д.

Тем не менее, JodaTime, вероятно, имеет встроенную функциональность.

0 голосов
/ 17 декабря 2010

Возможно, вам лучше сравнивать год и день или год, а не миллисекунды, которые проходят за день.

 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;
0 голосов
/ 17 декабря 2010

Код, который вы указали в своем сообщении, вычисляет время между 1 сентября и 1 октября, а не 1 октября и 1 ноября. Вывод соответствует введенному вами коду.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...