Myślę, że zwrot ujemnej liczby dni dostarcza istotnych informacji. I powinieneś używać $your_date-$now, jeśli chcesz, aby przyszła data zwróciła dodatnią liczbę całkowitą.
Tim
23
Co z sekundami przestępnymi? Nie wszystkie dni mają dokładnie 24 * 60 * 60 sekund. Ten kod może być wystarczający ze względów praktycznych, ale nie jest dokładny w bardzo rzadkich przypadkach brzegowych.
Benjamin Brizzi
49
Zapomnij o sekundach przestępnych (nie, w rzeczywistości też je rozważ), ale NIE uwzględnia to zmian czasu letniego! Każdego roku może być wyłączony przez cały dzień poza tymi granicami. Musisz użyć klas DateTime.
Levi,
6
@billynoah Przepraszamy, nigdy nie wróciłem, aby zaktualizować mój komentarz. Musisz być ostrożny przy strefach czasowych letnich. Jeśli porównasz datę z czasem letnim, ponownie data bez niej, na przykład zamiast 7 dni, zwraca 6,9 dnia. Zabranie głosu zwraca 6 zamiast 7.
Alex Angelico
4
Strtotime () nie tylko zawodzi w ciągu 20 lat, ale teraz jest bezużyteczny. OP określił Daty, a nie Czas. Daty mogą być dość stare. Oto prostsza odpowiedź: $ NumberDays = gregoriantojd ($ EndM, $ EndD, $ EndY) - gregoriantojd ($ StartM, $ StartD, $ StartY); (dla zakresu obejmującego). W przeciwieństwie do klasy Datetime, gregoriański do Julian jest dostępny w wersji v4 up. Prawidłowy zakres to od 4714 pne do 9999 ne Uważaj na funky kolejność parametrów (tak jakbyś potrzebował tego ostrzeżenia dla php).
Guy Gordon,
522
Jeśli używasz PHP 5.3 >, jest to zdecydowanie najdokładniejszy sposób obliczenia różnicy:
Zauważ, że ponieważ mówimy o przedziałach czasowych, a nie określonych punktach w czasie, składnia formatu jest inna niż składnia date () i strftime (). Składnię interwałów można znaleźć tutaj: php.net/manual/en/dateinterval.format.php
Andrew
1
lub w moim przypadku liczba dni pomiędzy to $ date2-> diff ($ date1) -> format ("% a") - 1
xeo
13
Pamiętaj, że jeśli masz daty w terminach, nie zadziała to tak, jak można się spodziewać. Na przykład, jeśli masz interwał 23:30 godzin ... i są one w różne dni, różnica będzie wynosić 0.
Layke
19
Jeśli potrzebujesz względnej liczby dni (ujemna, gdy $date1jest przednia $date2), użyj $diff = $date2->diff($date1)->format("%r%a");zamiast tego.
Socce,
1
W jakim formacie jest data DateTime("2010-07-06")?, Czy to Y-m-dlub Y-d-m?, Jaki jest format DateTimeparametru. który jest dzień
Tak, wydaje się to lepsza niż zaakceptowana odpowiedź, która w niektórych przypadkach nie działa. Jak: $ from = '2014-03-01'; $ do = „31.03.2014”;
MBozic
1
Zgadzam się, więc łatwe do naśladowania i działa świetnie !!! Tylko nie zapomnij użyć funkcji date_default_timezone_set (), ponieważ da to dziwne wyniki w oparciu o czas UTC.
zeckdude
2
Ta funkcja zakończyła się niepowodzeniem 0 z 14603 testów między 1980 a 2020.
LSerni,
128
Konwertuj daty na uniksowe znaczniki czasu, a następnie odejmij jedną od drugiej. To da ci różnicę w sekundach, którą dzielisz przez 86400 (ilość sekund w ciągu dnia), aby dać przybliżoną liczbę dni w tym zakresie.
Jeśli daty są w formacie 25.1.2010, 01/25/2010lub 2010-01-25możesz użyć strtotimefunkcji:
Za pomocą ceilzaokrągleń ilość dni do następnego pełnego dnia. Użyj floorzamiast tego, jeśli chcesz uzyskać liczbę pełnych dni między tymi dwiema datami.
Jeśli Twoje daty są już w formacie uniksowego znacznika czasu, możesz pominąć konwersję i po prostu wykonać $days_betweenczęść. W przypadku bardziej egzotycznych formatów dat może być konieczne wykonanie niestandardowej analizy, aby uzyskać właściwy wynik.
Pozwól mi rozwinąć: powiedzmy, że dziś rano o 3 nad ranem prawie cała Europa cofnęła zegar o godzinę. Oznacza to, że dzisiejszy dzień ma dodatkowe 3600 sekund, co powinno znaleźć odzwierciedlenie w znacznikach czasu UNIX. Jeśli tak, oznacza to, że dzisiaj będzie się liczyć przez dwa dni z powyższym sposobem obliczania liczby dni. I nawet nie zaczynam od sekund przestępnych, ponieważ ani PHP, ani UNIX nie wydają się uwzględniać tych (co jest w rzeczywistości zrozumiałe dla IMO). TL; DR: nie wszystkie dni trwają 86 400 sekund .
toon81
2
@ toon81 Nie ma jeszcze 3600 sekund dla tej daty. Nic nie dzieje się ze znacznikiem czasowym UNIX podczas podróży do lub z czasu letniego.
nickdnk,
1
Jeśli nic się nie dzieje ze znacznikiem czasu UNIX, to zgadzasz się ze mną: różnica między znacznikiem czasu UNIX o godzinie 13:00 $daya datownikiem UNIX o godzinie 13:00 $day+1nie zawsze wynosi 86400 sekund w strefach czasowych, w których obserwuje się czas letni. Może to być 23 lub 25 godzin sekund zamiast 24 godzin.
toon81
111
TL; DR czy nie używać znaczników czasu UNIX. Nie używaćtime() . Jeśli tak, przygotuj się, jeśli zawiodą cię niezawodność na poziomie 98,0825%. Użyj DateTime (lub Carbon).
Poprawną odpowiedzią jest ta podana przez Saksham Gupta (również inne odpowiedzi są poprawne):
/**
* Number of days between two dates.
*
* @param date $dt1 First date
* @param date $dt2 Second date
* @return int
*/function daysBetween($dt1, $dt2){return date_diff(
date_create($dt2),
date_create($dt1))->format('%a');}
Z zastrzeżeniem: „% a” wydaje się wskazywać bezwzględną liczbę dni. Jeśli chcesz, aby była to liczba całkowita ze znakiem, tzn. Ujemna, gdy druga data jest przed pierwszą, musisz użyć przedrostka „% r” (tj format('%r%a').).
Jeśli naprawdę musisz używać znaczników czasu UNIX, ustaw strefę czasową na GMT, aby uniknąć większości pułapek opisanych poniżej.
Długa odpowiedź: dlaczego dzielenie przez 24 * 60 * 60 (aka 86400) jest niebezpieczne
Większość odpowiedzi wykorzystujących znaczniki czasu UNIX (i 86400 do konwersji na dni) przyjmuje dwa założenia, które razem mogą prowadzić do scenariuszy z błędnymi wynikami i subtelnymi błędami, które mogą być trudne do śledzenia, i mogą pojawić się nawet dni, tygodnie lub miesiące później udane wdrożenie. To nie tak, że rozwiązanie nie działa - działa. Dzisiaj. Ale jutro może przestać działać.
Pierwszym błędem nie jest to, że zapytany „ile dni minęło od wczoraj?”, Komputer może zgodnie z prawdą odpowiedzieć na zero, jeśli między teraźniejszością a chwilą wskazaną przez „wczoraj” minął mniej niż jeden cały dzień .
Zwykle podczas konwertowania „dnia” na znacznik czasu UNIX uzyskuje się znacznik czasu o północy tego konkretnego dnia.
Tak więc między północą 1 października a 15 października upłynęło piętnaście dni. Ale między 13:00 1 października a 14:55 15 października upłynęło piętnaście dni minus 5 minut , a większość rozwiązań wykorzystujących floor()lub dokonujących niejawnej konwersji liczb całkowitych zgłosi jeden dzień mniej niż oczekiwano .
Więc „ile dni temu był Ymd H: i: s”? da złą odpowiedź .
Drugi błąd to zrównanie jednego dnia z 86400 sekundami. Jest to prawie zawsze prawda - zdarza się to dość często, aby przeoczyć czasy, których nie ma. Ale odległość w sekundach między dwiema kolejnymi północami z pewnością nie wynosi 86400 co najmniej dwa razy w roku, kiedy zaczyna się czas letni. Porównanie dwóch dat w granicach DST da złą odpowiedź.
Tak więc, nawet jeśli użyjesz „hackowania” wymuszania wszystkich znaczników czasu daty na ustaloną godzinę, powiedzmy północ (dzieje się to również pośrednio przez różne języki i struktury, gdy podajesz tylko dzień-miesiąc-rok, a nie godzinę-minutę-sekundę; tak samo dzieje się z typem DATE w bazach danych, takich jak MySQL), powszechnie stosowana formuła
zwróci, powiedzmy, 17, gdy DATA1 i DATA2 będą w tym samym segmencie czasu letniego; ale może zwrócić 17.042, a jeszcze gorzej 16.958. Użycie funkcji floor () lub jakiegokolwiek niejawnego skrótu do liczby całkowitej spowoduje następnie konwersję wartości 17 na 16. W innych okolicznościach wyrażenia takie jak „$ dni> 17” powrócą truedo 17.042, nawet jeśli oznacza to, że liczy się upływający dzień ma 18 lat
A sprawy stają się jeszcze brzydsze, ponieważ taki kod nie jest przenośny na różnych platformach, ponieważ niektóre z nich mogą mieć sekundę przestępną, a inne nie . Na tych platformach zrobienia , różnica pomiędzy dwiema datami nie będzie 86400 ale 86401 lub 86399. Może więc kod, który pracował w maju i faktycznie przeszedł wszystkie testy złamie obok czerwca, kiedy 12.99999 dni są uważane za 12 dni zamiast 13. dwiema datami który pracował w 2015 r., nie zadziała w 2017 r. - w tych samych datach i żaden rok nie jest rokiem przestępnym. Ale między 2018-03-01 a 2017-03-01, na tych platformach, które się przejmują, minie 366 dni zamiast 365, dzięki czemu 2018 będzie rokiem przestępnym (co nie jest).
Jeśli więc naprawdę chcesz używać znaczników czasu UNIX:
używaj round()funkcji mądrze, nie floor().
alternatywnie nie obliczaj różnic między D1-M1-RRR1 a D2-M2-RRR2. Te daty będą naprawdę uważane za D1-M1-RRR1 00:00:00 i D2-M2-RRR2 00:00:00. Zamiast tego dokonaj konwersji między D1-M1-RRR1 22:30:00 a D2-M2-RRR2 04:30:00. Będziesz zawsze uzyskać pozostałą około dwudziestu godzin. Może to być dwadzieścia jeden godzin lub dziewiętnaście, a może osiemnaście godzin, pięćdziesiąt dziewięć minut trzydzieści sześć sekund. Bez znaczenia. Jest to duży margines, który pozostanie tam i pozostanie pozytywny w dającej się przewidzieć przyszłości. Teraz możesz go floor()bezpiecznie obciąć .
Poprawne rozwiązanie jednak, aby uniknąć magicznych stałych, zaokrąglając kludges i dług konserwacyjnych, jest
użyj biblioteki czasu (Datetime, Carbon, cokolwiek); nie rzucaj własnym
pisz obszerne przypadki testowe, używając naprawdę złych wyborów dat - poza granicami czasu letniego, lat przestępnych, sekund przestępnych itd., a także zwykłych dat. Idealnie (połączenia z datetime są szybkie !) Generują daty z całych czterech lat (i jednego dnia), łącząc je kolejno z ciągów znaków i zapewniają, że różnica między pierwszym dniem a testowanym dniem stale rośnie o jeden. Zapewni to, że jeśli cokolwiek zmieni się w procedurach niskiego poziomu i poprawki sekund przestępnych, sieją spustoszenie, przynajmniej będziesz wiedział .
uruchamiaj te testy regularnie wraz z resztą zestawu testów. Są kwestią milisekund i mogą zaoszczędzić dosłownie godziny drapania głowy.
Niezależnie od wybranego rozwiązania, przetestuj je!
Poniższa funkcja funcdiffimplementuje jedno z rozwiązań (tak się składa, przyjęte) w scenariuszu z prawdziwego świata.
<?php
$tz ='Europe/Rome';
$yearFrom =1980;
$yearTo =2020;
$verbose =false;function funcdiff($date2, $date1){
$now = strtotime($date2);
$your_date = strtotime($date1);
$datediff = $now - $your_date;return floor($datediff /(60*60*24));}########################################
date_default_timezone_set($tz);
$failures =0;
$tests =0;
$dom = array (0,31,28,31,30,31,30,31,31,30,31,30,31);(array_sum($dom)===365)||die("Thirty days hath September...");
$last = array();for($year = $yearFrom; $year < $yearTo; $year++){
$dom[2]=28;// Apply leap year rules.if($year %4===0){ $dom[2]=29;}if($year %100===0){ $dom[2]=28;}if($year %400===0){ $dom[2]=29;}for($month =1; $month <=12; $month ++){for($day =1; $day <= $dom[$month]; $day++){
$date = sprintf("%04d-%02d-%02d", $year, $month, $day);if(count($last)===7){
$tests ++;
$diff = funcdiff($date, $test = array_shift($last));if((double)$diff !==(double)7){
$failures ++;if($verbose){print"There seem to be {$diff} days between {$date} and {$test}\n";}}}
$last[]= $date;}}}print"This function failed {$failures} of its {$tests} tests between {$yearFrom} and {$yearTo}.\n";
Wynik to,
Thisfunction failed 280of its 14603 tests
Horror Story: koszt „oszczędności czasu”
To się wydarzyło kilka miesięcy temu. Pomysłowy programista postanowił zaoszczędzić kilka mikrosekund na obliczeniach, które zajęły najwyżej około trzydzieści sekund, podłączając niesławny kod „(MidnightOfDateB-MidnightOfDateA) / 86400” w kilku miejscach. Optymalizacja była tak oczywista, że nawet jej nie udokumentował, a optymalizacja przeszła testy integracyjne i czaiła się w kodzie przez kilka miesięcy, wszystkie niezauważone.
Stało się tak w programie, który oblicza płace dla kilku najlepiej sprzedających się sprzedawców, z których co najmniej ma o wiele straszniejszą siłę przebicia niż cały skromny pięcioosobowy zespół programistów razem wzięty. Pewnego dnia kilka miesięcy temu, z powodów, które nie mają znaczenia, błąd uderzył - i niektórzy z tych facetów stracili jeden dzień grubych prowizji. Na pewno nie byli rozbawieni.
Nieskończenie gorzej, stracili (już bardzo niewielką) wiarę w program, który nie został zaprojektowany do ukrycia ich, i udawali - i uzyskali - pełną, szczegółową recenzję kodu z uruchomionymi przypadkami testowymi i komentowali w kategoriach laika (plus wiele leczenia czerwonego dywanu w kolejnych tygodniach).
Co mogę powiedzieć: na plus, pozbyliśmy się dużego technicznego długu i byliśmy w stanie przepisać i przeredagować kilka kawałków bałaganu spaghetti, który wrócił do inwazji COBOL w latach 90. Program niewątpliwie działa teraz lepiej i jest o wiele więcej informacji do debugowania, aby szybko wyzerować, gdy coś wygląda podejrzanie. Szacuję, że tylko ta ostatnia rzecz pozwoli zaoszczędzić jeden lub dwa osobodni miesięcznie na dającą się przewidzieć przyszłość.
Z drugiej strony, cała brouhaha kosztowała firmę około 200 000 euro z góry - plus twarz, a także niewątpliwie pewną siłę przetargową (a tym samym jeszcze więcej pieniędzy).
Facet odpowiedzialny za „optymalizację” przed rokiem zmienił pracę przed katastrofą, ale wciąż było o co prosić go o odszkodowanie. A w wyższych sferach nie szło dobrze, że była to „wina ostatniego faceta” - wyglądało to jak ustawienie dla nas, abyśmy rozwiązali sprawę, a ostatecznie wciąż jesteśmy w niełasce i jeden z zespołów planuje odejść.
Dziewięćdziesiąt dziewięć razy na sto „hack 86400” będzie działał bezbłędnie. (Na przykład w PHP strtotime()zignoruje czas letni i zgłosi, że między północą ostatniej soboty października a tą w następny poniedziałek minęły dokładnie 2 * 24 * 60 * 60 sekund, nawet jeśli to po prostu nieprawda ... a dwie krzywdy na szczęście sprawią, że jedna będzie dobra).
Panie i panowie! To był jeden przypadek, kiedy tak się nie stało. Podobnie jak w przypadku poduszek powietrznych i pasów bezpieczeństwa, być może nigdy naprawdę nie potrzebujesz złożoności (i łatwości użytkowania) DateTimelub Carbon. Ale dni, kiedy może (lub dzień, kiedy będziesz musiał udowodnić, myślałeś o tym) przyjdzie jak złodziej w nocy. Być przygotowanym.
Kiedy zobaczyłem te odpowiedzi, pomyślałem, że jestem głupi i szalony, ponieważ zawsze używam DateTime, ale dałeś mi do zrozumienia, że użycie DateTime jest właściwym sposobem. Dziękuję
Syncro,
Hej, wszyscy tutaj, na SO, przybyli tutaj, szukając w google days between two days in phplub podobnym, tylko dlatego, że życie jest zbyt krótkie, aby napisać wszystko samemu.
Denis Matafonov
1
szczegółowe wyjaśnienie. + 1
Anant Singh --- Alive to Die
1
Czasami chciałbym, abyśmy mogli faworyzować odpowiedzi zamiast pytania.
Jest to dobry sposób proceduralny, aby to zrobić za pomocą klasy DateTime. Brakuje tylko wyraźnego użycia obiektu interwału ( $diffw tym przypadku zmiennej). Coś w stylu if ($diff->days > 30) { [doyourstuff]; }
Bardzo stary komentarz powyżej, ale jest niepoprawny. StackOverflow nie pozwalają odpowiedzieć na swoje pytanie (nawet jeśli zadać swoje pytanie). Samo udzielenie odpowiedzi po tym, jak ktoś już opublikował to samo rozwiązanie, jest jednak uważane za niegrzeczne.
Maarten Bodewes
Ta funkcja zakończyła się niepowodzeniem w 560 testach 14603 w latach
1980–2020
10
Cóż, wybrana odpowiedź nie jest najbardziej poprawna, ponieważ nie powiedzie się poza UTC. W zależności od strefy czasowej ( lista ) mogą istnieć korekty czasu, które tworzą dni „bez” 24 godzin, co spowoduje, że obliczenia (60 * 60 * 24) zakończą się niepowodzeniem.
Oto przykład:
date_default_timezone_set('europe/lisbon');
$time1 = strtotime('2016-03-27');
$time2 = strtotime('2016-03-29');
echo floor(($time2-$time1)/(60*60*24));^-- the output will be **1**
Tak więc poprawnym rozwiązaniem będzie użycie DateTime
date_default_timezone_set('europe/lisbon');
$date1 =newDateTime("2016-03-27");
$date2 =newDateTime("2016-03-29");
echo $date2->diff($date1)->format("%a");^-- the output will be **2**
function dateDiff($date1, $date2)//days find function{
$diff = strtotime($date2)- strtotime($date1);return abs(round($diff /86400));}//start day
$date1 ="11-10-2018";// end day
$date2 ="31-10-2018";// call the days find fun store to variable
$dateDiff = dateDiff($date1, $date2);
echo "Difference between two dates: ". $dateDiff ." Days ";
W przypadku starszych pytań z istniejącymi odpowiedziami warto wyjaśnić, jaki nowy aspekt ma odpowiedź na pytanie. W razie potrzeby przydatne jest również potwierdzenie wszelkich zmian, które mogły nastąpić od momentu zadania pytania.
// Change this to the day in the future
$day =15;// Change this to the month in the future
$month =11;// Change this to the year in the future
$year =2012;// $days is the number of days between now and the date in the future
$days =(int)((mktime (0,0,0,$month,$day,$year)- time(void))/86400);
echo "There are $days days until $day/$month/$year";
Witamy w SO! Kiedy odpowiadasz na post za pomocą samego kodu, wyjaśnij to trochę. Kod, który przyniesiesz, jest obliczany między 2019-11-10 a 2019-11-25. Więc to nie odpowiada na pytanie. Dlatego lepiej jest wyjaśnić POV lepiej niż dać się ponieść emocjom.
David García Bodego
0
Jeśli używasz MySql
function daysSince($date, $date2){
$q ="SELECT DATEDIFF('$date','$date2') AS days;";
$result = execQ($q);
$row = mysql_fetch_array($result,MYSQL_BOTH);return($row[0]);
Ogólnie używam „DateTime”, aby znaleźć dni między 2 datami. Ale jeśli z jakiegoś powodu niektóre konfiguracje serwera nie mają włączonej funkcji „DateTime”, zastosuje proste (ale nie bezpieczne) obliczenia z funkcją „strtotime ()”.
(new DateTime("2010-01-11"))->diff(new DateTime("2019-08-19"))->days;
Odpowiedzi:
źródło
$your_date-$now
, jeśli chcesz, aby przyszła data zwróciła dodatnią liczbę całkowitą.Jeśli używasz
PHP 5.3 >
, jest to zdecydowanie najdokładniejszy sposób obliczenia różnicy:źródło
$date1
jest przednia$date2
), użyj$diff = $date2->diff($date1)->format("%r%a");
zamiast tego.DateTime("2010-07-06")
?, Czy toY-m-d
lubY-d-m
?, Jaki jest formatDateTime
parametru. który jest dzieńOd wersji PHP 5.3 i nowszych dodano nowe funkcje daty / godziny, aby uzyskać różnicę:
Wynik jak poniżej:
Mam nadzieję, że to pomoże !
źródło
Konwertuj daty na uniksowe znaczniki czasu, a następnie odejmij jedną od drugiej. To da ci różnicę w sekundach, którą dzielisz przez 86400 (ilość sekund w ciągu dnia), aby dać przybliżoną liczbę dni w tym zakresie.
Jeśli daty są w formacie
25.1.2010
,01/25/2010
lub2010-01-25
możesz użyćstrtotime
funkcji:Za pomocą
ceil
zaokrągleń ilość dni do następnego pełnego dnia. Użyjfloor
zamiast tego, jeśli chcesz uzyskać liczbę pełnych dni między tymi dwiema datami.Jeśli Twoje daty są już w formacie uniksowego znacznika czasu, możesz pominąć konwersję i po prostu wykonać
$days_between
część. W przypadku bardziej egzotycznych formatów dat może być konieczne wykonanie niestandardowej analizy, aby uzyskać właściwy wynik.źródło
$day
a datownikiem UNIX o godzinie 13:00$day+1
nie zawsze wynosi 86400 sekund w strefach czasowych, w których obserwuje się czas letni. Może to być 23 lub 25 godzin sekund zamiast 24 godzin.TL; DR czy nie używać znaczników czasu UNIX. Nie używać
time()
. Jeśli tak, przygotuj się, jeśli zawiodą cię niezawodność na poziomie 98,0825%. Użyj DateTime (lub Carbon).Poprawną odpowiedzią jest ta podana przez Saksham Gupta (również inne odpowiedzi są poprawne):
Lub proceduralnie jako jedna linijka:
Z zastrzeżeniem: „% a” wydaje się wskazywać bezwzględną liczbę dni. Jeśli chcesz, aby była to liczba całkowita ze znakiem, tzn. Ujemna, gdy druga data jest przed pierwszą, musisz użyć przedrostka „% r” (tj
format('%r%a')
.).Jeśli naprawdę musisz używać znaczników czasu UNIX, ustaw strefę czasową na GMT, aby uniknąć większości pułapek opisanych poniżej.
Długa odpowiedź: dlaczego dzielenie przez 24 * 60 * 60 (aka 86400) jest niebezpieczne
Większość odpowiedzi wykorzystujących znaczniki czasu UNIX (i 86400 do konwersji na dni) przyjmuje dwa założenia, które razem mogą prowadzić do scenariuszy z błędnymi wynikami i subtelnymi błędami, które mogą być trudne do śledzenia, i mogą pojawić się nawet dni, tygodnie lub miesiące później udane wdrożenie. To nie tak, że rozwiązanie nie działa - działa. Dzisiaj. Ale jutro może przestać działać.
Pierwszym błędem nie jest to, że zapytany „ile dni minęło od wczoraj?”, Komputer może zgodnie z prawdą odpowiedzieć na zero, jeśli między teraźniejszością a chwilą wskazaną przez „wczoraj” minął mniej niż jeden cały dzień .
Zwykle podczas konwertowania „dnia” na znacznik czasu UNIX uzyskuje się znacznik czasu o północy tego konkretnego dnia.
Tak więc między północą 1 października a 15 października upłynęło piętnaście dni. Ale między 13:00 1 października a 14:55 15 października upłynęło piętnaście dni minus 5 minut , a większość rozwiązań wykorzystujących
floor()
lub dokonujących niejawnej konwersji liczb całkowitych zgłosi jeden dzień mniej niż oczekiwano .Więc „ile dni temu był Ymd H: i: s”? da złą odpowiedź .
Drugi błąd to zrównanie jednego dnia z 86400 sekundami. Jest to prawie zawsze prawda - zdarza się to dość często, aby przeoczyć czasy, których nie ma. Ale odległość w sekundach między dwiema kolejnymi północami z pewnością nie wynosi 86400 co najmniej dwa razy w roku, kiedy zaczyna się czas letni. Porównanie dwóch dat w granicach DST da złą odpowiedź.
Tak więc, nawet jeśli użyjesz „hackowania” wymuszania wszystkich znaczników czasu daty na ustaloną godzinę, powiedzmy północ (dzieje się to również pośrednio przez różne języki i struktury, gdy podajesz tylko dzień-miesiąc-rok, a nie godzinę-minutę-sekundę; tak samo dzieje się z typem DATE w bazach danych, takich jak MySQL), powszechnie stosowana formuła
lub
zwróci, powiedzmy, 17, gdy DATA1 i DATA2 będą w tym samym segmencie czasu letniego; ale może zwrócić 17.042, a jeszcze gorzej 16.958. Użycie funkcji floor () lub jakiegokolwiek niejawnego skrótu do liczby całkowitej spowoduje następnie konwersję wartości 17 na 16. W innych okolicznościach wyrażenia takie jak „$ dni> 17” powrócą
true
do 17.042, nawet jeśli oznacza to, że liczy się upływający dzień ma 18 latA sprawy stają się jeszcze brzydsze, ponieważ taki kod nie jest przenośny na różnych platformach, ponieważ niektóre z nich mogą mieć sekundę przestępną, a inne nie . Na tych platformach zrobienia , różnica pomiędzy dwiema datami nie będzie 86400 ale 86401 lub 86399. Może więc kod, który pracował w maju i faktycznie przeszedł wszystkie testy złamie obok czerwca, kiedy 12.99999 dni są uważane za 12 dni zamiast 13. dwiema datami który pracował w 2015 r., nie zadziała w 2017 r. - w tych samych datach i żaden rok nie jest rokiem przestępnym. Ale między 2018-03-01 a 2017-03-01, na tych platformach, które się przejmują, minie 366 dni zamiast 365, dzięki czemu 2018 będzie rokiem przestępnym (co nie jest).
Jeśli więc naprawdę chcesz używać znaczników czasu UNIX:
używaj
round()
funkcji mądrze, niefloor()
.alternatywnie nie obliczaj różnic między D1-M1-RRR1 a D2-M2-RRR2. Te daty będą naprawdę uważane za D1-M1-RRR1 00:00:00 i D2-M2-RRR2 00:00:00. Zamiast tego dokonaj konwersji między D1-M1-RRR1 22:30:00 a D2-M2-RRR2 04:30:00. Będziesz zawsze uzyskać pozostałą około dwudziestu godzin. Może to być dwadzieścia jeden godzin lub dziewiętnaście, a może osiemnaście godzin, pięćdziesiąt dziewięć minut trzydzieści sześć sekund. Bez znaczenia. Jest to duży margines, który pozostanie tam i pozostanie pozytywny w dającej się przewidzieć przyszłości. Teraz możesz go
floor()
bezpiecznie obciąć .Poprawne rozwiązanie jednak, aby uniknąć magicznych stałych, zaokrąglając kludges i dług konserwacyjnych, jest
użyj biblioteki czasu (Datetime, Carbon, cokolwiek); nie rzucaj własnym
pisz obszerne przypadki testowe, używając naprawdę złych wyborów dat - poza granicami czasu letniego, lat przestępnych, sekund przestępnych itd., a także zwykłych dat. Idealnie (połączenia z datetime są szybkie !) Generują daty z całych czterech lat (i jednego dnia), łącząc je kolejno z ciągów znaków i zapewniają, że różnica między pierwszym dniem a testowanym dniem stale rośnie o jeden. Zapewni to, że jeśli cokolwiek zmieni się w procedurach niskiego poziomu i poprawki sekund przestępnych, sieją spustoszenie, przynajmniej będziesz wiedział .
uruchamiaj te testy regularnie wraz z resztą zestawu testów. Są kwestią milisekund i mogą zaoszczędzić dosłownie godziny drapania głowy.
Niezależnie od wybranego rozwiązania, przetestuj je!
Poniższa funkcja
funcdiff
implementuje jedno z rozwiązań (tak się składa, przyjęte) w scenariuszu z prawdziwego świata.Wynik to,
Horror Story: koszt „oszczędności czasu”
To się wydarzyło kilka miesięcy temu. Pomysłowy programista postanowił zaoszczędzić kilka mikrosekund na obliczeniach, które zajęły najwyżej około trzydzieści sekund, podłączając niesławny kod „(MidnightOfDateB-MidnightOfDateA) / 86400” w kilku miejscach. Optymalizacja była tak oczywista, że nawet jej nie udokumentował, a optymalizacja przeszła testy integracyjne i czaiła się w kodzie przez kilka miesięcy, wszystkie niezauważone.
Stało się tak w programie, który oblicza płace dla kilku najlepiej sprzedających się sprzedawców, z których co najmniej ma o wiele straszniejszą siłę przebicia niż cały skromny pięcioosobowy zespół programistów razem wzięty. Pewnego dnia kilka miesięcy temu, z powodów, które nie mają znaczenia, błąd uderzył - i niektórzy z tych facetów stracili jeden dzień grubych prowizji. Na pewno nie byli rozbawieni.
Nieskończenie gorzej, stracili (już bardzo niewielką) wiarę w program, który nie został zaprojektowany do ukrycia ich, i udawali - i uzyskali - pełną, szczegółową recenzję kodu z uruchomionymi przypadkami testowymi i komentowali w kategoriach laika (plus wiele leczenia czerwonego dywanu w kolejnych tygodniach).
Co mogę powiedzieć: na plus, pozbyliśmy się dużego technicznego długu i byliśmy w stanie przepisać i przeredagować kilka kawałków bałaganu spaghetti, który wrócił do inwazji COBOL w latach 90. Program niewątpliwie działa teraz lepiej i jest o wiele więcej informacji do debugowania, aby szybko wyzerować, gdy coś wygląda podejrzanie. Szacuję, że tylko ta ostatnia rzecz pozwoli zaoszczędzić jeden lub dwa osobodni miesięcznie na dającą się przewidzieć przyszłość.
Z drugiej strony, cała brouhaha kosztowała firmę około 200 000 euro z góry - plus twarz, a także niewątpliwie pewną siłę przetargową (a tym samym jeszcze więcej pieniędzy).
Facet odpowiedzialny za „optymalizację” przed rokiem zmienił pracę przed katastrofą, ale wciąż było o co prosić go o odszkodowanie. A w wyższych sferach nie szło dobrze, że była to „wina ostatniego faceta” - wyglądało to jak ustawienie dla nas, abyśmy rozwiązali sprawę, a ostatecznie wciąż jesteśmy w niełasce i jeden z zespołów planuje odejść.
Dziewięćdziesiąt dziewięć razy na sto „hack 86400” będzie działał bezbłędnie. (Na przykład w PHP
strtotime()
zignoruje czas letni i zgłosi, że między północą ostatniej soboty października a tą w następny poniedziałek minęły dokładnie 2 * 24 * 60 * 60 sekund, nawet jeśli to po prostu nieprawda ... a dwie krzywdy na szczęście sprawią, że jedna będzie dobra).Panie i panowie! To był jeden przypadek, kiedy tak się nie stało. Podobnie jak w przypadku poduszek powietrznych i pasów bezpieczeństwa, być może nigdy naprawdę nie potrzebujesz złożoności (i łatwości użytkowania)
DateTime
lubCarbon
. Ale dni, kiedy może (lub dzień, kiedy będziesz musiał udowodnić, myślałeś o tym) przyjdzie jak złodziej w nocy. Być przygotowanym.źródło
days between two days in php
lub podobnym, tylko dlatego, że życie jest zbyt krótkie, aby napisać wszystko samemu.Łatwy w użyciu date_diff
źródło
$diff
w tym przypadku zmiennej). Coś w styluif ($diff->days > 30) { [doyourstuff]; }
Styl obiektowy:
Styl proceduralny:
źródło
Użyłem tego :)
Teraz działa
źródło
Cóż, wybrana odpowiedź nie jest najbardziej poprawna, ponieważ nie powiedzie się poza UTC. W zależności od strefy czasowej ( lista ) mogą istnieć korekty czasu, które tworzą dni „bez” 24 godzin, co spowoduje, że obliczenia (60 * 60 * 24) zakończą się niepowodzeniem.
Oto przykład:
Tak więc poprawnym rozwiązaniem będzie użycie DateTime
źródło
Oblicz różnicę między dwiema datami:
Wyjście: +272 dni
Funkcja date_diff () zwraca różnicę między dwoma obiektami DateTime.
źródło
źródło
Używam Carbon w moich projektach kompozytorskich do tego i podobnych celów.
Byłoby to tak proste:
źródło
Daty można znaleźć po prostu według
źródło
oraz w razie potrzeby:
źródło
Jeśli masz czasy w sekundach (znacznik czasu IE unix), możesz po prostu odjąć czasy i podzielić przez 86400 (sekund dziennie)
źródło
źródło
Możesz wypróbować poniższy kod:
źródło
źródło
Jeśli chcesz powtarzać wszystkie dni między datą rozpoczęcia i zakończenia, wymyśliłem:
źródło
Najłatwiejszy sposób na znalezienie różnicy dni między dwiema datami
źródło
źródło
Oto moja ulepszona wersja, która pokazuje 1 rok (lata) 2 miesiąc (y) 25 dni, jeśli drugi parametr został zaliczony.
źródło
źródło
użyłem powyższego kodu bardzo prosto. Dzięki.
źródło
źródło
Korzystanie z tej prostej funkcji. Funkcja deklaracji
i wywoływaj tę funkcję tak, jak chcesz
źródło
źródło
Jeśli używasz MySql
}
}
źródło
Spróbuj użyć węgla
Możesz także użyć
aby utworzyć obiekt daty Carbon przy użyciu podanego ciągu znacznika czasu.
źródło
Szukając wszystkich odpowiedzi piszę uniwersalną funkcję, która działa na wszystkich wersjach PHP.
Ogólnie używam „DateTime”, aby znaleźć dni między 2 datami. Ale jeśli z jakiegoś powodu niektóre konfiguracje serwera nie mają włączonej funkcji „DateTime”, zastosuje proste (ale nie bezpieczne) obliczenia z funkcją „strtotime ()”.
źródło