将分钟向下舍入到最接近的一刻钟

Round minute down to nearest quarter hour

提问人:Rob 提问时间:3/20/2010 最后编辑:eykanalRob 更新时间:11/18/2023 访问量:63660

问:

在 PHP 中,我需要将时间四舍五入到最接近的一刻钟。时间是从 MySQL 数据库的 datetime 列中提取的,格式类似于 .2010-03-18 10:50:00

例:

  • 10:50 需要 10:45
  • 1:12 需要 1:00
  • 3:28 需要 3:15
  • 等。

我假设参与其中,但不确定如何去做。floor()

谢谢

PHP 日期 时间 地板

评论

7赞 Richard JP Le Guen 3/20/2010
如果四舍五入到最接近的一刻钟,1:12 不会变成 1:15,3:28 不会变成 3:30?相反,你不是四舍入到一刻钟吗?前者比后者更具挑战性......
0赞 Rob 3/24/2010
对不起,是的,这就是目标。向下舍入到最接近的一刻钟。
0赞 Stephen R 8/8/2019
用于任意舍入 PHP DateTimes 的函数:stackoverflow.com/a/57399274/339440
0赞 mickmackusa 11/17/2023
关于在 PHP 有机会之前处理此任务的相关建议:如何将时间四舍五入到最接近的 15 分钟段,以及 Java、MySQL 或 PHP - 将分钟四舍五入到接下来的 10 分钟

答:

6赞 Scott Saunders 3/20/2010 #1
$minutes = ($minutes - ($minutes % 15));

评论

0赞 Richard JP Le Guen 3/20/2010
所以 1:19 变成 (19%15)*15 = 4*15 = 60!?
0赞 Scott Saunders 3/20/2010
谢谢理查德。我的头在别的地方。
12赞 Richard JP Le Guen 3/20/2010 #2
$now = getdate();
$minutes = $now['minutes'] - $now['minutes']%15;

 //Can add this to go to the nearest 15min interval (up or down)
  $rmin  = $now['minutes']%15;
  if ($rmin > 7){
    $minutes = $now['minutes'] + (15-$rmin);
   }else{
      $minutes = $now['minutes'] - $rmin;
  }

$rounded = $now['hours'].":".$minutes;
echo $rounded;

评论

0赞 TCB13 5/2/2012
我发现,如果时间是 10:59,则四舍五入为 10:60。为了解决这个问题,我添加了: if ($minutes == “60”) { $rounded = $date_split['hour']+1; $rounded .= “:00”; } else { $minutes = str_pad($minutes, 2, '0', STR_PAD_LEFT); $rounded = $date_split['hour'].“:”.$minutes; }还要str_pad确保到处都是前导零。此解决方案可能是最佳解决方案,但效果很好。
79赞 Veger 3/20/2010 #3
$seconds = time();
$rounded_seconds = round($seconds / (15 * 60)) * (15 * 60);

echo "Original: " . date('H:i', $seconds) . "\n";
echo "Rounded: " . date('H:i', $rounded_seconds) . "\n";

本示例获取当前时间并将其舍入到最接近的四分之一,并打印原始时间和舍入时间。

PS:如果要将其四舍五入,请替换为 .round()floor()

评论

11赞 zmonteca 7/12/2013
如果你想分别用 round() 来补充 ceil()/floor() 来代替 round()。
3赞 Robin Valk 9/14/2015
简单、巧妙且易于定制。棒!
36赞 Wickethewok 3/20/2010 #4

你的全部功能是这样的......

function roundToQuarterHour($timestring) {
    $minutes = date('i', strtotime($timestring));
    return $minutes - ($minutes % 15);
}

评论

1赞 Rob 3/24/2010
删除了该函数并遵循以下路线: $start_minutes = date('i', strtotime($row['start']));$minutes_floor = $start_分钟 - ($start_分钟 % 15);
8赞 FDisk 2/7/2012
如果 $minutes = 29,它将返回 15 - 必须返回 30
0赞 Yasiru Nilan 4/17/2020
如何向上舍入。像 9.10 四舍五入到 9.15 和 9.20 到 9.30 一样
0赞 nice_dev 4/17/2020
@FDisk没错。我们正在向下舍入,而不是最接近。所以,是正确的1529
6赞 Alfred 3/20/2010 #5

最近,我喜欢用TDD/单元测试的方式解决问题。我最近不再编写太多 PHP,但这就是我想出的。老实说,我实际上查看了这里的代码示例,并选择了我认为已经正确的代码示例。接下来,我想使用您上面提供的测试通过单元测试来验证这一点。

TimeTest 类

require_once 'PHPUnit/Framework.php';
require_once 'Time.php';

class TimeTest extends PHPUnit_Framework_TestCase 
{
    protected $time;

    protected function setUp() {
        $this->time = new Time(10, 50);
    }

    public function testConstructingTime() {
        $this->assertEquals("10:50", $this->time->getTime());
        $this->assertEquals("10", $this->time->getHours());
        $this->assertEquals("50", $this->time->getMinutes());        
    }

    public function testCreatingTimeFromString() {
        $myTime = Time::create("10:50");
        $this->assertEquals("10", $myTime->getHours());
        $this->assertEquals("50", $myTime->getMinutes());
    }

    public function testComparingTimes() {
        $timeEquals     = new Time(10, 50);
        $this->assertTrue($this->time->equals($timeEquals));
        $timeNotEquals  = new Time(10, 44);
        $this->assertFalse($this->time->equals($timeNotEquals));
    }


    public function testRoundingTimes()
    {
        // Round test time.
        $roundedTime = $this->time->round();
        $this->assertEquals("10", $roundedTime->getHours());
        $this->assertEquals("45", $roundedTime->getMinutes());

        // Test some more times.
        $timesToTest = array(
            array(new Time(1,00), new Time(1,12)),
            array(new Time(3,15), new Time(3,28)),
            array(new Time(1,00), new Time(1,12)),
        );

        foreach($timesToTest as $timeToTest) {
            $this->assertTrue($timeToTest[0]->equals($timeToTest[0]->round()));
        }        
    }
}

上课时间

<?php

class Time
{
    private $hours;
    private $minutes;

    public static function create($timestr) {
        $hours      = date('g', strtotime($timestr));
        $minutes    = date('i', strtotime($timestr));
        return new Time($hours, $minutes);
    }

    public function __construct($hours, $minutes) {
        $this->hours    = $hours;
        $this->minutes  = $minutes;
    }

    public function equals(Time $time) {
        return  $this->hours == $time->getHours() &&
                 $this->minutes == $time->getMinutes();
    }

    public function round() {
        $roundedMinutes = $this->minutes - ($this->minutes % 15);
        return new Time($this->hours, $roundedMinutes);
    }

    public function getTime() {
        return $this->hours . ":" . $this->minutes;
    }

    public function getHours() {
        return $this->hours;
    }

    public function getMinutes() {
        return $this->minutes;
    }
}

运行测试

alfred@alfred-laptop:~/htdocs/time$ phpunit TimeTest.php 
PHPUnit 3.3.17 by Sebastian Bergmann.

....

Time: 0 seconds

OK (4 tests, 12 assertions)
2赞 Sander 5/29/2011 #6

对于我的系统,我想添加计划在我的服务器上每 5 分钟运行一次的作业,我希望相同的作业在接下来的第 5 分钟块内运行,然后是 15、30、60、120、240 分钟、1 天和 2 天后,这就是此函数的计算结果

function calculateJobTimes() {
    $now = time();
    IF($now %300) {
        $lastTime = $now - ($now % 300);
    }
    ELSE {
        $lastTime = $now;
    }
    $next[] = $lastTime + 300;
    $next[] = $lastTime + 900;
    $next[] = $lastTime + 1800;
    $next[] = $lastTime + 3600;
    $next[] = $lastTime + 7200;
    $next[] = $lastTime + 14400;
    $next[] = $lastTime + 86400;
    $next[] = $lastTime + 172800;
    return $next;
}

echo "The time now is ".date("Y-m-d H:i:s")."<br />
Jobs will be scheduled to run at the following times:<br /><br />
<ul>";
foreach(calculateJobTimes() as $jTime) {
    echo "<li>".date("Y-m-d H:i:s", $jTime).'</li>';
}
echo '</ul>';
1赞 Craig B 12/8/2011 #7

我需要一种方法来四舍五入到这一天,并切断除此之外的所有内容:

$explodedDate = explode("T", gmdate("c",strtotime("now")));
$expireNowDate =  date_create($explodedDate[0]);

strtotime 给了我一个“now”的时间戳,gmdate 将其转换为 ISO 格式(类似于“2012-06-05T04:00:00+00:00”),然后我在“T”处使用 explode,在$explodedDate的第 0 个索引中给我“2012-06-05”,然后将其传递给 date_create 以获取日期对象。

不确定所有这些是否是必要的,但这似乎比经历和减去秒、分钟、小时等要少得多。

评论

0赞 Andy Gee 10/30/2012
或者这可能更容易。$expireNowDate = date_create(date("Y-m-d"));
0赞 ibram 12/5/2012 #8

我编写了一个函数,可以将时间戳四舍五入到秒或分钟。

我可能不是性能最好的方法,但我认为PHP并不关心一些简单的循环。

在您的例子中,您只需像这样传递 MySQL 日期时间:

<?php echo date('d/m/Y - H:i:s', roundTime(strtotime($MysqlDateTime), 'i', 15)); ?>

返回值:最接近的舍入值(向上和向下查看!

功能:

<?php
function roundTime($time, $entity = 'i', $value = 15){

    // prevent big loops
    if(strpos('is', $entity) === false){
        return $time;
    }

    // up down counters
    $loopsUp = $loopsDown = 0;

    // loop up
    $loop = $time;
    while(date($entity, $loop) % $value != 0){
        $loopsUp++;
        $loop++;
    }
    $return = $loop;    


    // loop down
    $loop = $time;
    while(date($entity, $loop) % $value != 0){
        $loopsDown++;
        $loop--;
        if($loopsDown > $loopsUp){
            $loop = $return;
            break;  
        }
    }
    $return = $loop;

    // round seconds down
    if($entity == 'i' && date('s', $return) != 0){
        while(intval(date('s', $return)) != 0){
            $return--;
        }
    }
    return $return;
}
?>

如果要向上或向下舍入到秒,只需将$entity替换为“s”,将 15 替换为要向上或向下排列到的秒数或分钟数。

0赞 Marcel Drews 6/18/2013 #9

这是我目前正在使用的函数:

/**
 * Rounds a timestamp
 *
 * @param int $input current timestamp
 * @param int $round_to_minutes rounds to this minute
 * @param string $type auto, ceil, floor
 * @return int rounded timestamp
 */
static function roundToClosestMinute($input = 0, $round_to_minutes = 5, $type = 'auto')
{
    $now = !$input ? time() : (int)$input;

    $seconds = $round_to_minutes * 60;
    $floored = $seconds * floor($now / $seconds);
    $ceiled = $seconds * ceil($now / $seconds);

    switch ($type) {
        default:
            $rounded = ($now - $floored < $ceiled - $now) ? $floored : $ceiled;
            break;

        case 'ceil':
            $rounded = $ceiled;
            break;

        case 'floor':
            $rounded = $floored;
            break;
    }

    return $rounded ? $rounded : $input;
}

希望它能帮助某人:)

9赞 Mufaddal 8/23/2013 #10

要舍入最接近的一刻钟,请使用以下代码

<?php
$time = strtotime("01:08");
echo $time.'<br />';
$round = 15*60;
$rounded = round($time / $round) * $round;
echo date("H:i", $rounded);
?>

01:08 变成 01:15

评论

0赞 Ilia Ross 12/7/2013
使用您的示例进行计算要精确得多,而不是使用已接受答案中的示例,谢谢!
0赞 Sai 10/4/2015 #11

可能会帮助他人。适用于任何语言。

roundedMinutes = yourRoundFun(Minutes / interval) * interval.

例如,间隔可以是 5 分钟、10 分钟、15 分钟、30 分钟。 然后,四舍五入的分钟数可以重置为相应的日期。

yourDateObj.setMinutes(0) 
yourDateObj.setMinutes(roundedMinutes)
1赞 ram108 1/4/2016 #12
// time = '16:58'
// type = auto, up, down
function round_time( $time, $round_to_minutes = 5, $type = 'auto' ) {
    $round = array( 'auto' => 'round', 'up' => 'ceil', 'down' => 'floor' );
    $round = @$round[ $type ] ? $round[ $type ] : 'round';
    $seconds = $round_to_minutes * 60;
    return date( 'H:i', $round( strtotime( $time ) / $seconds ) * $seconds );
}

评论

0赞 Johnish 8/26/2016
这按要求工作。11:29 到 11:30,11:22 到 11:15。这比 Wickethewok 的解决方案更好。我确实将默认的四舍五入更改为 15,因为请求者要求四舍五入到一刻钟。我还将输出更改为 g:i:s,这符合我的特定需求。
1赞 Rafał Schefczyk 7/27/2016 #13

简单的解决方案:

$oldDate = "2010-03-18 10:50:00";
$date = date("Y-m-d H:i:s", floor(strtotime($oldDate) / 15 / 60) * 15 * 60);

如果要四舍五入,可以更改为。floorceil

4赞 Trent Renshaw 10/19/2016 #14

这是一个老问题,但最近我自己实施了,我将分享我的解决方案:-

public function roundToQuarterHour($datetime) {

    $datetime = ($datetime instanceof DateTime) ? $datetime : new DateTime($datetime);

    return $datetime->setTime($datetime->format('H'), ($i = $datetime->format('i')) - ($i % 15));

}

public function someQuarterHourEvent() {

    print_r($this->roundToQuarterHour(new DateTime()));
    print_r($this->roundToQuarterHour('2016-10-19 10:50:00'));
    print_r($this->roundToQuarterHour('2016-10-19 13:12:00'));
    print_r($this->roundToQuarterHour('2016-10-19 15:28:00'));

}

评论

0赞 Hafenkranich 7/24/2021
它确实是地板 - 但不是四舍五入。
5赞 Ryan 4/13/2019 #15

令我惊讶的是,没有人提到令人惊叹的 Carbon 库(经常在 Laravel 中使用)。

/**
 * 
 * @param \Carbon\Carbon $now
 * @param int $minutesChunk
 * @return \Carbon\Carbon
 */
public static function getNearestTimeRoundedDown($now, $minutesChunk = 30) {
    $newMinute = $now->minute - ($now->minute % $minutesChunk); 
    return $now->minute($newMinute)->startOfMinute(); //https://carbon.nesbot.com/docs/
}

测试用例:

public function testGetNearestTimeRoundedDown() {
    $this->assertEquals('2018-07-06 14:00:00', TT::getNearestTimeRoundedDown(Carbon::parse('2018-07-06 14:12:59'))->format(TT::MYSQL_DATETIME_FORMAT));
    $this->assertEquals('14:00:00', TT::getNearestTimeRoundedDown(Carbon::parse('2018-07-06 14:29:25'))->format(TT::HOUR_MIN_SEC_FORMAT));
    $this->assertEquals('14:30:00', TT::getNearestTimeRoundedDown(Carbon::parse('2018-07-06 14:30:01'))->format(TT::HOUR_MIN_SEC_FORMAT));
    $this->assertEquals('18:00:00', TT::getNearestTimeRoundedDown(Carbon::parse('2019-07-06 18:05:00'))->format(TT::HOUR_MIN_SEC_FORMAT));
    $this->assertEquals('18:45:00', TT::getNearestTimeRoundedDown(Carbon::parse('2019-07-06 18:50:59'), 15)->format(TT::HOUR_MIN_SEC_FORMAT));
    $this->assertEquals('18:45:00', TT::getNearestTimeRoundedDown(Carbon::parse('2019-07-06 18:49:59'), 15)->format(TT::HOUR_MIN_SEC_FORMAT));
    $this->assertEquals('10:15:00', TT::getNearestTimeRoundedDown(Carbon::parse('1999-12-30 10:16:58'), 15)->format(TT::HOUR_MIN_SEC_FORMAT));
    $this->assertEquals('10:10:00', TT::getNearestTimeRoundedDown(Carbon::parse('1999-12-30 10:16:58'), 10)->format(TT::HOUR_MIN_SEC_FORMAT));
}

评论

7赞 Wesley - Synio 3/31/2020
实际上,在使用 Carbon 时,您可以执行或类似操作$now->floorMinute(15)
1赞 wizardzeb 12/5/2020
在Laravel中使用。为了像 @Wesley-Synio 那样四舍五入,它存在于其他时间间隔,例如小时和天。now()->floorMinutes(15)now()->roundMinutes(15)
0赞 Hafenkranich 7/24/2021
因为你不需要 Carbon 库。查看 @TrentRenshaw 的另一个答案
0赞 undefinedman 3/23/2022
如果您要处理日期操纵,迟早要有 Carbon
0赞 mickmackusa 9/19/2019 #16

虽然使用基于日期时间的函数来操作日期时间通常是最合适的,但此任务的要求不涉及任何与时间相关的特殊处理 - 它是对特定子字符串执行计算并使用数学结果替换子字符串的简单任务。

不是每个人都喜欢正则表达式,但它确实提供了一种单函数技术来改变输入字符串。

代码:(演示)

$timeString = "2010-03-18 10:50:57";
// PHP7.4+ arrow syntax
echo preg_replace_callback(
        '~:\K(\d{2}).*~',
        fn($m) => $m[1] - $m[1] % 15 . ':00',
        $timeString
     );

echo "\n---\n";
// below PHP7.3
echo preg_replace_callback(
        '~:\K(\d{2}).*~',
        function($m) {return $m[1] - $m[1] % 15 . ':00';},
        $timeString
     );

输出:

2010-03-18 10:45:00
---
2010-03-18 10:45:00

请注意,如果处理仅时间(冒号分隔)字符串,则此正则表达式模式同样有效。(演示)

2赞 turrican_34 10/19/2020 #17

使用内置的 PHP 函数来计算舍入时间以考虑日期和时间非常重要。例如,当四舍五入到最接近的小时时,需要成为。2020-10-09 23:37:352020-10-10 00:00:00

四舍五入到最接近的小时

$time = '2020-10-09 23:37:35';

$time = date("Y-m-d H:i:s", round(strtotime($time) / 3600) * 3600); // 2020-10-10 00:00:00

$time = '2020-10-09 23:15:35';

$time = date("Y-m-d H:i:s", round(strtotime($time) / 3600) * 3600); // 2020-10-09 23:00:00

将时间向下舍入到最接近的 15 分钟增量

$time = '2020-10-09 23:15:35';

$time = date("Y-m-d H:i:s", floor(strtotime($time) / (60*15))*(60*15)); // 2020-10-09 23:15:00

$time = '2020-10-09 23:41:35';

$time = date("Y-m-d H:i:s", floor(strtotime($time) / (60*15))*(60*15)); // 2020-10-09 23:30:00

如果您需要四舍五入到最接近的 15 分钟增量,请更改为例如floorceil

$time = date("Y-m-d H:i:s", ceil(strtotime($time) / (60*15))*(60*15)); // 2020-10-09 23:45:00

如果您需要将时间四舍五入到另一个分钟增量,您可以简单地执行以下操作:

$time = date("Y-m-d H:i:s", ceil(strtotime($time) / (60*20))*(60*20)); // 2020-10-10 00:00:00
0赞 Joep 7/2/2022 #18

或者你可以让MySQL完成这项工作(对于1970年的日期):

SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(date_col)/900)*900) FROM mytable;
0赞 Muhammed Musabeh 9/12/2022 #19

通过一行代码解决。

protected function roundToQuarterHour($timestring)
    {
        return Carbon::parse($timestring)->roundMinute(15);
    }
0赞 mbomb007 11/17/2023 #20

这里的大多数答案都使它比它需要的更复杂。

PHP 7+ 有一个内置的 intdiv 函数,用于整数除法(底除法)。由于 OP 希望向下舍入到最接近的 15 分钟标记,因此您可以简单地使用:

$t = time();
// Or from string:
$t = strtotime('2010-03-18 10:50:00');
$t = intdiv($t, 900) * 900;
$output = date('Y-m-d H:i:s', $t);

在此处在线运行

900是 15 分钟内的秒数 (60 * 15)。

如果性能非常关键,此代码也应该更快,因为它会减少底层指令,因为您不再使用浮点除法。

评论

0赞 mickmackusa 11/17/2023
是的,这比公认的答案要简单,但您的输入值是 unix 时间戳。但是,原始问题提供了一个格式化的日期字符串。为了完全满足这个问题并吹嘘性能优势,您需要将字符串解析为 unix,计算,然后将整数返回为原始日期时间格式。
0赞 mbomb007 11/18/2023
@mickmackusa OP 没有提供任何代码,而且询问非常模糊。尽管如此,我还是更新了我的答案。问题的核心是如何四舍五入日期。如果用户从数据库中进行选择,那么这取决于他们是否在选择中使用,或者他们如何将数据导入 PHP。UNIX_TIMESTAMP
0赞 mbomb007 11/18/2023
@mickmackusa 此外,还使用了得票最高的答案,这表明该答案仍然有用。time()
0赞 mickmackusa 11/18/2023
Stack Overflow 上的旧页面遭受“任务蠕变”/需求偏差是相对常见的,因为新答案会根据之前发布的答案而向方向摇摆。我只是想让答案专注于回答最初提出的问题。