Dada una marca de tiempo de Unix, ¿cómo empezar y terminar ese día?

80

Tengo una marca de tiempo de Unix como esta:

$timestamp=1330581600

¿Cómo obtengo el comienzo del día y el final del día para esa marca de tiempo?

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

Probé esto:

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

Pero no creo que funcione porque la marca de tiempo en sí no es el comienzo exacto del día.

Garra
fuente
Si es GMT, probablemente: $ boundary = floor / ceil ($ timestamp / (60 * 60 * 24)) * (60 * 60 * 24);
hakre
Si su salida esperada es una marca de tiempo como un entero, hay una línea simple: $ beginOfDay = mktime (0, 0, 0, date ('n'), date ('j') - 1); $ endOfDay = mktime (0, 0, 0, fecha ('n'), fecha ('j'));
jarederaj

Respuestas:

177

strtotime se puede utilizar para cortar rápidamente las horas / minutos / segundos

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

También se puede usar DateTime, aunque requiere algunos pasos adicionales para obtener una marca de tiempo larga

$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'),
    )
);

Con la adición de tiempo extendido en PHP7, existe la posibilidad de perder un segundo si se usa la $now <= $endverificación con esto. El uso de la $now < $nextStartverificación evitaría esa brecha, además de las rarezas de restar segundos y ahorro de luz en el manejo del tiempo de PHP.

rrehbein
fuente
2
¿Por qué menos 1 segundo al final?
Kurt Zhong
1
Había leído que la pregunta original tenía el último segundo del día, en lugar del primer segundo del día siguiente. 'tomorrow' dará el primer segundo del día, por lo que '-1' para obtener el segundo antes del primer segundo del día.
rrehbein
5
'hoy' también devuelve el comienzo del díastrtotime('today', $timestamp)
Semra
1
Parece propenso a problemas de segundos intercalares
Brad Kent
4
$endOfDay->modify('tomorrow -1 second');también funciona.
CGodo
11

Sólo 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();

Primero se crea un objeto DateTime y la marca de tiempo se establece en la marca de tiempo deseada. Luego, el objeto se formatea como una cadena que establece la hora / minuto / segundo al principio o al final del día. Por último, se crea un nuevo objeto DateTime a partir de esta cadena y se recupera la marca de tiempo.

Legible

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

Podemos obtener el final del día de una manera alternativa usando esta versión más larga:

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

Zona horaria

La zona horaria también se puede establecer agregando un indicador de marca de tiempo al formato Oy especificando la marca de tiempo después de crear el objeto 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();

Flexibilidad de DateTime

También podemos obtener otra información como el comienzo / final del mes o el comienzo / final de la hora cambiando el segundo formato especificado. Por mes:'Y-m-01 00:00:00' y 'Y-m-t 23:59:59'. Por hora: 'Y-m-d H:00:00'y'Y-m-d H:59:59'

Utilizando varios formatos en combinación con add () / sub () y objetos DateInterval, podemos obtener el comienzo o el final de cualquier período, aunque habrá que tener cuidado para manejar correctamente los años bisiestos.

Enlaces relevantes

De los documentos de PHP:

Robert Hickman
fuente
2
Lo que ha creado es conciso y legible. Desafortunadamente, fallará en segundos intercalares. Algunos días terminan a las 23:59:58, mientras que otros terminan a las 23:59:60. En particular, esto causará problemas si intenta crear una fecha para una fecha y hora inexistente, como 23:59:59 en un día que, en teoría, podría terminar un segundo antes. La respuesta aceptada, que usa el método \ DateTime :: modificar de PHP, evitaría esto. La biblioteca integrada de PHP es lo suficientemente inteligente como para tener en cuenta todas las rarezas que rodean las fechas y horas, incluidos los segundos intercalares.
Elimn
@Elimm, no sabía que algunos días terminaban un segundo antes o después debido a un día bisiesto. Pensé que el salto solo agregaba un día en los años bisiestos y nada más se veía afectado. ¿Puede dar algún ejemplo en código de cómo esto falla (o puede compartir un día que tiene uno más o uno menos)? Además, ¿hay algún artículo que puedas compartir que cuente más sobre esto? Gracias por señalar modify. No sabía de eso antes.
Robert Hickman
Sin lugar a duda. Estos se denominan segundos bisiestos, no días o años bisiestos . Dan cuenta de que la rotación de la tierra no es una velocidad constante. Creo que el 30 de junio de 2016 fue nuestro último segundo intercalar. La página de Wikipedia se sumerge en los detalles arenosos.
Elimn
7

Puede utilizar una combinación de date()y 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() es lo suficientemente inteligente como para envolver meses / años cuando se le da un día fuera del mes especificado (el 32 de enero será el 1 de febrero, etc.)

California
fuente
4

Puede convertir la hora a los datos actuales y luego usar la función strtotime para encontrar el comienzo del día y simplemente agregar 24 horas a eso para encontrar el final del día.

También puede usar el operador restante (%) para encontrar el día más cercano. Por ejemplo:

$start_of_day = time() - 86400 + (time() % 86400);
$end_of_day = $start_of_day + 86400;
Cameron
fuente
7
No todos los días duran 86400 segundos, por lo que esto no funcionará como espera
Cal
@Cal oh sí, no pensé en eso
Cameron
2
Cuando llamas time()dos veces, pueden ser segundos diferentes.
Chris Harrison
@ChrisHarrison $ ahora = hora (); $ start_of_day = $ ahora - 86400 + ($ ahora% 86400);
Kareem
4

Desafortunadamente, la respuesta aceptada se rompe debido a un error de php que ocurre en escenarios muy específicos. Discutiré esos escenarios, pero primero la respuesta usando DateTime. La única diferencia entre esto y la respuesta aceptada ocurre después de la // IMPORTANTlínea:

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

Por lo tanto, notará que en lugar de usar ->modify('1 second ago');, obtenemos la marca de tiempo y restamos una. La respuesta aceptada usando modify debería funcionar, pero se rompe debido a un error de php en escenarios muy específicos. Este error ocurre en zonas horarias que cambian el horario de verano a la medianoche, el día del año en que los relojes se adelantan. Aquí hay un ejemplo que puede usar para verificar ese error.

código de error de ejemplo

// 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;

salida de código de ejemplo de error

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
fuente
3

Hoy Fecha de inicio de marca de tiempo. Simple

$stamp = mktime(0, 0, 0);
echo date('m-d-Y H:i:s',$stamp);
Ganesh
fuente
1
Esto puede causar un comportamiento inesperado en los días que tienen un cambio de horario de verano exactamente a la medianoche
KayakinKoder
0

Para cualquiera que tenga esta pregunta en el futuro:

Cualquier código de día

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

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

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

Código del día actual

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

$diff = $now - $midnight;
echo $diff;
?>
Daniel Bjørnådal
fuente
0
$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = ceil (time() / 86400) * 86400;

Si necesita ambos valores en el mismo script. Es más rápido +/- 86400 segundos para una de las variables que disparar tanto en el piso como en el techo. Por ejemplo:

$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = $start_of_day + 86400;
Kareem
fuente