Учитывая временную метку Unix, как получить начало и конец этого дня?

80

У меня есть такая метка времени Unix:

$timestamp=1330581600

Как мне узнать начало и конец дня для этой отметки времени?

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

Я пробовал это:

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

Но я не думаю, что это сработает, потому что метка времени не является точным началом дня.

Talon
источник
Если это время по Гринвичу, возможно: $ border = floor / ceil ($ timestamp / (60 * 60 * 24)) * (60 * 60 * 24);
hakre
Если ваш ожидаемый результат - это временная метка в виде целого числа, есть простой лайнер: $ beginOfDay = mktime (0, 0, 0, date ('n'), date ('j') - 1); $ endOfDay = mktime (0, 0, 0, дата ('n'), дата ('j'));
jarederaj

Ответы:

177

strtotime можно использовать для быстрого сокращения часов / минут / секунд

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

DateTime также можно использовать, хотя для получения длинной временной метки требуется несколько дополнительных шагов.

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

С добавлением расширенного времени в PHP7 есть вероятность пропустить секунду при использовании $now <= $endпроверки с этим. Использование $now < $nextStartпроверки позволило бы избежать этого пробела, в дополнение к странностям, связанным с вычитанием секунд и переходом на летнее время при обработке времени PHP.

Rrehbein
источник
2
Почему минус 1 секунда в конце?
Курт Чжун
1
Я прочитал исходный вопрос как получение последней секунды дня, а не первой секунды следующего дня. "Завтра" даст первую секунду дня, поэтому "-1", чтобы получить вторую перед первой секундой дня.
rrehbein
5
«сегодня» также возвращает начало дняstrtotime('today', $timestamp)
Семра
1
Кажется склонным к проблемам с дополнительной секундой
Брэд Кент,
4
$endOfDay->modify('tomorrow -1 second');тоже работает.
CGodo
11

Просто 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();

Сначала создается объект DateTime, и в качестве метки времени устанавливается желаемая метка времени. Затем объект форматируется как строка, устанавливающая час / минуту / секунду для начала или конца дня. Наконец, из этой строки создается новый объект DateTime и извлекается метка времени.

Удобочитаемый

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

Мы можем получить конец дня альтернативным способом, используя эту более длинную версию:

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

Часовой пояс

Часовой пояс также можно установить, добавив индикатор отметки времени к формату, например, Oи указав отметку времени после создания объекта 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();

Гибкость DateTime

Мы также можем получить другую информацию, такую ​​как начало / конец месяца или начало / конец часа, изменив второй указанный формат. За месяц: 'Y-m-01 00:00:00'а 'Y-m-t 23:59:59'. В течение часа: 'Y-m-d H:00:00'и'Y-m-d H:59:59'

Используя различные форматы в сочетании с объектами add () / sub () и DateInterval, мы можем получить начало или конец любого периода, хотя для правильной обработки високосных лет потребуется определенная осторожность.

Соответствующие ссылки

Из документов PHP:

Роберт Хикман
источник
2
То, что вы создали, сжато и доступно для чтения. К сожалению, он не сработает на дополнительных секундах. Некоторые дни заканчиваются в 23:59:58, а другие - в 23:59:60. В частности, это вызовет проблемы при попытке создать дату для несуществующего datetime, например 23:59:59 в день, который теоретически может закончиться за одну секунду до этого. Принятый ответ, в котором используется метод PHP \ DateTime :: modify, позволит избежать этого. Встроенная библиотека PHP достаточно умен, чтобы учесть все странности, связанные с датами и временем, включая дополнительные секунды.
Elimn 08
@Elimm, я не знал, что некоторые дни заканчиваются на секунду раньше или позже из-за високосного дня. Я думал, что високосный год добавляет только день и больше ничего не меняется. Можете ли вы привести в коде пример того, как это не удается (или вы можете поделиться днем, в котором на одну секунду больше или на одну меньше)? Кроме того, есть ли статья, которой вы можете поделиться, чтобы рассказать об этом больше? Спасибо, что указали modify. Я не знал об этом раньше.
Роберт Хикман
Для уверенности. Это называется високосными секундами, а не високосными днями или годами . Они объясняют, что вращение Земли не является постоянной скоростью. Я считаю, что 30 июня 2016 года было нашей последней дополнительной секундой. Страница Википедии погружается в подробности.
Elimn
7

Вы можете использовать комбинацию date()и 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() достаточно умен, чтобы переносить месяцы / годы, когда дается день за пределами указанного месяца (32 января будет 1 февраля и т. д.)

Cal
источник
4

Вы можете преобразовать время в текущие данные, а затем использовать функцию strtotime, чтобы найти начало дня, и просто добавить к нему 24 часа, чтобы найти конец дня.

Вы также можете использовать оператор остатка (%), чтобы найти ближайший день. Например:

$start_of_day = time() - 86400 + (time() % 86400);
$end_of_day = $start_of_day + 86400;
Кэмерон
источник
7
Не все дни длятся 86400 секунд, так что это не сработает так, как вы ожидаете,
Кэл,
@Cal о да, я не думал об этом
Кэмерон
2
Когда вы звоните time()дважды, они могут быть разными секундами.
Крис Харрисон
@ChrisHarrison $ сейчас = время (); $ start_of_day = $ сейчас - 86400 + ($ сейчас% 86400);
Карим
4

Принятый ответ, к сожалению, не работает из-за ошибки php, которая возникает в очень конкретных сценариях. Я буду обсуждать эти сценарии, но сначала отвечу с помощью DateTime. Единственная разница между этим и принятым ответом появляется после // IMPORTANTстроки:

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

Итак, вы заметите, что вместо использования ->modify('1 second ago');мы получаем метку времени и вычитаем ее. Принятый ответ с использованием modify должен работать, но ломается из-за ошибки php в очень конкретных сценариях. Эта ошибка возникает в часовых поясах, которые изменяют летнее время в полночь, в день года, когда часы переводятся «вперед». Вот пример, который вы можете использовать для проверки этой ошибки.

пример кода ошибки

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

вывод кода примера ошибки

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
КаякинКодер
источник
3

Сегодня Отметка времени даты начала. просто

$stamp = mktime(0, 0, 0);
echo date('m-d-Y H:i:s',$stamp);
Ганеш
источник
1
Это может вызвать неожиданное поведение в дни, когда переход на летнее время меняется ровно в полночь
KayakinKoder
0

Для всех, у кого в будущем возникнет этот вопрос:

Код любого дня

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

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

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

Код текущего дня

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

$diff = $now - $midnight;
echo $diff;
?>
Даниэль Бьёрнадал
источник
0
$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = ceil (time() / 86400) * 86400;

Если вам нужны оба значения в одном скрипте. Это быстрее +/- 86400 секунд для одной из переменных, чем запускать и floor, и ceil. Например:

$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = $start_of_day + 86400;
Карим
источник