给定一个 Unix 时间戳,如何获得那一天的开始和结束?

Posted

技术标签:

【中文标题】给定一个 Unix 时间戳,如何获得那一天的开始和结束?【英文标题】:Given a Unix timestamp, how to get beginning and end of that day? 【发布时间】:2012-04-29 04:28:18 【问题描述】:

我有一个这样的 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); 如果您的预期输出是整数形式的时间戳,则有一个简单的一行: $beginOfDay = mktime(0, 0, 0, date('n'), date('j') - 1); $endOfDay = mktime(0, 0, 0, date('n'), date('j')); 【参考方案1】:

您可以将时间转换为当前数据,然后使用 strtotime 函数查找一天的开始,然后简单地添加 24 小时以查找一天的结束。

您还可以使用余数运算符 (%) 来查找最近的日期。例如:

$start_of_day = time() - 86400 + (time() % 86400);
$end_of_day = $start_of_day + 86400;

【讨论】:

并非所有的日子都是 86400 秒长,所以这不会像你期望的那样工作 当您调用time() 两次时,它们可能是不同的秒数。 @ChrisHarrison $now = time(); $start_of_day = $now - 86400 + ($now % 86400);【参考方案2】:

您可以使用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 日,等等)包装几个月/年。

【讨论】:

【参考方案3】:

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

【讨论】:

为什么最后要减去 1 秒? 我已将原始问题阅读为获得当天的最后一秒,而不是第二天的第一秒。 'tomorrow' 将给出当天的第一秒,因此 '-1' 在当天的第一秒之前获得第二秒。 'today' 也返回一天的开始strtotime('today', $timestamp) 似乎容易出现闰秒问题 $endOfDay->modify('tomorrow -1 second'); 也可以。【参考方案4】:

对于将来有此问题的任何人:

任何一天的代码

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

【讨论】:

【参考方案5】:

只是日期时间

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

日期时间的灵活性

我们还可以通过更改指定的第二种格式来获取其他信息,例如月份的开始/结束或小时的开始/结束。月份:'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 文档:

DateTime date 格式信息 DateTimeZone DateInterval

【讨论】:

您创建的内容简洁易读。不幸的是,它会在闰秒时失败。有些日子在 23:59:58 结束,而有些日子在 23:59:60 结束。特别是如果尝试为不存在的日期时间创建日期,例如理论上可能在此之前一秒结束的一天的 23:59:59,这将导致问题。使用 PHP 的 \DateTime::modify 方法的公认答案将避免这种情况。 PHP 的内置库足够智能,可以解决包括闰秒在内的日期和时间周围的所有异常情况。 @Elimm,我不知道有些日子会因为闰日而提前或延后一秒结束。我认为闰年只增加了一天,其他没有受到影响。您能否在代码中给出一些失败的示例(或者您能否分享一天多一秒或少一秒)?另外,您是否可以分享一篇文章来详细说明这一点?感谢您指出modify。我以前不知道。 当然。这些称为闰而不是闰。它们解释了地球的自转速度不是恒定的。我相信 2016 年 6 月 30 日是我们最后的闰秒。 Wikipedia page 深入了解细节。【参考方案6】:
$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = ceil (time() / 86400) * 86400;

如果您在同一个脚本中需要这两个值。对于其中一个变量,它比同时触发地板和天花板要快 +/- 86400 秒。例如:

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

【讨论】:

【参考方案7】:

今天 开始日期时间戳。简单

$stamp = mktime(0, 0, 0);
echo date('m-d-Y H:i:s',$stamp);

【讨论】:

这可能会导致夏令时恰好在午夜发生变化的日子出现意外行为【参考方案8】:

不幸的是,由于在非常特定的情况下发生的 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);

所以您会注意到,我们没有使用-&gt;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

【讨论】:

【参考方案9】:
$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

【讨论】:

【参考方案10】:
    $startOfDay = new \DateTime('tomorrow');
    $startOfDay->modify('-1 day');

这对我有用:)

【讨论】:

【参考方案11】:

聚会有点晚了,但这里有另一种简单的方法来实现您正在寻找的东西:

$timestamp=1330581600;
$format = DATE_ATOM;

$date = (new DateTime())->setTimestamp($timestamp);
// Here's your initial date, created from the timestamp above
// 2012-03-01T06:00:00+00:00
$dateFromTimestamp = $date->format($format);

// This is the beginning of the day
// 2012-03-01T00:00:00+00:00
$startOfDay = $date->setTime(0,0);

// This is the beginning of the next day
// 2012-03-02T00:00:00+00:00
$startOfNextDay = $startOfDay->modify('+1 day');

除非绝对必要,否则我个人会避免使用一天结束。当然,您可以使用23:59:59,但这并不是一天的实际结束时间(还剩 1 秒)。我所做的是使用第二天的开始作为我的结束边界,例如:

$start = new DateTime('2021-11-09 00:00:00');
$end = new DateTime('2021-11-10 00:00:00');
    
if ($someDateTime >= $start && $someDateTime < $end) 
    // do something

如果我必须使用一天的结束时间,我会计算第二天的开始时间,然后从中减去 1 微秒

【讨论】:

以上是关于给定一个 Unix 时间戳,如何获得那一天的开始和结束?的主要内容,如果未能解决你的问题,请参考以下文章

如何每隔一天自动获取 Unix 时间戳?

在特定时区的一天开始时获取时间对象

PHP怎么获得一天,一周,一个月的起始和结束的时间戳??求高人指点

Java 如何获得 Unix 时间戳

Excel技巧|如何在Excel中快速的批量将unix时间戳转化为北京时间

Spark Streaming:如何获取一天的时间戳计数?