Muszę znaleźć liczbę dni między dwiema datami : jeden pochodzi z raportu, a drugi to data bieżąca. Mój fragment:
int age=calculateDifference(agingDate, today);
Oto calculateDifference
prywatna metoda agingDate
i today
są to Date
obiekty, tylko dla twojego wyjaśnienia. Śledziłem dwa artykuły z forum Java, wątek 1 / wątek 2 .
Działa dobrze w samodzielnym programie, chociaż kiedy włączam to do mojej logiki do czytania z raportu, otrzymuję niezwykłą różnicę w wartościach.
Dlaczego tak się dzieje i jak mogę to naprawić?
EDYTOWAĆ :
Otrzymuję większą liczbę dni w porównaniu z rzeczywistą liczbą dni.
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;
}
Uwaga :
Niestety żadna z odpowiedzi nie pomogła mi rozwiązać problemu. Mam osiągnąć ten problem z pomocą Joda czasu bibliotece.
java.util.Date
,java.util.Calendar
ijava.text.SimpleDateFormat
są teraz spuścizny , wyparta przez java.time klas. Zobacz samouczek firmy Oracle . Podobnie projekt Joda-Time jest teraz w trybie konserwacji , a zespół doradza migrację do klas java.time.Odpowiedzi:
Sugerowałbym, abyś skorzystał z doskonałej biblioteki Joda Time zamiast wadliwego java.util.Date i przyjaciół. Możesz po prostu napisać
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
źródło
Mogę być za późno na dołączenie do gry, ale co do cholery? :)
Czy uważasz, że jest to problem z wątkami? Na przykład jak korzystasz z wyników tej metody? LUB
Czy możemy zmienić Twój kod, aby zrobić coś tak prostego, jak:
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."); }
źródło
Różnica / (24 * itd.) Nie bierze pod uwagę strefy czasowej, więc jeśli domyślna strefa czasowa zawiera czas letni, może to spowodować wyłączenie obliczeń.
Ten link ma ładną małą implementację.
Oto źródło powyższego linku na wypadek, gdyby link zszedł:
/** 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; }
i
/** 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; }
źródło
while
powinna być.while (cursor.getTimeInMillis() <= endInstant);
W przeciwnym razie otrzymasz nieskończoną pętlę, jeśli mniej niż jeden dzień.java.time
W Javie 8 i nowszych wersjach użyj struktury java.time ( samouczek ).
Duration
Duration
Klasa reprezentuje okres czasu jako liczbę sekund plus drugiego frakcyjnej. Może liczyć dni, godziny, minuty i sekundy.ZonedDateTime now = ZonedDateTime.now(); ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10); Duration duration = Duration.between(oldDate, now); System.out.println(duration.toDays());
ChronoUnit
Jeśli potrzebujesz tylko liczby dni, możesz też użyć rozszerzenia
ChronoUnit
wyliczenia . Zwróć uwagę, że metody obliczeniowe zwracająlong
zamiastint
.long days = ChronoUnit.DAYS.between( then, now );
źródło
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; } }
źródło
To zależy od tego, co zdefiniujesz jako różnicę. Aby porównać dwie daty o północy, możesz to zrobić.
long day1 = ...; // in milliseconds. long day2 = ...; // in milliseconds. long days = (day2 - day1) / 86400000;
źródło
Rozwiązanie wykorzystujące różnicę między czasem milisekund, z prawidłowym zaokrągleniem dat czasu letniego:
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 }
Jedna uwaga: oczywiście daty muszą być w jakiejś strefie czasowej.
Ważny kod:
Math.round( (to - from) / 86400000D )
Jeśli nie chcesz, możesz użyć dat UTC,
źródło
Ilustracja problemu: (mój kod oblicza deltę w tygodniach, ale ten sam problem dotyczy delty w dniach)
Oto bardzo rozsądnie wyglądająca realizacja:
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; }
Ale ten test się nie powiedzie:
public void testGetDeltaInWeeks() { delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23); assertEquals("weeks between Feb23 and Mar09", 2, delta); }
Powodem jest:
ale każdy, kto patrzy na kalendarz, zgodzi się, że odpowiedź to 2.
źródło
Używam tej funkcji:
DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days
moja funkcja:
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; }
źródło
Spójrz na metody getFragmentInDays w tej klasie Apache commons-lang
DateUtils
.źródło
Opierając się na odpowiedzi @ Mad_Troll, opracowałem tę metodę.
Przeprowadziłem około 30 przypadków testowych przeciwko temu, jest to jedyna metoda, która poprawnie obsługuje fragmenty pory dnia.
Przykład: Jeśli zdasz teraz i teraz + 1 milisekunda, to nadal jest ten sam dzień. Robi
1-1-13 23:59:59.098
się1-1-13 23:59:59.099
zwraca 0 dni, poprawnie; wiele innych opublikowanych tutaj metod nie zrobi tego poprawnie.Warto zauważyć, że nie dba o to, w jaki sposób je umieścisz, jeśli data zakończenia przypada przed datą rozpoczęcia, będzie odliczana wstecz.
/** * 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; }
Możesz usunąć
@NotNull
adnotacje, są one używane przez Intellij do analizy kodu w locieźródło
Mówisz, że „działa dobrze w samodzielnym programie”, ale otrzymujesz „niezwykłe wartości różnic”, gdy „włączasz to do mojej logiki, aby odczytać z raportu”. To sugeruje, że raport zawiera wartości, dla których nie działa poprawnie, a Twój samodzielny program nie ma tych wartości. Zamiast samodzielnego programu proponuję przypadek testowy. Napisz przypadek testowy podobnie jak w przypadku samodzielnego programu, podklasując go z klasy TestCase firmy JUnit. Teraz możesz uruchomić bardzo konkretny przykład, wiedząc, jakiej wartości oczekujesz (i nie podawaj jej dzisiaj za wartość testową, ponieważ dziś zmienia się w czasie). Jeśli wpiszesz wartości, których użyłeś w samodzielnym programie, twoje testy prawdopodobnie przejdą. To świetnie - chcesz, aby te sprawy nadal działały. Teraz dodaj wartość ze swojego raportu, taką, która nie t działa dobrze. Twój nowy test prawdopodobnie się nie powiedzie. Dowiedz się, dlaczego to się nie udaje, napraw to i przejdź do koloru zielonego (wszystkie testy zakończą się pomyślnie). Uruchom raport. Zobacz, co jest nadal zepsute; napisać test; spraw, by minęło. Wkrótce przekonasz się, że Twój raport działa.
źródło
Sto wierszy kodu dla tej podstawowej funkcji ???
Tylko prosta metoda:
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; }
źródło
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; }
źródło
ThreeTen-Extra
Odpowiedz przez Vitalii Fedorenko jest poprawna, opisujący jak wykonać te obliczenia w nowoczesny sposób z java.time klas (
Duration
iChronoUnit
) wbudowanych w Java 8 i później (i back-przeniesiony do Java 6 i 7 i Android ).Days
Jeśli używasz rutynowo w swoim kodzie liczby dni, możesz zastąpić zwykłe liczby całkowite za pomocą klasy.
Days
Klasy można znaleźć w ThreeTen-Extra projektu przedłużenia java.time i udowodnienia podstawy do ewentualnych uzupełnień java.time przyszłych.Days
Klasa dostarcza typu bezpieczny sposób reprezentujący liczbę dni w aplikacji. Klasa zawiera wygodne stałe dlaZERO
iONE
.Biorąc pod uwagę stare, przestarzałe
java.util.Date
obiekty w pytaniu, najpierw przekonwertuj je na nowoczesnejava.time.Instant
obiekty. Stare klasy data-czas mają nowo dodane metody ułatwiające konwersję do java.time, takie jakjava.util.Date::toInstant
.Instant start = utilDateStart.toInstant(); // Inclusive. Instant stop = utilDateStop.toInstant(); // Exclusive.
Przekaż oba
Instant
obiekty do metody fabrycznej dlaorg.threeten.extra.Days
.W bieżącej implementacji (2016-06) jest to wywołanie opakowania
java.time.temporal.ChronoUnit.DAYS.between
,ChronoUnit
szczegółowe informacje można znaleźć w dokumentacji klasy. Dla jasności: wszystkie wielkie literyDAYS
są w wyliczeniu,ChronoUnit
podczas gdy inicjałDays
jest klasą z ThreeTen-Extra.Możesz przekazać te
Days
obiekty dookoła własnego kodu. Możesz serializować do ciągu w standardowym formacie ISO 8601 , wywołująctoString
. Ten formatPnD
używa aP
do zaznaczenia początku iD
oznacza „dni”, z pewną liczbą dni pomiędzy nimi. Obie klasy java.time i ThreeTen-Extra używają tych standardowych formatów domyślnie podczas generowania i analizowania ciągów znaków reprezentujących wartości daty i godziny.Days days = Days.parse( "P3D" );
źródło
Ten kod oblicza dni między dwoma ciągami dat:
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; }
źródło
Jeśli szukasz rozwiązania, które zwraca odpowiednią liczbę lub dni między np.
11/30/2014 23:59
A12/01/2014 00:01
oto rozwiązanie wykorzystujące czas Joda.private int getDayDifference(long past, long current) { DateTime currentDate = new DateTime(current); DateTime pastDate = new DateTime(past); return currentDate.getDayOfYear() - pastDate.getDayOfYear(); }
Ta implementacja zwróci
1
różnicę w dniach. Większość przedstawionych tutaj rozwiązań oblicza różnicę w milisekundach między dwiema datami. Oznacza to,0
że zostanie zwrócony, ponieważ różnica między tymi dwiema datami wynosi tylko 2 minuty.źródło
Powinieneś używać biblioteki Joda Time, ponieważ Java Util Date czasami zwraca nieprawidłowe wartości.
Joda vs Java Util Date
Na przykład dni między wczoraj (dd-mm-rrrr, 12-07-2016) a pierwszym dniem roku 1957 (dd-mm-rrrr, 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); }
Dlatego naprawdę radzę korzystać z biblioteki Joda Time.
źródło
Zrobiłem to w ten sposób. To jest łatwe :)
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;
źródło