Jak obliczyć różnicę między dwiema datami za pomocą PHP?

722

Mam dwie daty formularza:

Start Date: 2007-03-24 
End Date: 2009-06-26

Teraz muszę znaleźć różnicę między tymi dwoma w następującej formie:

2 years, 3 months and 2 days

Jak mogę to zrobić w PHP?

Taryn
źródło
1
2 lata 94 dni. Obliczanie miesięcy z uwzględnieniem lat przestępnych byłoby problematyczne. Jak dokładne to musi być?
dbasnett
możliwy duplikat Jak obliczyć czas względny?
Cole Johnson

Odpowiedzi:

526

Użyj tego dla starszego kodu (PHP <5.3). Aktualne rozwiązanie znajduje się w odpowiedzi jurki poniżej

Możesz użyć strtotime (), aby przekonwertować dwie daty na czas uniksowy, a następnie obliczyć liczbę sekund między nimi. Na tej podstawie łatwo jest obliczyć różne okresy.

$date1 = "2007-03-24";
$date2 = "2009-06-26";

$diff = abs(strtotime($date2) - strtotime($date1));

$years = floor($diff / (365*60*60*24));
$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));

printf("%d years, %d months, %d days\n", $years, $months, $days);

Edycja: Oczywiście preferowanym sposobem na zrobienie tego jest opisany poniżej przez jurka. Mój kod jest ogólnie zalecany tylko wtedy, gdy nie masz PHP 5.3 lub nowszego.

Kilka osób w komentarzach zauważyło, że powyższy kod jest jedynie przybliżeniem. Nadal uważam, że dla większości celów jest to w porządku, ponieważ użycie zakresu służy raczej do określenia, ile czasu minęło lub pozostało, niż do zapewnienia precyzji - jeśli chcesz to zrobić, po prostu podaj datę.

Mimo to postanowiłem zająć się skargami. Jeśli naprawdę potrzebujesz dokładnego zakresu, ale nie masz dostępu do PHP 5.3, użyj poniższego kodu (powinien również działać w PHP 4). Jest to bezpośredni port kodu, którego PHP używa wewnętrznie do obliczania zakresów, z tym wyjątkiem, że nie bierze pod uwagę czasu letniego. Oznacza to, że jest wyłączony najwyżej o godzinę, ale poza tym powinien być poprawny.

<?php

/**
 * Calculate differences between two dates with precise semantics. Based on PHPs DateTime::diff()
 * implementation by Derick Rethans. Ported to PHP by Emil H, 2011-05-02. No rights reserved.
 * 
 * See here for original code:
 * http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/tm2unixtime.c?revision=302890&view=markup
 * http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/interval.c?revision=298973&view=markup
 */

function _date_range_limit($start, $end, $adj, $a, $b, $result)
{
    if ($result[$a] < $start) {
        $result[$b] -= intval(($start - $result[$a] - 1) / $adj) + 1;
        $result[$a] += $adj * intval(($start - $result[$a] - 1) / $adj + 1);
    }

    if ($result[$a] >= $end) {
        $result[$b] += intval($result[$a] / $adj);
        $result[$a] -= $adj * intval($result[$a] / $adj);
    }

    return $result;
}

function _date_range_limit_days($base, $result)
{
    $days_in_month_leap = array(31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
    $days_in_month = array(31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

    _date_range_limit(1, 13, 12, "m", "y", &$base);

    $year = $base["y"];
    $month = $base["m"];

    if (!$result["invert"]) {
        while ($result["d"] < 0) {
            $month--;
            if ($month < 1) {
                $month += 12;
                $year--;
            }

            $leapyear = $year % 400 == 0 || ($year % 100 != 0 && $year % 4 == 0);
            $days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];

            $result["d"] += $days;
            $result["m"]--;
        }
    } else {
        while ($result["d"] < 0) {
            $leapyear = $year % 400 == 0 || ($year % 100 != 0 && $year % 4 == 0);
            $days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];

            $result["d"] += $days;
            $result["m"]--;

            $month++;
            if ($month > 12) {
                $month -= 12;
                $year++;
            }
        }
    }

    return $result;
}

function _date_normalize($base, $result)
{
    $result = _date_range_limit(0, 60, 60, "s", "i", $result);
    $result = _date_range_limit(0, 60, 60, "i", "h", $result);
    $result = _date_range_limit(0, 24, 24, "h", "d", $result);
    $result = _date_range_limit(0, 12, 12, "m", "y", $result);

    $result = _date_range_limit_days(&$base, &$result);

    $result = _date_range_limit(0, 12, 12, "m", "y", $result);

    return $result;
}

/**
 * Accepts two unix timestamps.
 */
function _date_diff($one, $two)
{
    $invert = false;
    if ($one > $two) {
        list($one, $two) = array($two, $one);
        $invert = true;
    }

    $key = array("y", "m", "d", "h", "i", "s");
    $a = array_combine($key, array_map("intval", explode(" ", date("Y m d H i s", $one))));
    $b = array_combine($key, array_map("intval", explode(" ", date("Y m d H i s", $two))));

    $result = array();
    $result["y"] = $b["y"] - $a["y"];
    $result["m"] = $b["m"] - $a["m"];
    $result["d"] = $b["d"] - $a["d"];
    $result["h"] = $b["h"] - $a["h"];
    $result["i"] = $b["i"] - $a["i"];
    $result["s"] = $b["s"] - $a["s"];
    $result["invert"] = $invert ? 1 : 0;
    $result["days"] = intval(abs(($one - $two)/86400));

    if ($invert) {
        _date_normalize(&$a, &$result);
    } else {
        _date_normalize(&$b, &$result);
    }

    return $result;
}

$date = "1986-11-10 19:37:22";

print_r(_date_diff(strtotime($date), time()));
print_r(_date_diff(time(), strtotime($date)));
Emil H.
źródło
1
Jeśli używasz klasy DateTime, możesz wybrać $ date-> format ('U'), aby uzyskać uniksowy znacznik czasu.
Jon Cram,
4
To nieprawda, jeśli masz do czynienia z czasem letnim / zimowym. W tym konkretnym przypadku, gdy dostosowujesz czas letni / zimowy, jeden dzień to 23 lub 25 godzin.
Arno,
4
Cóż, ten sam argument można wysunąć na lata przestępne. Nie bierze tego pod uwagę. Mimo to nie jestem przekonany, czy w ogóle chcesz to wziąć pod uwagę, ponieważ omawiamy tutaj zakres. Semantyka dla zakresu jest nieco inna niż dla daty bezwzględnej.
Emil H
9
Ta funkcja jest nieprawidłowa. Jest to dobre dla przybliżenia, ale niepoprawne dla dokładnych zakresów. Po pierwsze, zakłada się, że jest 30 dni w miesiącu, co oznacza, że ​​będzie miała tę samą różnicę dni między 1 lutego a 1 marca, jak w przypadku 1 lipca do 1 sierpnia (niezależnie od roku przestępnego).
enobrev
1
W PHP zmienne referencyjne znajdują się w podpisie funkcji, a nie w wywołaniu. Przenieś wszystko &do podpisów.
Paul Tarjan
909

Sugeruję użycie obiektów DateTime i DateInterval.

$date1 = new DateTime("2007-03-24");
$date2 = new DateTime("2009-06-26");
$interval = $date1->diff($date2);
echo "difference " . $interval->y . " years, " . $interval->m." months, ".$interval->d." days "; 

// shows the total amount of days (not divided into years, months and days like above)
echo "difference " . $interval->days . " days ";

czytaj więcej php DateTime :: diff manual

Z instrukcji:

Począwszy od PHP 5.2.2, obiekty DateTime można porównywać za pomocą operatorów porównania.

$date1 = new DateTime("now");
$date2 = new DateTime("tomorrow");

var_dump($date1 == $date2); // bool(false)
var_dump($date1 < $date2);  // bool(true)
var_dump($date1 > $date2);  // bool(false)
jurka
źródło
14
+1 DateTime poprawnie obsługuje lata przestępne i strefy czasowe, a na półce jest dobra książka: phparch.com/books/…
hakre
3
Czy istnieje metoda, która podaje całkowitą liczbę sekund między dwoma DateTimes? (bez dodawania składników, to znaczy)
potatoe
1
@Panique $ przedział-> dni i $ przedział-> d to różne miary. Twój komentarz powyżej jest słuszny „pokazuje całkowitą liczbę dni (
niepodzieloną
1
@potatoe Prawdopodobnie chcesz $date2->format('U') - $date1->format('U').
Paulo Freitas,
3
zauważ, że występuje błąd, w którym DateInterval ma niepoprawną właściwość dni ( zawsze 6015 ) w systemie Windows w niektórych wersjach PHP: bugs.php.net/bug.php?id=51184 (zapoznaj się z komentarzami w celu naprawy / obejścia)
Pim Schaaf
73

Najlepszym sposobem działania jest użycie obiektów PHP DateTime(i DateInterval). Każda data jest kapsułkowana w DateTimeobiekcie, a następnie można wprowadzić różnicę między nimi:

$first_date = new DateTime("2012-11-30 17:03:30");
$second_date = new DateTime("2012-12-21 00:00:00");

DateTimeObiekt zaakceptuje każdy format strtotime()będzie. Jeśli potrzebny jest jeszcze bardziej szczegółowy format daty, DateTime::createFromFormat()można go użyć do utworzenia DateTimeobiektu.

Po utworzeniu instancji obu obiektów odejmujesz jeden od drugiego za pomocą DateTime::diff().

$difference = $first_date->diff($second_date);

$differenceteraz zawiera DateIntervalobiekt z informacją o różnicy. A var_dump()wygląda to tak:

object(DateInterval)
  public 'y' => int 0
  public 'm' => int 0
  public 'd' => int 20
  public 'h' => int 6
  public 'i' => int 56
  public 's' => int 30
  public 'invert' => int 0
  public 'days' => int 20

Aby sformatować DateIntervalobiekt, musimy sprawdzić każdą wartość i wykluczyć ją, jeśli wynosi 0:

/**
 * Format an interval to show all existing components.
 * If the interval doesn't have a time component (years, months, etc)
 * That component won't be displayed.
 *
 * @param DateInterval $interval The interval
 *
 * @return string Formatted interval string.
 */
function format_interval(DateInterval $interval) {
    $result = "";
    if ($interval->y) { $result .= $interval->format("%y years "); }
    if ($interval->m) { $result .= $interval->format("%m months "); }
    if ($interval->d) { $result .= $interval->format("%d days "); }
    if ($interval->h) { $result .= $interval->format("%h hours "); }
    if ($interval->i) { $result .= $interval->format("%i minutes "); }
    if ($interval->s) { $result .= $interval->format("%s seconds "); }

    return $result;
}

Teraz pozostaje tylko wywołać naszą funkcję na $difference DateIntervalobiekcie:

echo format_interval($difference);

I otrzymujemy poprawny wynik:

20 dni 6 godzin 56 minut 30 sekund

Pełny kod użyty do osiągnięcia celu:

/**
 * Format an interval to show all existing components.
 * If the interval doesn't have a time component (years, months, etc)
 * That component won't be displayed.
 *
 * @param DateInterval $interval The interval
 *
 * @return string Formatted interval string.
 */
function format_interval(DateInterval $interval) {
    $result = "";
    if ($interval->y) { $result .= $interval->format("%y years "); }
    if ($interval->m) { $result .= $interval->format("%m months "); }
    if ($interval->d) { $result .= $interval->format("%d days "); }
    if ($interval->h) { $result .= $interval->format("%h hours "); }
    if ($interval->i) { $result .= $interval->format("%i minutes "); }
    if ($interval->s) { $result .= $interval->format("%s seconds "); }

    return $result;
}

$first_date = new DateTime("2012-11-30 17:03:30");
$second_date = new DateTime("2012-12-21 00:00:00");

$difference = $first_date->diff($second_date);

echo format_interval($difference);
Duch Madary
źródło
DateTime()nie jest funkcją, jest obiektem i istnieje od PHP 5.2. Upewnij się, że Twój serwer to obsługuje.
Madara's Ghost
2
@SecondRikudo DateTime :: Diff need PHP 5.3.0
PhoneixS
Mamy problem, wymieniamy first_date na second_date i otrzymujemy ten sam wynik? Dlaczego nie powiesz 0 dni 0 godzin 0 minut 0 sekund lub tylko 0. Przykład: 2012-11-30 17:03:30 - 2012-12-21 00:00:00 i 2012-12-21 00:00:00 - 30.11.2012 17:03:30 uzyskaj ten sam wynik.
EgoistDeveloper
Ponieważ diff daje różnicę między tymi dwoma czasami. Różnica nie wynosi 0, niezależnie od tego, która data nastąpi później.
Duch Madary
1
To naprawdę dobra odpowiedź, ponieważ zapewnia wyraźną funkcję, którą można wywołać z dowolnego miejsca w bazie kodu bez dużej ilości czasu. Inne odpowiedzi pozwalają upuszczać cielęta w locie, które odnoszą się do objawów, a nie rozwiązują problem ... Jedynym elementem, który dodałem (i prawie wszystkie inne posty tego nie obejmują) jest pluralizacja elementów z przedziałami $ jeśli więcej niż 1.
nickhar
33

Zobacz godziny, minuty i sekundy ..

$date1 = "2008-11-01 22:45:00"; 

$date2 = "2009-12-04 13:44:01"; 

$diff = abs(strtotime($date2) - strtotime($date1)); 

$years   = floor($diff / (365*60*60*24)); 
$months  = floor(($diff - $years * 365*60*60*24) / (30*60*60*24)); 
$days    = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));

$hours   = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24 - $days*60*60*24)/ (60*60)); 

$minuts  = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24 - $days*60*60*24 - $hours*60*60)/ 60); 

$seconds = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24 - $days*60*60*24 - $hours*60*60 - $minuts*60)); 

printf("%d years, %d months, %d days, %d hours, %d minuts\n, %d seconds\n", $years, $months, $days, $hours, $minuts, $seconds); 
Khaldonno
źródło
7
Prawdopodobnie nie da to dokładnego wyniku.
Dolphin
8
I jest okropnym rozwiązaniem, chyba że jesteś zmuszony użyć strasznie przestarzałej wersji PHP ...
rdlowrey
2
Nie tak SUCHO . Na przykład 60 * 60 * 24 powtarza się 15 razy. Niech żyje ponowne użycie kopiuj-wklej!
Peter Mortensen
Co z latami przestępnymi? Rok średnio nie wynosi 365 dni.
Peter Mortensen
Ten kod zakłada, że ​​miesiąc wynosi średnio 30 dni. Nawet zakładając 365 dni w roku, średni miesiąc to 365/12 = 30,42 dni (w przybliżeniu).
Peter Mortensen
18

Spójrz na poniższy link. To najlepsza odpowiedź, jaką do tej pory znalazłem .. :)

function dateDiff ($d1, $d2) {

    // Return the number of days between the two dates:    
    return round(abs(strtotime($d1) - strtotime($d2))/86400);

} // end function dateDiff

Nie ma znaczenia, która data jest wcześniejsza czy późniejsza, gdy podasz parametry daty. Funkcja wykorzystuje wartość bezwzględną ABS () PHP, aby zawsze zwracać liczbę dodatnią jako liczbę dni między dwiema datami.

Pamiętaj, że liczba dni między dwiema datami NIE obejmuje obu dat. Jeśli więc szukasz liczby dni reprezentowanych przez wszystkie daty między wprowadzonymi datami włącznie, musisz dodać jeden (1) do wyniku tej funkcji.

Na przykład różnica (zwrócona przez powyższą funkcję) między 2013-02-09 a 2013-02-14 wynosi 5. Ale liczba dni lub dat reprezentowana przez zakres dat 2013-02-09 - 2013-02- 14 to 6.

http://www.bizinfosys.com/php/date-difference.html

casper123
źródło
Pytanie dotyczyło różnicy jako liczby lat, miesięcy i dni, a nie całkowitej liczby dni.
Peter Mortensen
14

Głosowałem za Jurka „s odpowiedź jak to mój ulubiony, ale mam wersję pre-php.5.3 ...

Odkryłem, że pracuję nad podobnym problemem - w ten sposób przede wszystkim dotarłem do tego pytania - ale potrzebowałem tylko różnicy godzin. Ale moja funkcja rozwiązała to całkiem nieźle i nie mam nigdzie w mojej własnej bibliotece, aby przechowywać ją tam, gdzie nie zostanie zgubiona i zapomniana, więc ... mam nadzieję, że jest to przydatne dla kogoś.

/**
 *
 * @param DateTime $oDate1
 * @param DateTime $oDate2
 * @return array 
 */
function date_diff_array(DateTime $oDate1, DateTime $oDate2) {
    $aIntervals = array(
        'year'   => 0,
        'month'  => 0,
        'week'   => 0,
        'day'    => 0,
        'hour'   => 0,
        'minute' => 0,
        'second' => 0,
    );

    foreach($aIntervals as $sInterval => &$iInterval) {
        while($oDate1 <= $oDate2){ 
            $oDate1->modify('+1 ' . $sInterval);
            if ($oDate1 > $oDate2) {
                $oDate1->modify('-1 ' . $sInterval);
                break;
            } else {
                $iInterval++;
            }
        }
    }

    return $aIntervals;
}

I test:

$oDate = new DateTime();
$oDate->modify('+111402189 seconds');
var_dump($oDate);
var_dump(date_diff_array(new DateTime(), $oDate));

A wynik:

object(DateTime)[2]
  public 'date' => string '2014-04-29 18:52:51' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'America/New_York' (length=16)

array
  'year'   => int 3
  'month'  => int 6
  'week'   => int 1
  'day'    => int 4
  'hour'   => int 9
  'minute' => int 3
  'second' => int 8

Stąd otrzymałem oryginalny pomysł , który zmodyfikowałem dla moich zastosowań (i mam nadzieję, że moja modyfikacja pojawi się również na tej stronie).

Możesz bardzo łatwo usunąć interwały, których nie chcesz (powiedz „tydzień”), usuwając je z $aIntervalstablicy lub dodając $aExcludeparametr, lub po prostu odfiltrowując je, gdy wypisujesz ciąg.

enobrev
źródło
Niestety nie zwraca to tego samego co DateInterval z powodu przepełnienia roku / miesiąca.
Stephen Harris
2
@StephenHarris: Nie testowałem tego, ale czytając kod jestem pewien, że powinien zwrócić ten sam wynik - pod warunkiem, że usuniesz weekindeks w $aIntervals(ponieważ DateDiffnigdy tego nie używa).
Alix Axel,
Jest to świetne rozwiązanie do wyszukiwania dat, które występują w każdym przedziale między dwiema datami.
betweenbrain 18.10.19
14
<?php
    $today = strtotime("2011-02-03 00:00:00");
    $myBirthDate = strtotime("1964-10-30 00:00:00");
    printf("Days since my birthday: ", ($today - $myBirthDate)/60/60/24);
?>
vengat
źródło
Pytanie dotyczyło różnicy liczby lat , miesięcy i dni . Daje to różnicę jako całkowitą liczbę dni.
Peter Mortensen
11

Nie wiem, czy używasz frameworka PHP, czy nie, ale wiele frameworków PHP ma biblioteki daty / godziny i pomocniki, które pomogą Ci powstrzymać się od ponownego wynalezienia koła.

Na przykład CodeIgniter ma timespan()funkcję. Po prostu wprowadź dwa znaczniki czasu Unix, a automatycznie wygeneruje taki wynik:

1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes

http://codeigniter.com/user_guide/helpers/date_helper.html

Jake Wilson
źródło
8

Użyj przykładu:

echo time_diff_string('2013-05-01 00:22:35', 'now');
echo time_diff_string('2013-05-01 00:22:35', 'now', true);

Wynik :

4 months ago
4 months, 2 weeks, 3 days, 1 hour, 49 minutes, 15 seconds ago

Funkcja:

function time_diff_string($from, $to, $full = false) {
    $from = new DateTime($from);
    $to = new DateTime($to);
    $diff = $to->diff($from);

    $diff->w = floor($diff->d / 7);
    $diff->d -= $diff->w * 7;

    $string = array(
        'y' => 'year',
        'm' => 'month',
        'w' => 'week',
        'd' => 'day',
        'h' => 'hour',
        'i' => 'minute',
        's' => 'second',
    );
    foreach ($string as $k => &$v) {
        if ($diff->$k) {
            $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
        } else {
            unset($string[$k]);
        }
    }

    if (!$full) $string = array_slice($string, 0, 1);
    return $string ? implode(', ', $string) . ' ago' : 'just now';
}
Glavić
źródło
jeśli chcę ustalić, czy różnica jest większa niż 30 minut, co powinienem zrobić?
Ofir Attia
@OfirAttia: masz kilka takich pytań tutaj na SO, wystarczy użyć wyszukiwania. Proste demo
Glavić
7

Mam do tego prostą logikę:

<?php
    per_days_diff('2011-12-12','2011-12-29')
    function per_days_diff($start_date, $end_date) {
        $per_days = 0;
        $noOfWeek = 0;
        $noOfWeekEnd = 0;
        $highSeason=array("7", "8");

        $current_date = strtotime($start_date);
        $current_date += (24 * 3600);
        $end_date = strtotime($end_date);

        $seassion = (in_array(date('m', $current_date), $highSeason))?"2":"1";

        $noOfdays = array('');

        while ($current_date <= $end_date) {
            if ($current_date <= $end_date) {
                $date = date('N', $current_date);
                array_push($noOfdays,$date);
                $current_date = strtotime('+1 day', $current_date);
            }
        }

        $finalDays = array_shift($noOfdays);
        //print_r($noOfdays);
        $weekFirst = array("week"=>array(),"weekEnd"=>array());
        for($i = 0; $i < count($noOfdays); $i++)
        {
            if ($noOfdays[$i] == 1)
            {
                //echo "This is week";
                //echo "<br/>";
                if($noOfdays[$i+6]==7)
                {
                    $noOfWeek++;
                    $i=$i+6;
                }
                else
                {
                    $per_days++;
                }
                //array_push($weekFirst["week"],$day);
            }
            else if($noOfdays[$i]==5)
            {
                //echo "This is weekend";
                //echo "<br/>";
                if($noOfdays[$i+2] ==7)
                {
                    $noOfWeekEnd++;
                    $i = $i+2;
                }
                else
                {
                    $per_days++;
                }
                //echo "After weekend value:- ".$i;
                //echo "<br/>";
            }
            else
            {
                $per_days++;
            }
        }

        /*echo $noOfWeek;
          echo "<br/>";
          echo $noOfWeekEnd;
          echo "<br/>";
          print_r($per_days);
          echo "<br/>";
          print_r($weekFirst);
        */

        $duration = array("weeks"=>$noOfWeek, "weekends"=>$noOfWeekEnd, "perDay"=>$per_days, "seassion"=>$seassion);
        return $duration;
      ?>
Hardik Raval
źródło
Wydaje się, że brakuje czegoś na końcu przykładowego kodu (nawias kończący i „ ?> ”?).
Peter Mortensen
„prosta” logika. Są to co najmniej 40 wierszy czystego kodu.
Madjosz
6

Możesz użyć

getdate()

funkcja, która zwraca tablicę zawierającą wszystkie elementy podanej daty / godziny:

$diff = abs($endDate - $startDate);
$my_t=getdate($diff);
print("$my_t[year] years, $my_t[month] months and $my_t[mday] days");

Jeśli data początkowa i końcowa mają format ciągów, użyj

$startDate = strtotime($startDateStr);
$endDate = strtotime($endDateStr);

przed powyższym kodem

Mark Pim
źródło
wydaje się nie działać. Dostaję datę na początku ery znaczników czasu.
Sirber,
Ważne jest, aby zrozumieć, że musisz zrobić, $my_t["year"] -= 1970aby uzyskać prawidłową liczbę lat. Należy również odjąć swój różnicę godziny od GMT, aby uzyskać prawo godzin. Musisz również odjąć 1 od miesiąca i daty.
Salman A
6
// If you just want to see the year difference then use this function.
// Using the logic I've created you may also create month and day difference
// which I did not provide here so you may have the efforts to use your brain.
// :)
$date1='2009-01-01';
$date2='2010-01-01';
echo getYearDifference ($date1,$date2);
function getYearDifference($date1=strtotime($date1),$date2=strtotime($date2)){
    $year = 0;
    while($date2 > $date1 = strtotime('+1 year', $date1)){
        ++$year;
    }
    return $year;
}
wyjście
źródło
Czy „strtotime („ + 1 rok ”, $ date1)” uwzględnia lata przestępne?
Peter Mortensen
6

To moja funkcja. Wymagane PHP> = 5.3.4. Używa klasy DateTime. Bardzo szybko, szybko i może zrobić różnicę między dwiema datami, a nawet tak zwanym „czasem od”.

if(function_exists('grk_Datetime_Since') === FALSE){
    function grk_Datetime_Since($From, $To='', $Prefix='', $Suffix=' ago', $Words=array()){
        #   Est-ce qu'on calcul jusqu'à un moment précis ? Probablement pas, on utilise maintenant
        if(empty($To) === TRUE){
            $To = time();
        }

        #   On va s'assurer que $From est numérique
        if(is_int($From) === FALSE){
            $From = strtotime($From);
        };

        #   On va s'assurer que $To est numérique
        if(is_int($To) === FALSE){
            $To = strtotime($To);
        }

        #   On a une erreur ?
        if($From === FALSE OR $From === -1 OR $To === FALSE OR $To === -1){
            return FALSE;
        }

        #   On va créer deux objets de date
        $From = new DateTime(@date('Y-m-d H:i:s', $From), new DateTimeZone('GMT'));
        $To   = new DateTime(@date('Y-m-d H:i:s', $To), new DateTimeZone('GMT'));

        #   On va calculer la différence entre $From et $To
        if(($Diff = $From->diff($To)) === FALSE){
            return FALSE;
        }

        #   On va merger le tableau des noms (par défaut, anglais)
        $Words = array_merge(array(
            'year'      => 'year',
            'years'     => 'years',
            'month'     => 'month',
            'months'    => 'months',
            'week'      => 'week',
            'weeks'     => 'weeks',
            'day'       => 'day',
            'days'      => 'days',
            'hour'      => 'hour',
            'hours'     => 'hours',
            'minute'    => 'minute',
            'minutes'   => 'minutes',
            'second'    => 'second',
            'seconds'   => 'seconds'
        ), $Words);

        #   On va créer la chaîne maintenant
        if($Diff->y > 1){
            $Text = $Diff->y.' '.$Words['years'];
        } elseif($Diff->y == 1){
            $Text = '1 '.$Words['year'];
        } elseif($Diff->m > 1){
            $Text = $Diff->m.' '.$Words['months'];
        } elseif($Diff->m == 1){
            $Text = '1 '.$Words['month'];
        } elseif($Diff->d > 7){
            $Text = ceil($Diff->d/7).' '.$Words['weeks'];
        } elseif($Diff->d == 7){
            $Text = '1 '.$Words['week'];
        } elseif($Diff->d > 1){
            $Text = $Diff->d.' '.$Words['days'];
        } elseif($Diff->d == 1){
            $Text = '1 '.$Words['day'];
        } elseif($Diff->h > 1){
            $Text = $Diff->h.' '.$Words['hours'];
        } elseif($Diff->h == 1){
            $Text = '1 '.$Words['hour'];
        } elseif($Diff->i > 1){
            $Text = $Diff->i.' '.$Words['minutes'];
        } elseif($Diff->i == 1){
            $Text = '1 '.$Words['minute'];
        } elseif($Diff->s > 1){
            $Text = $Diff->s.' '.$Words['seconds'];
        } else {
            $Text = '1 '.$Words['second'];
        }

        return $Prefix.$Text.$Suffix;
    }
}
David Bélanger
źródło
6

Wolałbym używać date_createi date_diffobiektów.

Kod:

$date1 = date_create("2007-03-24");
$date2 = date_create("2009-06-26");

$dateDifference = date_diff($date1, $date2)->format('%y years, %m months and %d days');

echo $dateDifference;

Wynik:

2 years, 3 months and 2 days

Aby uzyskać więcej informacji, przeczytaj instrukcję PHPdate_diff

Zgodnie z instrukcją date_diffjest pseudonimem DateTime :: diff ()

Adeel
źródło
5

Spróbuje to wykryć, czy podany został znacznik czasu, a także zwróci przyszłe daty / godziny jako wartości ujemne:

<?php

function time_diff($start, $end = NULL, $convert_to_timestamp = FALSE) {
  // If $convert_to_timestamp is not explicitly set to TRUE,
  // check to see if it was accidental:
  if ($convert_to_timestamp || !is_numeric($start)) {
    // If $convert_to_timestamp is TRUE, convert to timestamp:
    $timestamp_start = strtotime($start);
  }
  else {
    // Otherwise, leave it as a timestamp:
    $timestamp_start = $start;
  }
  // Same as above, but make sure $end has actually been overridden with a non-null,
  // non-empty, non-numeric value:
  if (!is_null($end) && (!empty($end) && !is_numeric($end))) {
    $timestamp_end = strtotime($end);
  }
  else {
    // If $end is NULL or empty and non-numeric value, assume the end time desired
    // is the current time (useful for age, etc):
    $timestamp_end = time();
  }
  // Regardless, set the start and end times to an integer:
  $start_time = (int) $timestamp_start;
  $end_time = (int) $timestamp_end;

  // Assign these values as the params for $then and $now:
  $start_time_var = 'start_time';
  $end_time_var = 'end_time';
  // Use this to determine if the output is positive (time passed) or negative (future):
  $pos_neg = 1;

  // If the end time is at a later time than the start time, do the opposite:
  if ($end_time <= $start_time) {
    $start_time_var = 'end_time';
    $end_time_var = 'start_time';
    $pos_neg = -1;
  }

  // Convert everything to the proper format, and do some math:
  $then = new DateTime(date('Y-m-d H:i:s', $$start_time_var));
  $now = new DateTime(date('Y-m-d H:i:s', $$end_time_var));

  $years_then = $then->format('Y');
  $years_now = $now->format('Y');
  $years = $years_now - $years_then;

  $months_then = $then->format('m');
  $months_now = $now->format('m');
  $months = $months_now - $months_then;

  $days_then = $then->format('d');
  $days_now = $now->format('d');
  $days = $days_now - $days_then;

  $hours_then = $then->format('H');
  $hours_now = $now->format('H');
  $hours = $hours_now - $hours_then;

  $minutes_then = $then->format('i');
  $minutes_now = $now->format('i');
  $minutes = $minutes_now - $minutes_then;

  $seconds_then = $then->format('s');
  $seconds_now = $now->format('s');
  $seconds = $seconds_now - $seconds_then;

  if ($seconds < 0) {
    $minutes -= 1;
    $seconds += 60;
  }
  if ($minutes < 0) {
    $hours -= 1;
    $minutes += 60;
  }
  if ($hours < 0) {
    $days -= 1;
    $hours += 24;
  }
  $months_last = $months_now - 1;
  if ($months_now == 1) {
    $years_now -= 1;
    $months_last = 12;
  }

  // "Thirty days hath September, April, June, and November" ;)
  if ($months_last == 9 || $months_last == 4 || $months_last == 6 || $months_last == 11) {
    $days_last_month = 30;
  }
  else if ($months_last == 2) {
    // Factor in leap years:
    if (($years_now % 4) == 0) {
      $days_last_month = 29;
    }
    else {
      $days_last_month = 28;
    }
  }
  else {
    $days_last_month = 31;
  }
  if ($days < 0) {
    $months -= 1;
    $days += $days_last_month;
  }
  if ($months < 0) {
    $years -= 1;
    $months += 12;
  }

  // Finally, multiply each value by either 1 (in which case it will stay the same),
  // or by -1 (in which case it will become negative, for future dates).
  // Note: 0 * 1 == 0 * -1 == 0
  $out = new stdClass;
  $out->years = (int) $years * $pos_neg;
  $out->months = (int) $months * $pos_neg;
  $out->days = (int) $days * $pos_neg;
  $out->hours = (int) $hours * $pos_neg;
  $out->minutes = (int) $minutes * $pos_neg;
  $out->seconds = (int) $seconds * $pos_neg;
  return $out;
}

Przykładowe użycie:

<?php
  $birthday = 'June 2, 1971';
  $check_age_for_this_date = 'June 3, 1999 8:53pm';
  $age = time_diff($birthday, $check_age_for_this_date)->years;
  print $age;// 28

Lub:

<?php
  $christmas_2020 = 'December 25, 2020';
  $countdown = time_diff($christmas_2020);
  print_r($countdown);
jerdiggity
źródło
5

„jeśli” data jest przechowywana w MySQL, łatwiej jest mi wykonać obliczenie różnicy na poziomie bazy danych ... Następnie na podstawie danych wyjściowych Dzień, Godzina, Min, Sek, parsowanie i wyświetlanie wyników odpowiednio ...

mysql> select firstName, convert_tz(loginDate, '+00:00', '-04:00') as loginDate, TIMESTAMPDIFF(DAY, loginDate, now()) as 'Day', TIMESTAMPDIFF(HOUR, loginDate, now())+4 as 'Hour', TIMESTAMPDIFF(MINUTE, loginDate, now())+(60*4) as 'Min', TIMESTAMPDIFF(SECOND, loginDate, now())+(60*60*4) as 'Sec' from User_ where userId != '10158' AND userId != '10198' group by emailAddress order by loginDate desc;
 +-----------+---------------------+------+------+------+--------+
 | firstName | loginDate           | Day  | Hour | Min  | Sec    |
 +-----------+---------------------+------+------+------+--------+
 | Peter     | 2014-03-30 18:54:40 |    0 |    4 |  244 |  14644 |
 | Keith     | 2014-03-30 18:54:11 |    0 |    4 |  244 |  14673 |
 | Andres    | 2014-03-28 09:20:10 |    2 |   61 | 3698 | 221914 |
 | Nadeem    | 2014-03-26 09:33:43 |    4 |  109 | 6565 | 393901 |
 +-----------+---------------------+------+------+------+--------+
 4 rows in set (0.00 sec)
Elastyczne Myśli
źródło
5

Znalazłem twój artykuł na następnej stronie, która zawiera szereg odniesień do obliczeń daty i godziny w PHP .

Oblicz różnicę między dwiema datami (i czasem) za pomocą PHP. Następująca strona zawiera szereg różnych metod (łącznie 7) do wykonywania obliczeń daty / godziny za pomocą PHP, w celu ustalenia różnicy czasu (godziny, munity), dni, miesięcy lub lat między dwiema datami.

Zobacz PHP Data Godzina - 7 metod obliczania różnicy między 2 datami .

James - Php Development
źródło
4

Możesz także użyć następującego kodu, aby zwrócić różnicę daty o ułamki zaokrąglone w górę $ date1 = $ duedate; // przypisanie terminu echo $ date2 = data („Ymd”); // bieżąca data $ ts1 = strtotime ($ date1); $ ts2 = strtotime ($ date2); $ seconds_diff = $ ts1 - $ ts2; echo $ datediff = ceil (($ seconds_diff / 3600) / 24); // powrót za kilka dni

Jeśli użyjesz metody podłogowej php zamiast sufitu, zwróci ci ona okrągłą frakcję w dół. Sprawdź różnicę tutaj, czasami, jeśli strefa czasowa serwerów pomostowych jest inna niż strefa czasowa witryny na żywo, w takim przypadku możesz uzyskać inne wyniki, więc odpowiednio zmień warunki.

Rikin Adhyapak
źródło
4
$date1 = date_create('2007-03-24');
$date2 = date_create('2009-06-26');
$interval = date_diff($date1, $date2);
echo "difference : " . $interval->y . " years, " . $interval->m." months, ".$interval->d." days ";
Liza
źródło
4

zawsze możesz skorzystać z następującej funkcji, która może przywrócić wiek w latach i miesiącach (tj. 1 rok 4 miesiące)

function getAge($dob, $age_at_date)
{  
    $d1 = new DateTime($dob);
    $d2 = new DateTime($age_at_date);
    $age = $d2->diff($d1);
    $years = $age->y;
    $months = $age->m;

    return $years.'.'.months;
}

lub jeśli chcesz, aby wiek był obliczany na bieżącą datę, możesz użyć

function getAge($dob)
{  
    $d1 = new DateTime($dob);
    $d2 = new DateTime(date());
    $age = $d2->diff($d1);
    $years = $age->y;
    $months = $age->m;

    return $years.'.'.months;
}
Rickus Harmse
źródło
4

Dla wersji php> = 5.3: Utwórz dwa obiekty daty, a następnie użyj date_diff()funkcji. Zwróci obiekt DateInterval php . patrz dokumentacja

$date1=date_create("2007-03-24");
$date2=date_create("2009-06-26");
$diff=date_diff($date1,$date2);
echo $diff->format("%R%a days");
bikram kc
źródło
4

Oto kod do uruchomienia

$date1 = date_create('2007-03-24');
$date2 = date_create('2009-06-26');
$diff1 = date_diff($date1,$date2);
$daysdiff = $diff1->format("%R%a");
$daysdiff = abs($daysdiff);
Mosin
źródło
3

Miałem ten sam problem z PHP 5.2 i rozwiązałem go za pomocą MySQL. Może nie być dokładnie tym, czego szukasz, ale to załatwi sprawę i zwróci liczbę dni:

$datediff_q = $dbh->prepare("SELECT DATEDIFF(:date2, :date1)");
$datediff_q->bindValue(':date1', '2007-03-24', PDO::PARAM_STR);
$datediff_q->bindValue(':date2', '2009-06-26', PDO::PARAM_STR);
$datediff = ($datediff_q->execute()) ? $datediff_q->fetchColumn(0) : false;

Więcej informacji tutaj http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_datediff

Klemen Tušar
źródło
3

Ponieważ wszyscy publikują próbki kodu, oto inna wersja.

Chciałem, aby funkcja wyświetlała różnice od sekund do lat (tylko jedna jednostka). W przypadku okresów dłuższych niż 1 dzień chciałem, aby kumulował się o północy (10 rano w poniedziałek widziany od 9 rano w środę jest 2 dni temu, a nie 1). I dla okresów powyżej miesiąca chciałem, aby rolowanie miało miejsce tego samego dnia miesiąca (w tym dla miesięcy 30 i 31 dni przestępnych).

Oto co wymyśliłem:

/**
 * Returns how long ago something happened in the past, showing it
 * as n seconds / minutes / hours / days / weeks / months / years ago.
 *
 * For periods over a day, it rolls over at midnight (so doesn't depend
 * on current time of day), and it correctly accounts for month-lengths
 * and leap-years (months and years rollover on current day of month).
 *
 * $param string $timestamp in DateTime format
 * $return string description of interval
 */
function ago($timestamp)
{
    $then = date_create($timestamp);

    // for anything over 1 day, make it rollover on midnight
    $today = date_create('tomorrow'); // ie end of today
    $diff = date_diff($then, $today);

    if ($diff->y > 0) return $diff->y.' year'.($diff->y>1?'s':'').' ago';
    if ($diff->m > 0) return $diff->m.' month'.($diff->m>1?'s':'').' ago';
    $diffW = floor($diff->d / 7);
    if ($diffW > 0) return $diffW.' week'.($diffW>1?'s':'').' ago';
    if ($diff->d > 1) return $diff->d.' day'.($diff->d>1?'s':'').' ago';

    // for anything less than 1 day, base it off 'now'
    $now = date_create();
    $diff = date_diff($then, $now);

    if ($diff->d > 0) return 'yesterday';
    if ($diff->h > 0) return $diff->h.' hour'.($diff->h>1?'s':'').' ago';
    if ($diff->i > 0) return $diff->i.' minute'.($diff->i>1?'s':'').' ago';
    return $diff->s.' second'.($diff->s==1?'':'s').' ago';
}
ChrisV
źródło
3

Jakiś czas temu napisałem format_datefunkcję, ponieważ daje wiele opcji, w jaki sposób chcesz mieć swoją datę :

function format_date($date, $type, $seperator="-")
{
    if($date)
    {
        $day = date("j", strtotime($date));
        $month = date("n", strtotime($date));
        $year = date("Y", strtotime($date));
        $hour = date("H", strtotime($date));
        $min = date("i", strtotime($date));
        $sec = date("s", strtotime($date));

        switch($type)
        {
            case 0:  $date = date("Y".$seperator."m".$seperator."d",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 1:  $date = date("D, F j, Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 2:  $date = date("d".$seperator."m".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 3:  $date = date("d".$seperator."M".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 4:  $date = date("d".$seperator."M".$seperator."Y h:i A",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 5:  $date = date("m".$seperator."d".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 6:  $date = date("M",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 7:  $date = date("Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 8:  $date = date("j",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 9:  $date = date("n",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 10: 
                     $diff = abs(strtotime($date) - strtotime(date("Y-m-d h:i:s"))); 
                     $years = floor($diff / (365*60*60*24));
                     $months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
                     $days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));
                     $date = $years . " years, " . $months . " months, " . $days . "days";
        }
    }
    return($date);
}    
Mistrz
źródło
2
Ta odpowiedź jest tak samo błędna jak odpowiedź Khaldonno. Zakłada (przypadek 10), że rok ma 365 dni (co czwarty rok ma 366 dni (z wyjątkiem reguł 100-letnich / 400-letnich dla kalendarza gregoriańskiego)) i że miesiąc ma 30 dni (około 30,42 dni w latach bez przestępstwa). Nawet przy lepszych stałych jest to tylko poprawne średnio, niekoniecznie poprawne dla dwóch konkretnych dat.
Peter Mortensen,
3

Bardzo prosta:

    <?php
        $date1 = date_create("2007-03-24");
        echo "Start date: ".$date1->format("Y-m-d")."<br>";
        $date2 = date_create("2009-06-26");
        echo "End date: ".$date2->format("Y-m-d")."<br>";
        $diff = date_diff($date1,$date2);
        echo "Difference between start date and end date: ".$diff->format("%y years, %m months and %d days")."<br>";
    ?>

Proszę sprawdzić poniższy link, aby uzyskać szczegółowe informacje:

PHP: date_diff - Manual

Zauważ, że dotyczy PHP 5.3.0 lub nowszego.

Choudhury Saadmaan Mahmid
źródło
3

Łatwa funkcja

function time_difference($time_1, $time_2, $limit = null)
{

    $val_1 = new DateTime($time_1);
    $val_2 = new DateTime($time_2);

    $interval = $val_1->diff($val_2);

    $output = array(
        "year" => $interval->y,
        "month" => $interval->m,
        "day" => $interval->d,
        "hour" => $interval->h,
        "minute" => $interval->i,
        "second" => $interval->s
    );

    $return = "";
    foreach ($output AS $key => $value) {

        if ($value == 1)
            $return .= $value . " " . $key . " ";
        elseif ($value >= 1)
            $return .= $value . " " . $key . "s ";

        if ($key == $limit)
            return trim($return);
    }
    return trim($return);
}

Użyj jak

echo time_difference ($time_1, $time_2, "day");

Wróci jak 2 years 8 months 2 days

Anuj
źródło
3

Wypróbuj tę bardzo prostą odpowiedź za pomocą date_diff () , to jest przetestowane.

$date1 = date_create("2017-11-27");
$date2 = date_create("2018-12-29");
$diff=date_diff($date1,$date2);
$months = $diff->format("%m months");
$years = $diff->format("%y years");
$days = $diff->format("%d days");

echo $years .' '.$months.' '.$days;

dane wyjściowe to:

1 years 1 months 2 days
larp
źródło
2

Korzystam z następującej funkcji, którą napisałem, gdy PHP 5.3 (odpowiednio date_diff ()) nie jest dostępne:

        function dateDifference($startDate, $endDate)
        {
            $startDate = strtotime($startDate);
            $endDate = strtotime($endDate);
            if ($startDate === false || $startDate < 0 || $endDate === false || $endDate < 0 || $startDate > $endDate)
                return false;

            $years = date('Y', $endDate) - date('Y', $startDate);

            $endMonth = date('m', $endDate);
            $startMonth = date('m', $startDate);

            // Calculate months
            $months = $endMonth - $startMonth;
            if ($months <= 0)  {
                $months += 12;
                $years--;
            }
            if ($years < 0)
                return false;

            // Calculate the days
            $measure = ($months == 1) ? 'month' : 'months';
            $days = $endDate - strtotime('+' . $months . ' ' . $measure, $startDate);
            $days = date('z', $days);   

            return array($years, $months, $days);
        }
Konstantin Shegunov
źródło
2

DateInterval jest świetne, ale ma kilka zastrzeżeń:

  1. tylko dla PHP 5.3+ ( ale to już nie jest dobra wymówka )
  2. obsługuje tylko lata, miesiące, dni, godziny, minuty i sekundy (bez tygodni)
  3. oblicza różnicę dla wszystkich powyższych + dni (nie można uzyskać różnicy tylko w miesiącach)

Aby temu zaradzić, napisałem następujące (poprawione z odpowiedzi @enobrev ):

function date_dif($since, $until, $keys = 'year|month|week|day|hour|minute|second')
{
    $date = array_map('strtotime', array($since, $until));

    if ((count($date = array_filter($date, 'is_int')) == 2) && (sort($date) === true))
    {
        $result = array_fill_keys(explode('|', $keys), 0);

        foreach (preg_grep('~^(?:year|month)~i', $result) as $key => $value)
        {
            while ($date[1] >= strtotime(sprintf('+%u %s', $value + 1, $key), $date[0]))
            {
                ++$value;
            }

            $date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]);
        }

        foreach (preg_grep('~^(?:year|month)~i', $result, PREG_GREP_INVERT) as $key => $value)
        {
            if (($value = intval(abs($date[0] - $date[1]) / strtotime(sprintf('%u %s', 1, $key), 0))) > 0)
            {
                $date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]);
            }
        }

        return $result;
    }

    return false;
}

Działa dwie pętle; pierwszy dotyczy względnych przedziałów (lat i miesięcy) za pomocą brutalnego wymuszania, a drugi oblicza dodatkowe przedziały bezwzględne za pomocą prostej arytmetyki (więc jest szybszy):

echo humanize(date_dif('2007-03-24', '2009-07-31', 'second')); // 74300400 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'minute|second')); // 1238400 minutes, 0 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'hour|minute|second')); // 20640 hours, 0 minutes, 0 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|day')); // 2 years, 129 days
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week')); // 2 years, 18 weeks
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week|day')); // 2 years, 18 weeks, 3 days
echo humanize(date_dif('2007-03-24', '2009-07-31')); // 2 years, 4 months, 1 week, 0 days, 0 hours, 0 minutes, 0 seconds

function humanize($array)
{
    $result = array();

    foreach ($array as $key => $value)
    {
        $result[$key] = $value . ' ' . $key;

        if ($value != 1)
        {
            $result[$key] .= 's';
        }
    }

    return implode(', ', $result);
}
Alix Axel
źródło
@PeterMortensen: Powinno działać, ale nie daje żadnych gwarancji. Ustaw strefę czasową i spróbuj.
Alix Axel