Obliczanie dni między dwiema datami w Javie

150

Potrzebuję programu Java, który oblicza dni między dwiema datami.

  1. Wpisz pierwszą datę (notacja niemiecka; ze spacjami: „dd mm rrrr”)
  2. Wpisz drugą datę.
  3. Program powinien obliczyć liczbę dni między dwiema datami.

Jak uwzględnić lata przestępne i czas letni?

Mój kod:

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
źródło
Co nie działa? Czy to się zawiesza? Czy podaje złe liczby?
jens108
Gdzie jest deklaracja tablicy: insert1?
Rhys
1
insert1 = eingabe1 po niemiecku :)
peter.petrov
@ peter.petrov Ach, rozumiem!
Rhys
Myślę, że ma problem z mmi MM: P
Sage

Odpowiedzi:

230

AKTUALIZACJA: Oryginalna odpowiedź z 2013 roku jest już nieaktualna, ponieważ niektóre klasy zostały zastąpione. Nowym sposobem na to jest użycie nowych java.timeklas.

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

Zwróć uwagę, że to rozwiązanie poda liczbę rzeczywistych 24-godzinnych dni, a nie liczbę dni kalendarzowych. W tym drugim przypadku użyj

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

Oryginalna odpowiedź (nieaktualna od wersji Java 8)

Dokonujesz pewnych konwersji za pomocą swoich ciągów, które nie są konieczne. Jest do tego SimpleDateFormatklasa - spróbuj tego:

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

EDYCJA: Ponieważ toczyły się dyskusje na temat poprawności tego kodu: rzeczywiście uwzględnia on lata przestępne. Jednak TimeUnit.DAYS.convertfunkcja traci precyzję, ponieważ milisekundy są konwertowane na dni (zobacz połączony dokument, aby uzyskać więcej informacji). Jeśli jest to problem, diffmożna również przekonwertować ręcznie:

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

Zauważ, że jest to floatwartość, niekoniecznie int.

jens108
źródło
40
To zła implementacja, która nie uwzględnia odpowiednio lat przestępnych.
Groovy Ed
3
TimeUnit.DAYS.convert (diff, TimeUnit.MILLISECONDS)); <3
Guihgo,
3
@GroovyEd Z tego, co przetestowałem, wynika, że ​​ten kod nie ma problemu z latami przestępnymi. Zwróć uwagę, że TimeUnit.Days.convert () zignoruje pozostałe jednostki, np. Zamiana 999 milisekund na sekundy daje 0. Oznacza to, że jeśli użyjesz new Date () jako jednego z obiektów Date, możesz otrzymać jeden dzień mniej, więc uważaj
Klitos G.
3
Działa również w latach przestępnych. sprawdź tę odpowiedź String inputString1 = "28 2 2016"; String inputString2 = "1 3 2016";: 2
Rohit Gaikwad
10
Uważam, że działa to poprawnie w latach przestępnych, ale psuje czas letni. Jeśli w Twoim regionie obowiązuje czas letni, to każdego roku jeden dzień ma 23 godziny i jeden dzień ma 25 godzin. To rozwiązanie błędnie zakłada, że ​​każdy dzień ma 24 godziny. W związku z tym podaje błędną odpowiedź dla dowolnego okresu, który rozpoczyna się w okresie innym niż czas letni i kończy się w okresie letnim. NIE UŻYWAJ TEGO ROZWIĄZANIA - są lepsze sposoby.
Dawood ibn Kareem
100

Najprostszy sposób:

public static long getDifferenceDays(Date d1, Date d2) {
    long diff = d2.getTime() - d1.getTime();
    return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
Bart
źródło
7
Cóż, w zasadzie jest to to samo, co obecnie najlepsza odpowiedź , chociaż ta odpowiedź zapewnia ją jako funkcję.
Andrew T.
16
Zauważ, że liczy się to tylko wtedy, gdy odstęp czasu między dwiema datami jest większy niż 24 godziny (dość oczywiste z kodu), a zatemgetDifferenceDays(11PM, 4 AM nextday) == 0
ReDetection
1
ta implementacja zajmuje ostatni dzień, tak jak dzisiaj. Na przykład, jeśli uruchomię program z d1 = dzisiaj i d2 = wczoraj, zwraca 0 dni ..
karvoynistas
1
@Nav to dlatego, że w czerwcu jest 30 dni.
Dawood ibn Kareem
1
Ta odpowiedź jest nieprawidłowa. Nie radzi sobie poprawnie z czasem letnim. Nie używaj go, jeśli chcesz uzyskać poprawne wyniki.
Dawood ibn Kareem
89

W Javie 8 można to osiągnąć za pomocą LocalDatei DateTimeFormatter. Z Javadoc z LocalDate:

LocalDate to niezmienny obiekt daty i godziny, który reprezentuje datę, często postrzeganą jako rok-miesiąc-dzień.

Wzór można skonstruować za pomocą DateTimeFormatter. Oto Javadoc i odpowiednie znaki wzorca, których użyłem:

Symbol - Znaczenie - Prezentacja - Przykłady

y - rok ery - rok - 2004; 04

M / L - miesiąc roku - liczba / tekst - 7; 07; Jul; Lipiec; jot

d - dzień miesiąca - liczba - 10

Oto przykład:

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

Przykładowe wejście / wyjście z nowszym ostatnim:

23 01 1997
27 04 1997
Days between: 94

Z nowszymi pierwszymi:

27 04 1997
23 01 1997
Days between: -94

Cóż, możesz to zrobić jako metodę w prostszy sposób:

public static long betweenDates(Date firstDate, Date secondDate) throws IOException
{
    return ChronoUnit.DAYS.between(firstDate.toInstant(), secondDate.toInstant());
}
mkobit
źródło
4
Doskonały przykład. To automatycznie uwzględnia rok przestępny. Jeśli zaznaczyłeś 1991 i 1992 (rok przestępny), to oblicza poprawnie. Idealny!
woahguy
Świetne wykorzystanie standardowej biblioteki.
dieresys
Powinna to być obecnie akceptowana odpowiedź. Standardowe wykorzystanie bibliotek i rozliczenia lat przestępnych i czasu letniego nie będą stanowić problemu.
Pehmolelu
3
To jest aktualna / nowoczesna odpowiedź (pozostałe są nieaktualne). Uwzględnia również czas letni (DST). Aby używać w Javie 6 lub 7, pobierz ThreeTen Backport . Na nie-nowym Androidzie ThreeTenABP .
Ole VV
nie bierze pod uwagę czasu letniego
Alex
63

Większość odpowiedzi spowodowała problemy, gdy nadszedł czas letni. Oto nasze rozwiązanie robocze dla wszystkich dat, bez korzystania z JodaTime. Wykorzystuje obiekty kalendarza:

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 ;
    }
}
John Leehey
źródło
ładnie radzi sobie z przełącznikami czasu letniego
Ravi Sanwal
1
+1 do odpowiedzi chcemy jednak dodać, że potrzebujemy Calendar dayOne = (Calendar) day1.clone(), dayTwo = (Calendar) day2.clone();wierszy, ponieważ zapewniają, że oryginalne wartości kalendarza nie zostaną nadpisane. Usunąłem te wiersze, uważając je za zbędne i zmarnowałem godzinę na fakt, że wartości mojego oryginalnego obiektu były nadpisywane z poziomu tej funkcji.
Rohan Kandwal
2
Nie zapominaj, że wartość miesiąca w klasie kalendarza jest oparta na 0. calendar.set (2015, 11, 30, 0, 00, 00); właściwie oznacza 30.12.2015
Dmitry
20

Najlepszy sposób, a jako bonus zamienia się na String;)

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
źródło
jaka jest możliwość rzucenia wyjątku?
CoDe
Zauważ, że to nie działa w latach przestępnych. Na przykład od „15.02.2020” do „02.04.2020” wynosi 47 dni. Ta logika
wyłoni
11

Posługiwać się:

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;
}
saidesh kilaru
źródło
Czy to uwzględnia lata przestępne i czas letni?
Max Alexander Hanna
1
@MaxAlexanderHanna poprawnie uwzględnia lata przestępne, ale nie czas letni. Podaje poprawną odpowiedź tylko wtedy, gdy okres zaczyna się poza czasem letnim, ale kończy się w czasie letnim. We wszystkich innych przypadkach jest zmniejszony o jeden.
Dawood ibn Kareem
1
@saidesh_kilaru Co to jest „+1”? Myślę, że powinieneś to usunąć.
Alisa
Miałem problem, że rzutowanie na INT skutkowało 4, a rzutowanie na float dawało 4.9, więc to nie jest to, czego chciałem; Może nie wystarczająco jasno opisał przypadki dla daty 1 o 23:59 i daty 2 o 00:01 i jaki byłby oczekiwany wynik.
EricG
10

Biblioteki dat Java są notorycznie zepsute. Radziłbym użyć Joda Time . Zadba o rok przestępny, strefę czasową i tak dalej.

Minimalny przykład pracy:

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
źródło
4
Tę odpowiedź można poprawić na kilka sposobów. (a) Określ strefę czasową zamiast polegać na domyślnej wartości maszyny JVM. Dlatego podczas tworzenia tego DateTimeFormatter Dodaj wywołanie do withZone( DateTimeZone.forID( "Europe/Berlin" ) ). (b) Po co używać LocalDatew daysBetweenrozmowie? Po prostu przekaż obiekty DateTime (firstTime, secondTime). Dzwoń przez całe dni withTimeAtStartOfDays. (c) firstDateTimeZamiast tego użyłbym nazw zmiennych , firstTimeaby uniknąć niejednoznaczności między obiektami daty, godziny i daty i godziny. (d) Dodaj trochę try-catch, aby obsłużyć błędne dane wejściowe, które nie pasują do naszego oczekiwanego formatu.
Basil Bourque,
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.");
Gautam Viradiya
źródło
1

Kiedy uruchamiam twój program, nawet nie doprowadza mnie to do punktu, w którym mogę wprowadzić drugą datę.

Jest to prostsze i mniej podatne na błędy.

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
źródło
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
źródło
0

Możemy skorzystać z biblioteki java LocalDate i ChronoUnit, poniższy kod działa poprawnie. Data powinna mieć format rrrr-MM-dd.

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);
    }
}
Abhijeet Upadhyay
źródło
4
Dzięki za chęć udziału. Uważam, że tę dobrą sugestię przedstawił już w odpowiedzi mkobit.
Ole VV