Расчет дней между двумя датами с помощью Java

150

Я хочу Java-программу, которая рассчитывает дни между двумя датами.

  1. Введите первую дату (немецкая запись; с пробелами: "дд мм гггг")
  2. Введите вторую дату.
  3. Программа должна рассчитать количество дней между двумя датами.

Как я могу включить високосные годы и летнее время?

Мой код:

import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;

public class NewDateDifference {

    public static void main(String[] args) {

        System.out.print("Insert first date: ");
        Scanner s = new Scanner(System.in);
        String[] eingabe1 = new String[3];

        while (s.hasNext()) {
            int i = 0;
            insert1[i] = s.next();
            if (!s.hasNext()) {
                s.close();
                break;
            }
            i++;
        }

        System.out.print("Insert second date: ");
        Scanner t = new Scanner(System.in);
        String[] insert2 = new String[3];

        while (t.hasNext()) {
            int i = 0;
            insert2[i] = t.next();
            if (!t.hasNext()) {
                t.close();
                break;
            }
            i++;
        }

        Calendar cal = Calendar.getInstance();

        cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(insert1[0]));
        cal.set(Calendar.MONTH, Integer.parseInt(insert1[1]));
        cal.set(Calendar.YEAR, Integer.parseInt(insert1[2]));
        Date firstDate = cal.getTime();

        cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(insert2[0]));
        cal.set(Calendar.MONTH, Integer.parseInt(insert2[1]));
        cal.set(Calendar.YEAR, Integer.parseInt(insert2[2]));
        Date secondDate = cal.getTime();


        long diff = secondDate.getTime() - firstDate.getTime();

        System.out.println ("Days: " + diff / 1000 / 60 / 60 / 24);
    }
}
user3025448
источник
Что не работает? Это сбой? Это дает вам неправильные номера?
jens108
Где объявление массива: insert1?
Рис
1
insert1 = eingabe1 по-немецки :)
peter.petrov
@ peter.petrov Ах, понятно!
Рис
Я думаю , что он имеет проблемы с mmи MM: P
Sage

Ответы:

230

ОБНОВЛЕНИЕ: оригинальный ответ от 2013 года теперь устарел, потому что некоторые классы были заменены. Новый способ сделать это - использовать новые java.timeклассы.

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd MM yyyy");
String inputString1 = "23 01 1997";
String inputString2 = "27 04 1997";

try {
    LocalDateTime date1 = LocalDate.parse(inputString1, dtf);
    LocalDateTime date2 = LocalDate.parse(inputString2, dtf);
    long daysBetween = Duration.between(date1, date2).toDays();
    System.out.println ("Days: " + daysBetween);
} catch (ParseException e) {
    e.printStackTrace();
}

Обратите внимание, что это решение даст количество фактических 24 часовых дней, а не количество календарных дней. Для последнего используйте

long daysBetween = ChronoUnit.DAYS.between(date1, date2)

Оригинальный ответ (устаревший с Java 8)

Вы делаете некоторые преобразования с вашими строками, которые не нужны. Для этого есть SimpleDateFormatкласс - попробуйте это:

SimpleDateFormat myFormat = new SimpleDateFormat("dd MM yyyy");
String inputString1 = "23 01 1997";
String inputString2 = "27 04 1997";

try {
    Date date1 = myFormat.parse(inputString1);
    Date date2 = myFormat.parse(inputString2);
    long diff = date2.getTime() - date1.getTime();
    System.out.println ("Days: " + TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS));
} catch (ParseException e) {
    e.printStackTrace();
}

РЕДАКТИРОВАТЬ: Поскольку были некоторые дискуссии о правильности этого кода: он действительно заботится о високосных годах. Однако TimeUnit.DAYS.convertфункция теряет точность, поскольку миллисекунды преобразуются в дни (дополнительную информацию см. В связанном документе). Если это проблема, diffтакже можно конвертировать вручную:

float days = (diff / (1000*60*60*24));

Обратите внимание, что это floatзначение, а не обязательно int.

jens108
источник
40
Это плохая реализация, которая не учитывает високосные годы должным образом.
Groovy Ed
3
TimeUnit.DAYS.convert (diff, TimeUnit.MILLISECONDS)); <3
Гихго
3
@GroovyEd Из того, что я тестировал, кажется, что у этого кода нет проблем с високосными годами. Обратите внимание, что TimeUnit.Days.convert () будет игнорировать оставшиеся единицы, например, преобразование 999 миллисекунд в секунды приводит к 0. Это означает, что если вы используете new Date () в качестве одного из объектов Date, вы можете получить на один день меньше, поэтому будьте осторожны
Клитос Г.
3
Это работает и для високосных лет. проверьте это String inputString1 = "28 2 2016"; String inputString2 = "1 3 2016";: 2
Рохит Гайквад
10
Я считаю, что это работает правильно для високосных лет, но это портит летнее время. Если в вашем регионе есть летнее время, то каждый год есть один день с 23 часами и один день с 25 часами. Это решение неправильно предполагает, что каждый день имеет 24 часа. Следовательно, он дает неправильный ответ для любого периода, который начинается в периоды, когда нет дневного света, и заканчивается в течение летнего периода. НЕ ИСПОЛЬЗУЙТЕ ЭТО РЕШЕНИЕ - есть лучшие способы.
Дауд ибн Карим
100

Самый простой способ:

public static long getDifferenceDays(Date d1, Date d2) {
    long diff = d2.getTime() - d1.getTime();
    return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
Барт
источник
7
Ну, в основном это то же самое, что и текущий лучший ответ , хотя этот ответ предоставляет его как функцию.
Эндрю Т.
16
Обратите внимание, что это считается только тогда, когда интервал времени между двумя датами больше 24 часов (что довольно очевидно из кода), таким образомgetDifferenceDays(11PM, 4 AM nextday) == 0
ReDetection
1
эта реализация занимает последний день как сегодня. Например, если я запускаю программу с d1 = сегодня и d2 = вчера, возвращается 0 дней ..
karvoynistas
1
@Nav, потому что в июне 30 дней.
Дауд ибн Карим
1
Этот ответ неверен. Это не относится к летнему времени правильно. Не используйте его, если хотите получить правильные результаты.
Дауд ибн Карим
89

В Java 8 вы можете сделать это, используя LocalDateи DateTimeFormatter. Из Javadoc из LocalDate:

LocalDate - это неизменяемый объект даты-времени, представляющий дату, которая часто рассматривается как год-месяц-день.

И шаблон может быть построен с помощью DateTimeFormatter. Вот Javadoc и соответствующие символы шаблона, которые я использовал:

Символ - Значение - Презентация - Примеры

у - год эры - год - 2004; 04

M / L - месяц года - число / текст - 7; 07; июль; июль; J

d - число месяца - число - 10

Вот пример:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Java8DateExample {
    public static void main(String[] args) throws IOException {
        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MM yyyy");
        final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        final String firstInput = reader.readLine();
        final String secondInput = reader.readLine();
        final LocalDate firstDate = LocalDate.parse(firstInput, formatter);
        final LocalDate secondDate = LocalDate.parse(secondInput, formatter);
        final long days = ChronoUnit.DAYS.between(firstDate, secondDate);
        System.out.println("Days between: " + days);
    }
}

Пример ввода / вывода с более поздним последним:

23 01 1997
27 04 1997
Days between: 94

С более свежим первым:

27 04 1997
23 01 1997
Days between: -94

Ну, вы можете сделать это как метод более простым способом:

public static long betweenDates(Date firstDate, Date secondDate) throws IOException
{
    return ChronoUnit.DAYS.between(firstDate.toInstant(), secondDate.toInstant());
}
mkobit
источник
4
Отличный пример. Это автоматически учитывает високосный год. Если вы проверили 1991 и 1992 (високосный год), он рассчитывает правильно. Отлично!
Woahguy
Отличное использование стандартной библиотеки.
dieresys
Это должен быть текущий принятый ответ. Стандартное использование библиотеки и учет високосных лет и летнего времени не будут проблемой.
Пехмолелу
3
Это текущий / современный ответ (остальные устарели). Это также учитывает летнее время (DST). Чтобы использовать на Java 6 или 7, получите ThreeTen Backport . На не-новом Android ThreeTenABP .
Оле В.В.
не учитывается переход на летнее время
Alex
63

Большинство / все ответы вызвали у нас проблемы, когда наступило летнее время. Вот наше рабочее решение на все даты, без использования JodaTime. Он использует объекты календаря:

public static int daysBetween(Calendar day1, Calendar day2){
    Calendar dayOne = (Calendar) day1.clone(),
            dayTwo = (Calendar) day2.clone();

    if (dayOne.get(Calendar.YEAR) == dayTwo.get(Calendar.YEAR)) {
        return Math.abs(dayOne.get(Calendar.DAY_OF_YEAR) - dayTwo.get(Calendar.DAY_OF_YEAR));
    } else {
        if (dayTwo.get(Calendar.YEAR) > dayOne.get(Calendar.YEAR)) {
            //swap them
            Calendar temp = dayOne;
            dayOne = dayTwo;
            dayTwo = temp;
        }
        int extraDays = 0;

        int dayOneOriginalYearDays = dayOne.get(Calendar.DAY_OF_YEAR);

        while (dayOne.get(Calendar.YEAR) > dayTwo.get(Calendar.YEAR)) {
            dayOne.add(Calendar.YEAR, -1);
            // getActualMaximum() important for leap years
            extraDays += dayOne.getActualMaximum(Calendar.DAY_OF_YEAR);
        }

        return extraDays - dayTwo.get(Calendar.DAY_OF_YEAR) + dayOneOriginalYearDays ;
    }
}
Джон лихей
источник
отлично справляется с переключением летнего времени
Рави Санвал
1
+1 за ответ, однако, хочу добавить, что нам нужны Calendar dayOne = (Calendar) day1.clone(), dayTwo = (Calendar) day2.clone();строки, поскольку они гарантируют, что исходные значения календаря не будут переопределены. Я удалил эти строки, считая их избыточными, и потратил впустую час на то, что значения моего исходного объекта перезаписывались внутри этой функции.
Рохан Кандвал
2
Не забывайте, что значение месяца основано на 0 в классе календаря. calendar.set (2015, 11, 30, 0, 00, 00); на самом деле означает 30/12/2015
Дмитрий
20

Лучший способ, и он превращается в строку в качестве бонуса;)

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    try {
        //Dates to compare
        String CurrentDate=  "09/24/2015";
        String FinalDate=  "09/26/2015";

        Date date1;
        Date date2;

        SimpleDateFormat dates = new SimpleDateFormat("MM/dd/yyyy");

        //Setting dates
        date1 = dates.parse(CurrentDate);
        date2 = dates.parse(FinalDate);

        //Comparing dates
        long difference = Math.abs(date1.getTime() - date2.getTime());
        long differenceDates = difference / (24 * 60 * 60 * 1000);

        //Convert long to String
        String dayDifference = Long.toString(differenceDates);

        Log.e("HERE","HERE: " + dayDifference);
    }
    catch (Exception exception) {
        Log.e("DIDN'T WORK", "exception " + exception);
    }
}
SoVinceble
источник
что такое возможность броска исключения?
Код
Обратите внимание, что это не работает в високосные годы. Например, с «15.02.2020» по «04.02.2020» - 47 дней. Эта логика придет с 46.
user1070304
11

Использование:

public int getDifferenceDays(Date d1, Date d2) {
    int daysdiff = 0;
    long diff = d2.getTime() - d1.getTime();
    long diffDays = diff / (24 * 60 * 60 * 1000) + 1;
    daysdiff = (int) diffDays;
    return daysdiff;
}
Саидеш Килару
источник
Учитывает ли это високосные годы и летнее время?
Макс Александр Ханна
1
@MaxAlexanderHanna правильно учитывает високосные годы, а не летнее время. Он дает правильный ответ только тогда, когда период начинается в летнее время, а заканчивается в летнее время. Во всех остальных случаях он выключен одним.
Дауд ибн Карим
1
@saidesh_kilaru Что такое "+ 1"? Я думаю, что вы должны удалить это.
Алиса
У меня была проблема, что приведение к INT привело к 4, а приведение к float привело к 4.9, так что это не совсем то, что я хотел; Может быть, недостаточно ясно описали случаи для даты1 в 23:59 и даты2 в 00:01 и ожидаемого результата.
EricG
10

Библиотеки дат Java, как известно, сломаны. Я бы посоветовал использовать Joda Time . Он позаботится о високосном году, часовом поясе и так далее.

Минимальный рабочий пример:

import java.util.Scanner;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class DateTestCase {

    public static void main(String[] args) {

        System.out.print("Insert first date: ");
        Scanner s = new Scanner(System.in);
        String firstdate = s.nextLine();
        System.out.print("Insert second date: ");
        String seconddate = s.nextLine();

        // Formatter
        DateTimeFormatter dateStringFormat = DateTimeFormat
                .forPattern("dd MM yyyy");
        DateTime firstTime = dateStringFormat.parseDateTime(firstdate);
        DateTime secondTime = dateStringFormat.parseDateTime(seconddate);
        int days = Days.daysBetween(new LocalDate(firstTime),
                                    new LocalDate(secondTime)).getDays();
        System.out.println("Days between the two dates " + days);
    }
}
Julien
источник
4
Этот ответ может быть улучшен несколькими способами. (a) Укажите часовой пояс, а не полагайтесь на JVM по умолчанию. Поэтому при создании этого DateTimeFormatter добавьте вызов withZone( DateTimeZone.forID( "Europe/Berlin" ) ). (б) Зачем использовать LocalDateв daysBetweenвызове? Просто передайте объекты DateTime (firstTime, secondTime). Для полных дней звоните withTimeAtStartOfDays. (c) Я бы использовал имена переменных, firstDateTimeа не firstTimeчтобы избежать двусмысленности между датой, временем и датой-временем. (d) Добавьте несколько try-catch для обработки неверного ввода данных, который не соответствует нашему ожидаемому формату.
Василий Бурк
5
String dateStart = "01/14/2015 08:29:58";
String dateStop = "01/15/2015 11:31:48";

//HH converts hour in 24 hours format (0-23), day calculation
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

Date d1 = null;
Date d2 = null;

d1 = format.parse(dateStart);
d2 = format.parse(dateStop);

//in milliseconds
long diff = d2.getTime() - d1.getTime();

long diffSeconds = diff / 1000 % 60;
long diffMinutes = diff / (60 * 1000) % 60;
long diffHours = diff / (60 * 60 * 1000) % 24;
long diffDays = diff / (24 * 60 * 60 * 1000);

System.out.print(diffDays + " days, ");
System.out.print(diffHours + " hours, ");
System.out.print(diffMinutes + " minutes, ");
System.out.print(diffSeconds + " seconds.");
Гаутам Вирадия
источник
1

Когда я запускаю вашу программу, она даже не доходит до того, что я могу ввести вторую дату.

Это проще и менее подвержено ошибкам.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Test001 {

    public static void main(String[] args) throws Exception {

        BufferedReader br = null;

        br = new BufferedReader(new InputStreamReader(System.in));
        SimpleDateFormat sdf = new SimpleDateFormat("dd MM yyyy");

        System.out.println("Insert first date : ");
        Date dt1 = sdf.parse(br.readLine().trim());

        System.out.println("Insert second date : ");
        Date dt2 = sdf.parse(br.readLine().trim());

        long diff = dt2.getTime() - dt1.getTime();

        System.out.println("Days: " + diff / 1000L / 60L / 60L / 24L);

        if (br != null) {
            br.close();
        }
    }
}
peter.petrov
источник
1
// date format, it will be like "2015-01-01"
private static final String DATE_FORMAT = "yyyy-MM-dd";

// convert a string to java.util.Date
public static Date convertStringToJavaDate(String date)
        throws ParseException {
    DateFormat dataFormat = new SimpleDateFormat(DATE_FORMAT);
    return dataFormat.parse(date);
}

// plus days to a date
public static Date plusJavaDays(Date date, int days) {
    // convert to jata-time
    DateTime fromDate = new DateTime(date);
    DateTime toDate = fromDate.plusDays(days);
    // convert back to java.util.Date
    return toDate.toDate();
}

// return a list of dates between the fromDate and toDate
public static List<Date> getDatesBetween(Date fromDate, Date toDate) {
    List<Date> dates = new ArrayList<Date>(0);
    Date date = fromDate;
    while (date.before(toDate) || date.equals(toDate)) {
        dates.add(date);
        date = plusJavaDays(date, 1);
    }
    return dates;
}
Dayong
источник
0

Мы можем использовать Java-библиотеку LocalDate и ChronoUnit, код ниже работает нормально. Дата должна быть в формате гггг-мм-дд.

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
class Solution {
    public int daysBetweenDates(String date1, String date2) {
        LocalDate dt1 = LocalDate.parse(date1);
        LocalDate dt2= LocalDate.parse(date2);

        long diffDays = ChronoUnit.DAYS.between(dt1, dt2);

        return Math.abs((int)diffDays);
    }
}
Абхиджит Упадхьяй
источник
4
Спасибо за желание внести свой вклад. Я считаю, что это хорошее предложение уже было представлено в ответе mkobit.
Оле В.В.