PHP DateTime向最近的10分钟四舍五入

18

我正在从mysql字段中检索日期时间,但我需要将其向上舍入到最近的10分钟。

例如,如果日期时间是2013-11-06 14:00:01,则希望将时间返回为6/11/2013 14:10。

最简单的方法是什么?

$datetime = new DateTime($mysqldata);

echo $datetime->format('d/m/Y G:i');

希望得到任何建议。

谢谢。


请查看此链接:http://php.net/manual/zh/function.date.php - Noob
任意四舍五入PHP日期时间的函数:https://dev59.com/-mIj5IYBdhLWcg3w24kF#57399274 - Stephen R
10个回答

20

如果需要的话,将秒数四舍五入到最近的一分钟并将其设为0。

$second = $datetime->format("s");
if($second > 0)
    $datetime->add(new DateInterval("PT".(60-$second)."S"));

2) 获取分钟数

$minute = $datetime->format("i");

3) 将模数转换为 10

$minute = $minute % 10;

4) 如有必要,计算距下一个10分钟的整数倍还有多少分钟

if($minute != 0)
{
    // Count difference
    $diff = 10 - $minute;
    // Add difference
    $datetime->add(new DateInterval("PT".$diff."M"));
}

编辑,感谢 @Ondrej Henek 和 @berturion


1
当时间已经可以被10分钟整除或者为0秒时,它并不准确。它分别添加了10分钟或60秒,因此不可靠。下面有一个改进版本。 - Ondrej Henek
1
谢谢,现在应该已经修复了。 - rand

18

我偶然发现了这个问题,想知道为什么所有的解决方案都这么复杂。其实很简单:

function roundToNext10Min(\DateTime $dt, $precision = 10) {
    $s = $precision * 60;
    $dt->setTimestamp($s * (int) ceil($dt->getTimestamp() / $s));
    return $dt;
}

$dt = roundToNext10Min(new DateTime($str));
echo $dt->format('d/m/Y G:i');

未定义变量:str - somebadhat

17

编辑:我很久以前写了这个答案,有些人发现毫秒没有被考虑在内。值得尝试一下其他答案来找到完美的时间舍入方法。

当我们要舍入的 DateTime 已经是 10 分钟的倍数时,@rand 的答案会有一个 bug。它会跳到 10 分钟后,而这并不是想要的。

同时,当秒已经为零时,总是跳到下一分钟 ($datetime->add(new \DateInterval("PT".(60-$second)."S")); 不是想要的。

这个增强的函数修复了这些 bug,并带有一个 $precision 参数,以涵盖更多的用例。

function roundTime(\DateTime &$datetime, $precision = 30, $round_lower = false) {
    // 1) Set number of seconds to 0 (by rounding up to the nearest minute if necessary)
    $second = (int) $datetime->format("s");
    if ($second > 30 && $round_lower == false) {
        // Jumps to the next minute
        $datetime->add(new \DateInterval("PT".(60-$second)."S"));
    } elseif ($second > 0) {
        // Back to 0 seconds on current minute
        $datetime->sub(new \DateInterval("PT".$second."S"));
    }
    // 2) Get minute
    $minute = (int) $datetime->format("i");
    // 3) Convert modulo $precision
    $minute = $minute % $precision;
    if ($minute > 0) {
        if($round_lower) {
            $datetime->sub(new \DateInterval("PT".$minute."M"));
        } else {
            // 4) Count minutes to next $precision-multiple minutes
            $diff = $precision - $minute;
            // 5) Add the difference to the original date time
            $datetime->add(new \DateInterval("PT".$diff."M"));
        }
    }
}

这个功能现在非常适合我的使用,可能也适合其他人。

编辑:现在它允许设置上下舍入。


尝试了一下,@berturion 是正确的。这应该是被接受的答案。 - Pere
这不会四舍五入微秒,因此将接近的日期时间四舍五入以查看它们是否相等是行不通的。 - Stephen R
有人能否请解释一下这个代码如何在不返回任何内容的情况下工作?我从测试中看到它正在修改原始的Datetime对象,但如果您没有通过引用传递,例如&$datetime,那么它是如何工作的呢? - Stephen R
2
@StephenR $datetime 是一个对象,因此默认情况下是按引用传递的。 - Iter Ator
@IterAtor 我不知道那个。谢谢。 - Stephen R
请查看我的答案,它处理微秒并且还有更多功能。 - Stephen R

9
您可以使用 ceil、floor 和 round 函数将 DateTime 四舍五入到最近的整分钟、向上或向下取整,如下所示:
/**
 * @param \DateTime $dateTime
 * @param int $minuteInterval
 * @return \DateTime
 */
public function roundUpToMinuteInterval(\DateTime $dateTime, $minuteInterval = 10)
{
    return $dateTime->setTime(
        $dateTime->format('H'),
        ceil($dateTime->format('i') / $minuteInterval) * $minuteInterval,
        0
    );
}

/**
 * @param \DateTime $dateTime
 * @param int $minuteInterval
 * @return \DateTime
 */
public function roundDownToMinuteInterval(\DateTime $dateTime, $minuteInterval = 10)
{
    return $dateTime->setTime(
        $dateTime->format('H'),
        floor($dateTime->format('i') / $minuteInterval) * $minuteInterval,
        0
    );
}

/**
 * @param \DateTime $dateTime
 * @param int $minuteInterval
 * @return \DateTime
 */
public function roundToNearestMinuteInterval(\DateTime $dateTime, $minuteInterval = 10)
{
    return $dateTime->setTime(
        $dateTime->format('H'),
        round($dateTime->format('i') / $minuteInterval) * $minuteInterval,
        0
    );
}

为避免对传递给这些函数的 DateTime 对象进行意外更改,建议先克隆该对象或使用 DateTimeImmutable 代替 DateTime - Aad Mathijssen
请注意,这依赖于手册中“超出范围的值会添加到其父级值”的奇特行为。即将72分钟设置为1小时+12分钟。(我本来以为在名为“setTime”的函数中这将是一个验证错误,但在“addTime”中我可以理解它)。此答案中的roundUp函数使用此功能来使设置current-hour, 60s=hour+1, 0s,这在每个小时的最后一个$minuteInterval中需要进行四舍五入和正确溢出。 - scipilot

4
我会使用时间戳:
<?php

function roundTime(\DateTime $datetime, $precision = 10) {

    $ts = $datetime->getTimestamp();
    $s = ($precision * 60);
    $remainder = $ts % $s;

    if ($remainder > 0) {
        $datetime->setTimestamp($ts + $s - $remainder);
    }

    return $datetime;
}

echo roundTime(\DateTime::createFromFormat('Y-m-d H:i:s', '2016-03-04 09:00:00'))
    ->format('Y-m-d H:i:s');
// prints: 2016-03-04 09:00:00


echo roundTime(\DateTime::createFromFormat('Y-m-d H:i:s', '2016-03-04 09:00:01'))
    ->format('Y-m-d H:i:s');
// prints: 2016-03-04 09:10:00

1
public function roundupMinutesFromDatetime(\Datetime $date, $minuteOff = 10)
{
    $string = sprintf(
         "%d minutes %d seconds", 
         $date->format("i") % $minuteOff, 
         $date->format("s")
    );

    return  $date->sub(\DateInterval::createFromDateString($string));
}

并且测试:

public function datetimeProvider()
{
    return array(
        array('2015-01-01 15:16:17', '2015-01-01 15:10:00'),
        array('2015-01-01 09:59:39', '2015-01-01 09:50:00'),
    );
}

/**
 * @dataProvider datetimeProvider
 */
public function testRoundupToMinutesFromDatetime($dateStr, $dateStrAssert)
{
    $format = 'Y-m-d H:i:s';

    $date = \DateTime::createFromFormat($format, $dateStr);
    $date = $this->repo->roundupMinutesFromDatetime($date);
    $this->assertEquals($dateStrAssert, $date->format('Y-m-d H:i:s'));
}

玩得开心。


这是向早期时间舍入的操作。 - user3698243

0

你需要使用 strtotime() 函数将日期转换为时间戳,然后添加或减去分钟,例如

<?php
$datenow  = date("Y-m-d H:i:s", time());
$maketime = strtotime($datenow );

$minutes     = 10;

$after_10min = date("Y-m-d H:i:s", ( $maketime+(60*$minutes) ));
$before_10min = date("Y-m-d H:i:s", ( $maketime-(60*$minutes) ));

echo "Date now : $datenow  <br />";
echo "Date After $minutes min : $after_10min <br />";
echo "Date Before $minutes min : $before_10min <br />";
?>

0

这里有一个函数,可以将数字四舍五入(向上、向下或普通四舍五入)到任意秒、分钟或小时。受@esqilin 's answer启发。

许多博坦人为了给你带来这个信息而牺牲了。

PHP 7,虽然可能不难调整为5。

/**
 * Round $datetime to nearest <$precision> <$unit>s - e.g nearest 10 seconds -- or always up/down per $calc
 * @param \DateTimeInterface $datetime
 * @param int    $precision Round to the nearest number of {$unit}s
 * @param string $unit      'h', 'm', or 's' for Hours, Minutes, or Seconds; or PHP's 'G'/'H' (hours) or 'i' (minutes)
 * @param int    $calc      -1, 0, or 1 = floor, round, or ceiling calculations
 * @return \DateTimeInterface
 * @throws \Exception
 */
function roundTime( \DateTimeInterface $datetime, int $precision = 1, string $unit = 's', int $calc = 0 ) :\DateTimeInterface {
    $offset = $datetime->format( 'Z' );
    $datetime = ( clone $datetime )->modify( "+$offset seconds" );
    switch( $unit ) {   // convert human readable units to PHP DateTime format string
        case 'h':
        case 'H':
            $unit = 'G';
            break;
        case 'm':
            $unit = 'i';
            break;
        // 's' -> 's', no change
    }
    $secs = [ 'G'=>3600, 'i'=>60, 's'=>1 ]; // seconds per hour/minute/second
    if( ! in_array( $unit, array_keys( $secs ) ) ) {
        trigger_error( "Unknown \$unit parameter '$unit' in " . __FUNCTION__ . ". Recognized formats are 'h', 'm', 's', (hours/minutes/seconds), or PHP's 'G', 'H', or 'i'" );
        return $datetime;
    }
    $precision = $precision * $secs[$unit];
    if( -1 === $calc ) {
        $round = 0;
    } elseif( 1 === $calc ) {
        $round = $precision;
    } elseif( 1 == $precision && 's' == $unit ) { // in this -- and only this -- case, we need partial seconds
        $round = ( (int) $datetime->format('v') ) >= 500 ? 1 : 0;
    } else {
        $round = ( ( (int) $datetime->format('U') ) % $precision >= ( $precision/2 ) ) ? $precision : 0;
    }
    $result = $datetime->setTimestamp( ( $precision * floor( $datetime->format('U') / $precision ) ) + $round - $offset );
    return $result;
}

在提出的问题中,您将使用以下内容:
$datetime = new DateTime($mysqldata);

echo roundTime( $datetime, 10, 'm', 1 );

0

简单的代码

<?php
    function next10Minutes()  {
        $time = time() + 600;

        $nextMinutes = floor(date("i", $time) / 10) * 10;

        if ($nextMinutes < 10) {
            $nextMinutes = "00";
        }

        return strtotime(date("d-m-Y H:{$nextMinutes}:00", $time));
    }

这个答案缺少了它的解释说明。 - mickmackusa

-1

我重载了这个类并创建了一个方法:

class MyWish_DateTime extends DateTime {

public function formatToNearRangeMinutesSql($range) {
        $midleRange = $range / 2;

        $minutes = self::format("i");
        $modulo = $minutes % $range;
        if ($modulo > $midleRange) {
            $this->addMinutes($range - $modulo);
        }
        else {
            $this->addMinutes(-$modulo);
        }
        return self::format("H:i");
    }
}

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