如何根据Unix时间戳获取某一天的开始和结束时间?

92

我有一个类似这样的Unix时间戳:

$timestamp=1330581600

如何获取给定时间戳所在日期的开始时间和结束时间?

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

我尝试过这个:

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

但我认为这不会起作用,因为时间戳本身并不是一天的确切开始。


如果是GMT,可能是这样的:$boundary = 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, date('n'), date('j')); - jarederaj
12个回答

193

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时间处理中减去秒和夏令时的奇怪问题。


2
为什么在结尾要减去1秒钟? - Kurt Zhong
5
"today"也会返回一天的开始,即strtotime('today', $timestamp) - Semra
1
似乎容易出现闰秒问题。 - Brad Kent
5
$endOfDay->modify('tomorrow -1 second'); 同样有效。 - CGodo
1
不幸的是,由于PHP的一个漏洞,这个答案在某些非常特定的情况下会出现问题。请参考我的答案进行修复。@rrehbein我知道这是一个旧答案,但如果你想根据我的答案进行编辑,请随意,它是谷歌搜索结果的首选。 - KayakinKoder
显示剩余3条评论

17

仅限日期时间

$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文档:


3
您所创建的内容简洁易读,但遗憾的是它无法适应闰秒。有些日子的结束时间是23:59:58,而另一些则在23:59:60结束。特别是如果尝试为不存在的日期时间创建日期,例如在可以理论上在那之前一秒钟结束的日期23:59:59,则会出现问题。可以采用PHP中的\DateTime::modify方法来解决这个问题。PHP内置库已经足够聪明,能够考虑到围绕日期和时间的所有奇怪情况,包括闰秒。 - Elimn
@Elimm,我不知道由于闰年的存在,有些日子会在前一秒或后一秒结束。我以为闰年只是在闰年多加了一天,其他什么都没有受到影响。你能否给出一些代码示例来说明这种情况的失败(或者你能分享一个有一秒钟更多或更少的日期)?此外,你能分享一篇更详细的文章吗?感谢指出“modify”。我之前不知道这个。 - Robert Hickman
当然。这些被称为闰秒而不是闰日或闰年。它们考虑到地球的旋转速度不是恒定的。我相信2016年6月30日是我们最后一次闰秒。维基百科页面深入探讨了细节。 - Elimn

9
您可以使用 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()函数在处理日期时会自动调整月份/年份,当给定的日期不在指定的月份内时(如1月32日将被调整为2月1日等)。


4
你可以将时间转换为当前日期,然后使用strtotime函数找到当天的开始时间,再加上24小时即可找到当天的结束时间。
你也可以使用取余运算符(%)来找到最近的一天。例如:
$start_of_day = time() - 86400 + (time() % 86400);
$end_of_day = $start_of_day + 86400;

7
不是所有的天都有86400秒,所以这个方法不会像你期望的那样起作用。 - Cal
2
当您两次调用 time() 时,它们可能是不同的秒数。 - Chris Harrison
@ChrisHarrison $now = time(); $start_of_day = $now - 86400 + ($now % 86400); - Kareem

4
很遗憾,已接受的答案因特定情况下发生的php bug而中断。我将讨论这些情况,但首先介绍使用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);

请注意,我们使用获取时间戳并减去1的方法来代替 ->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

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

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
$date = (new \DateTime())->setTimestamp(1330581600);

echo $date->modify('today')->format('Y-m-d H:i:s'); // 2012-02-29 00:00:00
echo PHP_EOL;
echo $date->modify('tomorrow - 1 second')->format('Y-m-d H:i:s'); // 2012-02-29 23:59:59

0
$beginOfDay = (new DateTime('today', new DateTimeZone('Asia/Tehran')))->getTimestamp();
$endOfDay   = $beginOfDay + 86399;

你可以通过替换 "Asia/Tehran" 来设置时区。一天是 86400 秒,不要问我为什么是 86399,它只是我脑海里的一个耳语,所以我甚至不想去考虑它的真实性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接