将分钟数向下取到最接近的15分钟

62
我需要在PHP中将时间向下舍入到最近的15分钟。这些时间是从MySQL数据库中的日期时间列中提取并格式化的,例如2010-03-18 10:50:00
例如:
  • 10:50 需要变成 10:45
  • 1:12 需要变成 1:00
  • 3:28 需要变成 3:15
  • 等等。
我假设需要使用floor()函数,但不确定如何实现。
谢谢。

7
如果你要将时间取到最接近的一刻钟,那么1:12会变成1:15,3:28会变成3:30,而不是向下取整到一刻钟。但实际上,你是要向下取整到一刻钟,这比前者稍微有些难度。 - Richard JP Le Guen
抱歉,是的,那就是目标。向下舍入到最近的15分钟。 - Rob
PHP日期时间任意舍入函数:https://dev59.com/-mIj5IYBdhLWcg3w24kF#57399274 - Stephen R
在PHP开始之前,关于处理这个任务的相关建议:如何将时间四舍五入到最近的15分钟段Java、MySQL或PHP - 将分钟向下舍入到下一个10分钟 - undefined
20个回答

79
$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
如果你想强制向上/向下舍入时间,可以使用 ceil()/floor() 来代替 round()。 - zmonteca
3
简单、聪明且易于定制。太棒了! - Robin Valk

36

你的完整函数应该是这样的...

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

1
移除了该函数并采用了以下方法:$start_minutes = date('i', strtotime($row['start'])); $minutes_floor = $start_minutes - ($start_minutes % 15); - Rob
8
如果$minutes等于29,它会返回15,但必须返回30。 - FDisk
如何进行向上舍入。例如,将9.10舍入为9.15,将9.20舍入为9.30,以此类推。 - Yasiru Nilan
@FDisk 没错。我们是向下取整,而不是四舍五入。因此,15 对于 29 是正确的。 - nice_dev

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

我发现如果时间是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函数来确保每个地方都有前导零。这个解决方案可能不是最优的,但运行良好。 - TCB13

9
要将时间取到最接近的15分钟,使用以下代码:
<?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


计算使用您的示例比使用已接受答案中的示例更精确,谢谢! - Ilia

6

最近我喜欢用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)

6
$minutes = ($minutes - ($minutes % 15));

所以1:19变成(19%15)15 = 415 = 60!? - Richard JP Le Guen
谢谢Richard。我的头脑有些混乱。 - Scott Saunders

5

我很惊讶没有人提到令人惊叹的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
在使用Carbon时,实际上你只需要执行$now->floorMinute(15)或类似的操作。 - Wesley - Synio
1
在Laravel中使用now()->floorMinutes(15)来向下舍入时间。要进行四舍五入,请使用now()->roundMinutes(15),就像@Wesley-Synio所说的那样,这也适用于其他时间间隔,如小时和天数。 - wizardzeb
因为您不需要Carbon库。请参考@TrentRenshaw的其他答案。 - Hafenkranich
如果你处理日期操作,Carbon 绝对是必不可少的。 - undefinedman

4

虽然这是一个老问题,但最近我自己实现了,我来分享一下我的解决方案:

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

}

它执行的是向下取整操作,而不是四舍五入。 - Hafenkranich

2

对于我的系统,我想添加定时在服务器上每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>';

2

在将时间四舍五入到最近的小时时,重要的是使用内置的PHP函数,以考虑日期和时间。例如,2020-10-09 23:37:35需要向上舍入到最近的小时变为2020-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分钟增量,请将 floor 更改为 ceil,例如:
$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

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