Разница в днях между двумя датами в Java?
мне нужно найти количество дней между двумя датами: один из отчета, а другой-текущая дата. Мой фрагмент:
int age=calculateDifference(agingDate, today);
здесь calculateDifference
является частным методом,agingDate
и today
are Date
объекты, просто для уточнения. Я следил за двумя статьями с форума Java,резьба 1 / резьба 2.
он отлично работает в Автономной программе, хотя когда я включаю это в свою логику для чтения из сообщаю, что получаю необычную разницу в ценностях.
почему это происходит и как я могу это исправить?
EDIT:
Я получаю большее количество дней по сравнению с фактическим количеством дней.
public static int calculateDifference(Date a, Date b)
{
int tempDifference = 0;
int difference = 0;
Calendar earlier = Calendar.getInstance();
Calendar later = Calendar.getInstance();
if (a.compareTo(b) < 0)
{
earlier.setTime(a);
later.setTime(b);
}
else
{
earlier.setTime(b);
later.setTime(a);
}
while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR))
{
tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
difference += tempDifference;
earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
}
if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR))
{
tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
difference += tempDifference;
earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
}
return difference;
}
Примечание :
к сожалению, ни один из ответов помог мне решить проблему. Я достиг проблема С помощью Joda времени библиотека.
20 ответов
Я бы предложил вам использовать отличный Джода Времени библиотека вместо дефектной java.утиль.Свидание и друзья. Вы могли бы просто написать
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;
Date past = new Date(110, 5, 20); // June 20th, 2010
Date today = new Date(110, 6, 24); // July 24th
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays(); // => 34
Я, возможно, слишком поздно, чтобы присоединиться к игре, но какого черта? :)
Как вы думаете, это проблема с потоками? Как вы используете вывод этого метода, например? Или
можем ли мы изменить ваш код, чтобы сделать что-то простое, как:
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
calendar1.set(<your earlier date>);
calendar2.set(<your current date>);
long milliseconds1 = calendar1.getTimeInMillis();
long milliseconds2 = calendar2.getTimeInMillis();
long diff = milliseconds2 - milliseconds1;
long diffSeconds = diff / 1000;
long diffMinutes = diff / (60 * 1000);
long diffHours = diff / (60 * 60 * 1000);
long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.println("\nThe Date Different Example");
System.out.println("Time in milliseconds: " + diff
+ " milliseconds.");
System.out.println("Time in seconds: " + diffSeconds
+ " seconds.");
System.out.println("Time in minutes: " + diffMinutes
+ " minutes.");
System.out.println("Time in hours: " + diffHours
+ " hours.");
System.out.println("Time in days: " + diffDays
+ " days.");
}
diff / (24 * etc) не учитывает часовой пояс, поэтому, если в вашем часовом поясе по умолчанию есть DST, он может сбросить расчет.
этой ссылке есть миленький реализации.
вот источник приведенной выше ссылки в случае, если ссылка идет вниз:
/** Using Calendar - THE CORRECT WAY**/
public static long daysBetween(Calendar startDate, Calendar endDate) {
//assert: startDate must be before endDate
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
и
/** Using Calendar - THE CORRECT (& Faster) WAY**/
public static long daysBetween(final Calendar startDate, final Calendar endDate)
{
//assert: startDate must be before endDate
int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
long endInstant = endDate.getTimeInMillis();
int presumedDays =
(int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);
Calendar cursor = (Calendar) startDate.clone();
cursor.add(Calendar.DAY_OF_YEAR, presumedDays);
long instant = cursor.getTimeInMillis();
if (instant == endInstant)
return presumedDays;
final int step = instant < endInstant ? 1 : -1;
do {
cursor.add(Calendar.DAY_OF_MONTH, step);
presumedDays += step;
} while (cursor.getTimeInMillis() != endInstant);
return presumedDays;
}
java.время
в Java 8 и более поздних, используйте java.временные рамки (учебник).
Duration
на Duration
класс представляет промежуток времени как количество секунд плюс дробная секунда. Он может считать дни, часы, минуты и секунды.
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println(duration.toDays());
ChronoUnit
если все, что вам нужно, это количество дней, в качестве альтернативы вы можете использовать ChronoUnit
перечисление. Обратите внимание, что методы расчета возвращают long
, а не int
.
long days = ChronoUnit.DAYS.between( then, now );
Это зависит от того, что вы определите как разницу. Сравнить две даты в полночь можно.
long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;
import java.util.Calendar;
import java.util.Date;
public class Main {
public static long calculateDays(String startDate, String endDate)
{
Date sDate = new Date(startDate);
Date eDate = new Date(endDate);
Calendar cal3 = Calendar.getInstance();
cal3.setTime(sDate);
Calendar cal4 = Calendar.getInstance();
cal4.setTime(eDate);
return daysBetween(cal3, cal4);
}
public static void main(String[] args) {
System.out.println(calculateDays("2012/03/31", "2012/06/17"));
}
/** Using Calendar - THE CORRECT WAY**/
public static long daysBetween(Calendar startDate, Calendar endDate) {
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
}
решение, использующее разницу между миллисекундами времени, с правильным округлением для дат DST:
public static long daysDiff(Date from, Date to) {
return daysDiff(from.getTime(), to.getTime());
}
public static long daysDiff(long from, long to) {
return Math.round( (to - from) / 86400000D ); // 1000 * 60 * 60 * 24
}
одно примечание: конечно, даты должны быть в некотором часовом поясе.
важные код:
Math.round( (to - from) / 86400000D )
Если вы не хотите раунд, вы можете использовать даты UTC,
иллюстрация проблемы: (мой код вычисляет дельту в неделях, но та же проблема применяется к Дельте в днях)
вот очень разумно выглядящая реализация:
public static final long MILLIS_PER_WEEK = 7L * 24L * 60L * 60L * 1000L;
static public int getDeltaInWeeks(Date latterDate, Date earlierDate) {
long deltaInMillis = latterDate.getTime() - earlierDate.getTime();
int deltaInWeeks = (int)(deltaInMillis / MILLIS_PER_WEEK);
return deltaInWeeks;
}
но этот тест не получится:
public void testGetDeltaInWeeks() {
delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23);
assertEquals("weeks between Feb23 and Mar09", 2, delta);
}
причина:
Пн 09 марта 00:00: 00 EDT 2009 = 1,236,571,200,000
Пн 23 Февраля 00: 00: 00 EST 2009 = 1,235,365,200,000
MillisPerWeek = 604,800,000
таким образом,
(Mar09 - Feb23) / MillisPerWeek =
1,206,000,000 / 604,800,000 = 1.994...
но любой, кто смотрит на календарь, согласится, что ответ 2.
Я использую эту функцию:
DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days
функции:
import java.util.Date;
public long DATEDIFF(String date1, String date2) {
long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
long days = 0l;
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); // "dd/MM/yyyy HH:mm:ss");
Date dateIni = null;
Date dateFin = null;
try {
dateIni = (Date) format.parse(date1);
dateFin = (Date) format.parse(date2);
days = (dateFin.getTime() - dateIni.getTime())/MILLISECS_PER_DAY;
} catch (Exception e) { e.printStackTrace(); }
return days;
}
посмотреть getFragmentInDays методы в этом классе Apache commons-lang DateUtils
.
основываясь на ответе @Mad_Troll, я разработал этот метод.
Я запустил около 30 тестовых случаев против него, это единственный метод, который обрабатывает фрагменты суб-дневного времени правильно.
пример: если вы проходите сейчас и сейчас + 1 миллисекунда, это все тот же день.
Делать 1-1-13 23:59:59.098
to 1-1-13 23:59:59.099
возвращает 0 дней, правильно; выделение других методов, размещенных здесь, не будет делать это правильно.
стоит отметить, что он не заботится о том, каким образом вы их поместите, если ваша дата окончания перед вашей датой начала он будет отсчитываться назад.
/**
* This is not quick but if only doing a few days backwards/forwards then it is very accurate.
*
* @param startDate from
* @param endDate to
* @return day count between the two dates, this can be negative if startDate is after endDate
*/
public static long daysBetween(@NotNull final Calendar startDate, @NotNull final Calendar endDate) {
//Forwards or backwards?
final boolean forward = startDate.before(endDate);
// Which direction are we going
final int multiplier = forward ? 1 : -1;
// The date we are going to move.
final Calendar date = (Calendar) startDate.clone();
// Result
long daysBetween = 0;
// Start at millis (then bump up until we go back a day)
int fieldAccuracy = 4;
int field;
int dayBefore, dayAfter;
while (forward && date.before(endDate) || !forward && endDate.before(date)) {
// We start moving slowly if no change then we decrease accuracy.
switch (fieldAccuracy) {
case 4:
field = Calendar.MILLISECOND;
break;
case 3:
field = Calendar.SECOND;
break;
case 2:
field = Calendar.MINUTE;
break;
case 1:
field = Calendar.HOUR_OF_DAY;
break;
default:
case 0:
field = Calendar.DAY_OF_MONTH;
break;
}
// Get the day before we move the time, Change, then get the day after.
dayBefore = date.get(Calendar.DAY_OF_MONTH);
date.add(field, multiplier);
dayAfter = date.get(Calendar.DAY_OF_MONTH);
// This shifts lining up the dates, one field at a time.
if (dayBefore == dayAfter && date.get(field) == endDate.get(field))
fieldAccuracy--;
// If day has changed after moving at any accuracy level we bump the day counter.
if (dayBefore != dayAfter) {
daysBetween += multiplier;
}
}
return daysBetween;
}
вы можете удалить @NotNull
аннотации, они используются Intellij для анализа кода на лету
вы говорите, что он" отлично работает в Автономной программе", но вы получаете" необычные значения разницы", когда вы"включаете это в мою логику для чтения из отчета". Это говорит о том, что ваш отчет имеет некоторые значения, для которых он работает неправильно, и ваша автономная программа не имеет этих значений. Вместо отдельной программы, я предлагаю тест. Напишите тестовый случай так же, как и отдельную программу, подкласс из класса TestCase JUnit. Теперь вы можете запустить очень специфический например, зная, какое значение вы ожидаете (и не давайте его сегодня для тестового значения, потому что сегодня меняется со временем). Если вы введете значения, которые вы использовали в Автономной программе, ваши тесты, вероятно, пройдут. Это здорово - вы хотите, чтобы эти дела продолжали работать. Теперь добавьте значение из вашего отчета, которое работает неправильно. Ваш новый тест, вероятно, провалится. Выясните, почему он терпит неудачу, исправьте его и получите зеленый цвет (все тесты проходят). Запустите отчет. Посмотрите, что все еще сломано; напишите тест; пусть пройдет. Очень скоро вы обнаружите, что ваш отчет работает.
сто строк кода для этой основной функции???
простой способ:
protected static int calculateDayDifference(Date dateAfter, Date dateBefore){
return (int)(dateAfter.getTime()-dateBefore.getTime())/(1000 * 60 * 60 * 24);
// MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
}
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
final int SECONDS = 60;
final int MINUTES = 60;
final int HOURS = 24;
final int MILLIES = 1000;
long temp;
if (timestamp1 < timestamp2) {
temp = timestamp1;
timestamp1 = timestamp2;
timestamp2 = temp;
}
Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
endDate.setTimeInMillis(timestamp1);
startDate.setTimeInMillis(timestamp2);
if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
int day1 = endDate.get(Calendar.DAY_OF_MONTH);
int day2 = startDate.get(Calendar.DAY_OF_MONTH);
if (day1 == day2) {
return 0;
} else {
return 1;
}
}
int diffDays = 0;
startDate.add(Calendar.DAY_OF_MONTH, diffDays);
while (startDate.before(endDate)) {
startDate.add(Calendar.DAY_OF_MONTH, 1);
diffDays++;
}
return diffDays;
}
ThreeTen-Extra
на ответ Виталия Федоренко правильно, описывая, как выполнить этот расчет современным способом с java.время класса (Duration
& ChronoUnit
) встроенный в Java 8 и позже (и back-ported to Java 6 & 7 и для Android).
Days
если вы используете несколько дней регулярно в коде, вы можете заменить простые целые числа с использованием класса. The Days
класс можно найти в ThreeTen-Extra project, расширение java.время и испытательный полигон для возможных будущих дополнений к java.время. The Days
класс предоставляет типобезопасный способ представления количества дней в вашем приложении. Класс включает удобные константы для ZERO
и ONE
.
учитывая старый устаревший java.util.Date
объекты в вопросе, сначала преобразовать их в modern java.time.Instant
объекты. Старые классы date-time недавно добавили методы для облегчения преобразования в java.время такое java.util.Date::toInstant
.
Instant start = utilDateStart.toInstant(); // Inclusive.
Instant stop = utilDateStop.toInstant(); // Exclusive.
передать Instant
объекты Заводского метода для org.threeten.extra.Days
.
в текущей реализации (2016-06) это оболочка, вызывающая java.time.temporal.ChronoUnit.DAYS.between
, читать ChronoUnit
class doc для деталей. Чтобы быть ясным: все прописные DAYS
в перечисление ChronoUnit
в то время как начальный-cap Days
является классом от ThreeTen-Extra.
Days days = Days.between( start , stop );
вы можете пройти эти Days
объекты вокруг вашего собственного кода. Вы можете сериализовать строку в стандартном ISO 8601 по телефону toString
. Этот формат PnD
использует P
отметить начало и D
означает "дни", с количеством дней между ними. Как в Java.классы времени и ThreeTen-Extra используют эти стандартные форматы по умолчанию при создании и анализе строк, представляющих значения даты и времени.
String output = days.toString();
P3D все
Days days = Days.parse( "P3D" );
этот код вычисляет дни между 2 строками даты:
static final long MILLI_SECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
static final String DATE_FORMAT = "dd-MM-yyyy";
public long daysBetween(String fromDateStr, String toDateStr) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
Date fromDate;
Date toDate;
fromDate = format.parse(fromDateStr);
toDate = format.parse(toDateStr);
return (toDate.getTime() - fromDate.getTime()) / MILLI_SECONDS_IN_A_DAY;
}
если вы ищете решение, которое возвращает правильное число или дни между, например,11/30/2014 23:59
и 12/01/2014 00:01
вот решение, использующее время Joda.
private int getDayDifference(long past, long current) {
DateTime currentDate = new DateTime(current);
DateTime pastDate = new DateTime(past);
return currentDate.getDayOfYear() - pastDate.getDayOfYear();
}
эта реализация будет возвращать 1
как разница в днях. Большинство решений, размещенных здесь, рассчитывают разницу в миллисекундах между двумя датами. Это значит, что 0
будет возвращен, потому что разница между этими двумя датами составляет всего 2 минуты.
Я уже писал об этом. Это репост из вычисление разницы между двумя экземплярами даты Java.
public int getDiffernceInDays(long timeAfter, long timeBefore) {
Calendar calendarAfter = Calendar.getInstance();
calendarAfter.setTime(new Date(timeAfter));
Calendar calendarNewAfter = Calendar.getInstance();
calendarNewAfter.set(calendarAfter.get(Calendar.YEAR), calendarAfter.get(Calendar.MONTH), calendarAfter.get(Calendar.DAY_OF_MONTH));
Calendar calendarBefore = Calendar.getInstance();
calendarBefore.setTime(new Date(timeBefore));
Calendar calendarNewBefore = Calendar.getInstance();
calendarNewBefore.set(calendarBefore.get(Calendar.YEAR), calendarBefore.get(Calendar.MONTH), calendarBefore.get(Calendar.DAY_OF_MONTH));
return (int) ((calendarNewAfter.getTime().getTime() - calendarNewBefore.getTime().getTime()) / (24 * 60 * 60 * 1000));
}
вы должны использовать библиотеку времени Joda, потому что Java Util Date иногда возвращает неправильные значения.
Joda vs Java Util дата
например, дни между вчерашним днем (ДД-ММ-гггг, 12-07-2016) и первым днем 1957 года (ДД-ММ-гггг, 01-01-1957):
public class Main {
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date date = null;
try {
date = format.parse("12-07-2016");
} catch (ParseException e) {
e.printStackTrace();
}
//Try with Joda - prints 21742
System.out.println("This is correct: " + getDaysBetweenDatesWithJodaFromYear1957(date));
//Try with Java util - prints 21741
System.out.println("This is not correct: " + getDaysBetweenDatesWithJavaUtilFromYear1957(date));
}
private static int getDaysBetweenDatesWithJodaFromYear1957(Date date) {
DateTime jodaDateTime = new DateTime(date);
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd-MM-yyyy");
DateTime y1957 = formatter.parseDateTime("01-01-1957");
return Days.daysBetween(y1957 , jodaDateTime).getDays();
}
private static long getDaysBetweenDatesWithJavaUtilFromYear1957(Date date) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date y1957 = null;
try {
y1957 = format.parse("01-01-1957");
} catch (ParseException e) {
e.printStackTrace();
}
return TimeUnit.DAYS.convert(date.getTime() - y1957.getTime(), TimeUnit.MILLISECONDS);
}
поэтому я действительно советую вам использовать библиотеку времени Joda.
Я сделал это таким образом. это просто:)
Date d1 = jDateChooserFrom.getDate();
Date d2 = jDateChooserTo.getDate();
Calendar day1 = Calendar.getInstance();
day1.setTime(d1);
Calendar day2 = Calendar.getInstance();
day2.setTime(d2);
int from = day1.get(Calendar.DAY_OF_YEAR);
int to = day2.get(Calendar.DAY_OF_YEAR);
int difference = to-from;