Biorąc pod uwagę znacznik czasu w systemie Unix, jak określić początek i koniec tego dnia?

80

Mam taki znacznik czasu w Uniksie:

$timestamp=1330581600

Jak uzyskać początek i koniec dnia dla tego znacznika czasu?

e.g.
$beginOfDay = Start of Timestamp's Day
$endOfDay = End of Timestamp's Day

Próbowałem tego:

$endOfDay = $timestamp + (60 * 60 * 23);

Ale nie sądzę, żeby to zadziałało, ponieważ sam znacznik czasu nie jest dokładnym początkiem dnia.

Talon
źródło
Jeśli jest to czas GMT, prawdopodobnie: $ boundary = floor / ceil ($ timestamp / (60 * 60 * 24)) * (60 * 60 * 24);
hakre
Jeśli oczekiwanym wynikiem jest znacznik czasu w postaci liczby całkowitej, istnieje prosta jedna linijka: $ beginOfDay = mktime (0, 0, 0, date ('n'), date ('j') - 1); $ endOfDay = mktime (0, 0, 0, date ('n'), date ('j'));
jarederaj

Odpowiedzi:

177

strtotime może służyć do szybkiego odcinania godziny / minut / sekund

$beginOfDay = strtotime("today", $timestamp);
$endOfDay   = strtotime("tomorrow", $beginOfDay) - 1;

Można również użyć DateTime, ale wymaga to kilku dodatkowych kroków, aby uzyskać z długiego znacznika czasu

$dtNow = new DateTime();
// Set a non-default timezone if needed
$dtNow->setTimezone(new DateTimeZone('Pacific/Chatham'));
$dtNow->setTimestamp($timestamp);

$beginOfDay = clone $dtNow;
$beginOfDay->modify('today');

$endOfDay = clone $beginOfDay;
$endOfDay->modify('tomorrow');
// adjust from the start of next day to the end of the day,
// per original question
// Decremented the second as a long timestamp rather than the
// DateTime object, due to oddities around modifying
// into skipped hours of day-lights-saving.
$endOfDateTimestamp = $endOfDay->getTimestamp();
$endOfDay->setTimestamp($endOfDateTimestamp - 1);

var_dump(
    array(
        'time ' => $dtNow->format('Y-m-d H:i:s e'),
        'start' => $beginOfDay->format('Y-m-d H:i:s e'),
        'end  ' => $endOfDay->format('Y-m-d H:i:s e'),
    )
);

Z dodaniem wydłużonego czasu w PHP7, istnieje możliwość pominięcia sekundy, jeśli używasz $now <= $endsprawdzania z tym. Użycie $now < $nextStartsprawdzania pozwoliłoby uniknąć tej luki, oprócz dziwactw związanych z odejmowaniem sekund i oszczędzaniem światła dziennego w obsłudze czasu PHP.

rrehbein
źródło
2
Dlaczego minus 1 sekunda na końcu?
Kurt Zhong
1
Pierwotne pytanie odczytałem jako ostatnią sekundę dnia, a nie pierwszą sekundę następnego dnia. „jutro” da pierwszą sekundę dnia, więc „-1”, aby otrzymać drugą przed pierwszą sekundą dnia.
rrehbein
5
„dzisiaj” zwraca również początek dniastrtotime('today', $timestamp)
Semra
1
Wydaje się podatny na problemy z sekundą przestępną
Brad Kent
4
$endOfDay->modify('tomorrow -1 second');też działa.
CGodo
11

Just DateTime

$beginOfDay = DateTime::createFromFormat('Y-m-d H:i:s', (new DateTime())->setTimestamp($timestamp)->format('Y-m-d 00:00:00'))->getTimestamp();
$endOfDay = DateTime::createFromFormat('Y-m-d H:i:s', (new DateTime())->setTimestamp($timestamp)->format('Y-m-d 23:59:59'))->getTimestamp();

Najpierw tworzony jest obiekt DateTime, a sygnatura czasowa jest ustawiana na żądany znacznik czasu. Następnie obiekt jest formatowany jako ciąg znaków określający godzinę / minutę / sekundę na początek lub koniec dnia. Na koniec tworzony jest nowy obiekt DateTime z tego ciągu i pobierany jest znacznik czasu.

Czytelny

$dateTimeObject = new DateTime();
$dateTimeObject->setTimestamp($timestamp);
$beginOfDayString = $dateTimeObject->format('Y-m-d 00:00:00');
$beginOfDayObject = DateTime::createFromFormat('Y-m-d H:i:s', $beginOfDayString);
$beginOfDay = $beginOfDayObject->getTimestamp();

Koniec dnia możemy uzyskać w alternatywny sposób korzystając z tej dłuższej wersji:

$endOfDayObject = clone $beginOfDayOject(); // Cloning because add() and sub() modify the object
$endOfDayObject->add(new DateInterval('P1D'))->sub(new DateInterval('PT1S'));
$endOfDay = $endOfDayOject->getTimestamp();

Strefa czasowa

Strefę czasową można również ustawić, dodając wskaźnik czasu do formatu, na przykład Oi określając sygnaturę czasową po utworzeniu obiektu DateTime:

$beginOfDay = DateTime::createFromFormat('Y-m-d H:i:s O', (new DateTime())->setTimezone(new DateTimeZone('America/Los_Angeles'))->setTimestamp($timestamp)->format('Y-m-d 00:00:00 O'))->getTimestamp();

Elastyczność DateTime

Możemy również uzyskać inne informacje, takie jak początek / koniec miesiąca lub początek / koniec godziny, zmieniając drugi określony format. Przez miesiąc: 'Y-m-01 00:00:00'i 'Y-m-t 23:59:59'. Na godzinę: 'Y-m-d H:00:00'i'Y-m-d H:59:59'

Używając różnych formatów w połączeniu z obiektami add () / sub () i DateInterval, możemy uzyskać początek lub koniec dowolnego okresu, chociaż należy zachować ostrożność, aby poprawnie obsłużyć lata przestępne.

Odpowiednie linki

Z dokumentacji PHP:

Robert Hickman
źródło
2
To, co stworzyłeś, jest zwięzłe i czytelne. Niestety zawiedzie w sekundach przestępnych. Niektóre dni kończą się o 23:59:58, a inne o 23:59:60. W szczególności spowoduje to problemy, jeśli spróbujesz utworzyć datę dla nieistniejącej daty i godziny, na przykład 23:59:59 w dniu, który teoretycznie może zakończyć się jedną sekundę wcześniej. Zaakceptowana odpowiedź, która używa metody PHP \ DateTime :: modyfikuj, pozwoliłaby tego uniknąć. Wbudowana biblioteka PHP jest wystarczająco inteligentna, aby uwzględnić wszystkie dziwactwa związane z datami i godzinami, w tym z sekundami przestępnymi.
Elimn,
@Elimm, nie zdawałem sobie sprawy, że niektóre dni kończyły się jedną sekundę wcześniej lub później z powodu dnia przestępnego. Myślałem, że ten skok dodał tylko jeden dzień w latach przestępnych i nic więcej nie zostało zmienione. Czy możesz podać jakiś przykład w kodzie, jak to się nie udaje (czy możesz podzielić dzień, który ma jedną sekundę więcej lub jedną mniej)? Czy jest też artykuł, który możesz udostępnić, który mówi więcej na ten temat? Dzięki za wskazanie modify. Nie wiedziałem o tym wcześniej.
Robert Hickman
Na pewno. Są to tak zwane sekundy przestępne, a nie dni przestępne lub lata . Uważają, że obrót ziemi nie jest stałą prędkością. Uważam, że 30 czerwca 2016 roku był naszą ostatnią sekundą przestępną. Strona Wikipedii zagłębia się w szczegółowe informacje.
Elimn
7

Możesz użyć kombinacji date()i mktime():

list($y,$m,$d) = explode('-', date('Y-m-d', $ts));
$start = mktime(0,0,0,$m,$d,$y);
$end = mktime(0,0,0,$m,$d+1,$y);

mktime() jest wystarczająco sprytny, aby zawijać miesiące / lata, gdy podany jest dzień poza określonym miesiącem (32 stycznia będzie 1 lutego itp.)

Cal
źródło
4

Możesz przekonwertować czas na aktualne dane, a następnie użyć funkcji strtotime, aby znaleźć początek dnia i po prostu dodać 24 godziny do tego, aby znaleźć koniec dnia.

Możesz również użyć operatora reszty (%), aby znaleźć najbliższy dzień. Na przykład:

$start_of_day = time() - 86400 + (time() % 86400);
$end_of_day = $start_of_day + 86400;
Cameron
źródło
7
Nie wszystkie dni mają długość 86400 sekund, więc nie będzie to działać tak, jak się spodziewasz
Cal
@Cal o tak, nie pomyślałem o tym
Cameron
2
Kiedy dzwonisz time()dwa razy, mogą to być różne sekundy.
Chris Harrison
@ChrisHarrison $ now = time (); $ start_of_day = $ teraz - 86400 + ($ teraz% 86400);
Kareem
4

Zaakceptowana odpowiedź niestety psuje się z powodu błędu php, który występuje w bardzo specyficznych sytuacjach. Omówię te scenariusze, ale najpierw odpowiedź przy użyciu DateTime. Jedyna różnica między tą a zaakceptowaną odpowiedzią występuje po // IMPORTANTlinii:

$dtNow = new DateTime();
// Set a non-default timezone if needed
$dtNow->setTimezone(new DateTimeZone('America/Havana'));
$dtNow->setTimestamp($timestamp);

$beginOfDay = clone $dtNow;

// Go to midnight.  ->modify('midnight') does not do this for some reason
$beginOfDay->modify('today');

// now get the beginning of the next day
$endOfDay = clone $beginOfDay;
$endOfDay->modify('tomorrow');

// IMPORTANT
// get the timestamp
$ts = $endOfDay->getTimestamp();
// subtract one from that timestamp
$tsEndOfDay = $ts - 1;

// we now have the timestamp at the end of the day. we can now use that timestamp
// to set our end of day DateTime
$endOfDay->setTimestamp($tsEndOfDay);

Zauważysz więc, że zamiast używać ->modify('1 second ago');, otrzymujemy znacznik czasu i odejmujemy jeden. Zaakceptowana odpowiedź przy użyciu modify powinna działać, ale psuje się z powodu błędu php w bardzo specyficznych sytuacjach. Ten błąd występuje w strefach czasowych, które zmieniają czas letni o północy, w dniu roku, w którym zegary są przesuwane „do przodu”. Oto przykład, którego możesz użyć do zweryfikowania tego błędu.

przykładowy kod błędu

// a time zone, Cuba, that changes their clocks forward exactly at midnight. on
// the day before they make that change. there are other time zones which do this
$timezone = 'America/Santiago';
$dateString = "2020-09-05";

echo 'the start of the day:<br>';
$dtStartOfDay = clone $dtToday;
$dtStartOfDay->modify('today');
echo $dtStartOfDay->format('Y-m-d H:i:s');
echo ', '.$dtStartOfDay->getTimestamp();

echo '<br><br>the start of the *next* day:<br>';
$dtEndOfDay = clone $dtToday;
$dtEndOfDay->modify('tomorrow');
echo $dtEndOfDay->format('Y-m-d H:i:s');
echo ', '.$dtEndOfDay->getTimestamp();

echo '<br><br>the end of the day, this is incorrect. notice that with ->modify("-1 second") the second does not decrement the timestamp by 1:<br>';
$dtEndOfDayMinusOne = clone $dtEndOfDay;
$dtEndOfDayMinusOne->modify('1 second ago');
echo $dtEndOfDayMinusOne->format('Y-m-d H:i:s');
echo ', '.$dtEndOfDayMinusOne->getTimestamp();

echo '<br><br>the end of the day, this is correct:<br>';
$dtx = clone $dtEndOfDay;
$tsx = $dtx->getTimestamp() - 1;
$dty = clone $dtEndOfDay;
$dty->setTimestamp($tsx);
echo $dty->format('Y-m-d H:i:s');
echo ', '.$tsx;

przykładowy kod błędu

the start of the day:
2020-03-26 00:00:00, 1585173600

the start of the *next* day:
2020-03-27 01:00:00, 1585260000

the end of the day, this is incorrect. notice that with ->modify("1 second ago") the
second does not decrement the timestamp by 1:
2020-03-27 01:59:59, 1585263599

the end of the day, this is correct:
2020-03-26 23:59:59, 1585259999
KayakinKoder
źródło
3

Dzisiaj Znacznik czasu daty rozpoczęcia. Prosty

$stamp = mktime(0, 0, 0);
echo date('m-d-Y H:i:s',$stamp);
Ganesh
źródło
1
Może to powodować nieoczekiwane zachowanie w dni, w których czas letni zmienia się dokładnie o północy
KayakinKoder
0

Dla każdego, kto ma to pytanie w przyszłości:

Kod na dowolny dzień

<?php
$date = "2015-04-12 09:20:00";

$midnight = strtotime("midnight", strtotime($date));
$now = strtotime($date);

$diff = $now - $midnight;
echo $diff;
?>

Kod bieżącego dnia

<?php
$midnight = strtotime("midnight");
$now = date('U');

$diff = $now - $midnight;
echo $diff;
?>
Daniel Bjørnådal
źródło
0
$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = ceil (time() / 86400) * 86400;

Jeśli potrzebujesz obu wartości w tym samym skrypcie. Jest szybszy do +/- 86400 sekund dla jednej ze zmiennych niż wystrzelenie zarówno podłogi, jak i sufitu. Na przykład:

$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = $start_of_day + 86400;
Kareem
źródło