返回

PHP 时间戳校验: 判断是否在 5 分钟范围内

php

检查两个 Unix 时间戳是否在五分钟范围内

问题:如何使用 DateTime 类,判断两个 Unix 时间戳是否在“系统时钟”的五分钟范围内,而非单纯的 5 分钟 (300 秒) 的时长。例如,允许时间范围为 04:59 到 05:04, 而不仅是单纯地时长在 5 分钟之内的。 这种时间范围边界跨越分钟,小时甚至是天的情景为问题带来了额外的复杂性。

分析问题

问题的难点在于,传统的 abs(timestamp1 - timestamp2) <= 5*60 方法只考虑了时间差,未考虑实际的时钟时间。我们需要的是以时钟分钟数为基础的判断。 直接进行时间戳相减并不可行,尤其当涉及到跨越分钟边界甚至小时、天边界的时候。 另外, DateTime 类在处理这类问题时更加适用,能够提供时、分、秒等信息。

解决方案一: 基于分钟差值的方法

该方案的核心思想是将两个时间戳的分钟数对齐,将时间标准化到同一分钟范围。这样比较的结果将更加准确可靠,有效解决跨越边界的问题。

  1. 初始化 DateTime 对象: 将两个时间戳转化为 DateTime 对象。
  2. 获取各自的分钟数:DateTime 对象中提取分钟数。
  3. 规范分钟范围: 构造当前分钟的起止时间戳,两个时间都设定为同一天的同一个小时。然后对 DateTime 加上或减去1小时,直到他们处于相同的小时中。之后,比较第二个 DateTime 对象与这个起始时间和结束时间的间隔,确保它在范围之内。
  4. 判断范围: 比较两个 DateTime 对象的分钟值,保证差值的绝对值不超过5分钟。 如果差值超出5分钟,检查是否有小时/日界线跨度,在允许跨度的情况将两个时间戳分别减去24小时(或加上24小时),使其在临界点两侧的同一时间范围,在对比。
  5. 返回结果: 满足所有条件,则返回 true,否则返回 false

代码示例:

<?php
function checkTimeRange($timestamp1, $timestamp2) {
    $date1 = new DateTime();
    $date1->setTimestamp($timestamp1);

    $date2 = new DateTime();
    $date2->setTimestamp($timestamp2);
    
     $date1_minute = (int)$date1->format('i');
     $date2_minute = (int)$date2->format('i');


    $minute_diff = abs($date1_minute - $date2_minute);


    if ($minute_diff <= 5) {
       
       return true; // 5分钟内
       } else {
         //尝试跨边界 比如 23:59  和 00:02
       
            $temp_date2_min = clone $date2;
            $temp_date1_min = clone $date1;

           //往前调整 date2 时间到 过去24 小时,并比较是否在范围内
           $temp_date2_min->modify("-1 day");
           $temp_date2_minute = (int)$temp_date2_min->format("i");
            $diff_prev = abs((int)$date1->format("i")-$temp_date2_minute);

           //往前调整 date1 时间到 过去24 小时,并比较是否在范围内
           $temp_date1_min->modify("-1 day");
            $temp_date1_minute =(int) $temp_date1_min->format("i");

           $diff_next = abs($date2_minute - $temp_date1_minute);


           // 往后调整 date2 时间到 未来24小时 并对比是否在范围之内
           $temp_date2_future = clone $date2;
           $temp_date2_future ->modify("+1 day");

           $temp_date2_future_minute = (int) $temp_date2_future->format('i');

          
           $diff_future =  abs((int) $date1->format('i') - $temp_date2_future_minute);



           if ($diff_prev <= 5  || $diff_next<=5 || $diff_future<=5) {

             return true;  //在临界值 24小时调整后 范围仍然在 5分钟之内。
          
          }


      }

      return false; // 时间不在范围内
}

// 测试示例
$timestamp1 = 1711762800; // 2024-03-30 04:00:00
$timestamp2 = 1711763100; // 2024-03-30 04:05:00

if (checkTimeRange($timestamp1,$timestamp2)){
   echo 'within 5 minute Range';

}else{

    echo "not in 5 min range";
}

echo "<br>";

$timestamp3 = 1711762800; // 2024-03-30 04:00:00
$timestamp4 = 1711762200;  // 2024-03-30 03:50:00
if (checkTimeRange($timestamp3,$timestamp4)){
     echo 'within 5 minute Range';
} else{
      echo 'Not in 5 min range';
}

echo "<br>";

$timestamp5 = 1711762800; // 2024-03-30 04:00:00
$timestamp6 = 1711853760;  // 2024-03-31 04:16:00
if (checkTimeRange($timestamp5,$timestamp6)){
    echo 'within 5 minute Range';
} else{
  echo "Not in 5 min range";
}

echo "<br>";


$timestamp7 = 1711798740;  // 2024-03-30 14:59:00
$timestamp8 = 1711798860; // 2024-03-30 15:01:00


if (checkTimeRange($timestamp7,$timestamp8)){

      echo 'within 5 minute Range';
  }else{
      echo "not in 5 min range";
 }


echo "<br>";
$timestamp9 =  1711758360;   // 2024-03-30 03:46:00
$timestamp10 =  1711759380;  // 2024-03-30 04:03:00

if (checkTimeRange($timestamp9,$timestamp10)){

    echo 'within 5 minute Range';
} else {
    echo "not in 5 min range";
}


echo "<br>";
$timestamp11 = 1711791540;    // 2024-03-30 13:09:00
$timestamp12 =  1711793940;   //2024-03-30 13:49:00

if (checkTimeRange($timestamp11,$timestamp12)){
  echo "within 5 minute Range";

} else{
     echo "not in 5 min range";
}

此方法准确且易于理解。 需要额外注意时间戳边界处理,当时间边界刚好在两天的跨度时,将出现跨天分钟数差值较小但不在范围的问题,可以通过加减一整天进行纠正。

方案二: 使用 DateTimediff 方法(改进)

此方案将 diff 方法进行封装,能够更精确计算时间范围。 关键点在于正确理解 diff 返回的 DateInterval 对象。

  1. 创建 DateTime 对象: 同方案一,将时间戳转化为 DateTime 对象。
  2. 使用 diff() 计算差值: 调用 DateTime 对象的 diff 方法,计算时间差。
  3. 提取差值(改进): DateInterval 可以通过不同的格式输出,但我们最需要关注的是分钟。 DateInterval 对象本身并没有提供总分钟数的属性,但是可以提供相差的小时,分钟以及总时间,我们可以依据这个对最终差值进行计算。
  4. 计算跨天调整: 计算差值小时是否等于23或者-23,如果有此类情况,意味着该差值可能来自跨天,那么我们可以将时间日期标准化到一个范围进行对比,再次对分钟差进行检查。
  5. 判断范围: 使用获得的分钟差绝对值和预期值进行比较,确认是否符合范围要求。
  6. 返回结果: 根据判断结果,返回 truefalse

代码示例:

<?php
function checkTimeRangeUsingDiff($timestamp1, $timestamp2) {
  $date1 = new DateTime();
    $date1->setTimestamp($timestamp1);
    
    $date2 = new DateTime();
    $date2->setTimestamp($timestamp2);

  $interval = $date1->diff($date2);


   $totalMinutesDiff = ($interval->days * 24 * 60) + ($interval->h * 60) + $interval->i;



   //处理跨天  
    if($interval->h === 23 || $interval->h ===-23 ){
       $tempDate2 = clone $date2;
       $tempDate2->modify("+ 1 day");


       $intervalTemp = $date1->diff($tempDate2);
       $totalMinutesDiffTemp =  abs(($intervalTemp->days * 24 * 60) + ($intervalTemp->h * 60) + $intervalTemp->i);
       if($totalMinutesDiffTemp<=5){
          return true;
       }
   }


   return abs($totalMinutesDiff) <= 5;

}


// 测试
$timestamp1 = 1711762800; // 2024-03-30 04:00:00
$timestamp2 = 1711763100; // 2024-03-30 04:05:00
echo "within 5 min diff :" . (checkTimeRangeUsingDiff($timestamp1,$timestamp2)? "true" : "false")."\n" ; // true

$timestamp3 = 1711762800; // 2024-03-30 04:00:00
$timestamp4 = 1711762200; // 2024-03-30 03:50:00
echo  "within 5 min diff :".(checkTimeRangeUsingDiff($timestamp3,$timestamp4)? "true" : "false")."\n"; // false

$timestamp5 = 1711762800; // 2024-03-30 04:00:00
$timestamp6 = 1711853760;  // 2024-03-31 04:16:00
echo  "within 5 min diff :".(checkTimeRangeUsingDiff($timestamp5,$timestamp6)? "true" : "false")."\n";  //false

$timestamp7 = 1711798740;  // 2024-03-30 14:59:00
$timestamp8 = 1711798860; // 2024-03-30 15:01:00
echo  "within 5 min diff :".(checkTimeRangeUsingDiff($timestamp7,$timestamp8)? "true" : "false")."\n"; // true

$timestamp9 = 1711758360; // 2024-03-30 03:46:00
$timestamp10 = 1711759380;  //2024-03-30 04:03:00

echo  "within 5 min diff :".(checkTimeRangeUsingDiff($timestamp9,$timestamp10)? "true" : "false")."\n";  // false

$timestamp11 =  1711791540; // 2024-03-30 13:09:00
$timestamp12= 1711793940; // 2024-03-30 13:49:00
echo  "within 5 min diff :".(checkTimeRangeUsingDiff($timestamp11,$timestamp12)? "true" : "false")."\n";  // false

这个改进版本更加优雅简洁,减少了代码量。它巧妙地利用 DateInterval 对象,进行计算,准确度也有保证。 需要注意的是需要额外判断可能存在的跨天场景并进行修正计算。

安全建议

处理时间戳和时间计算时需要特别注意以下事项:

  • 时区问题: 如果应用程序部署在全球不同的时区,需要仔细处理时区转换。务必在存储和处理时间时,使用一致的时区设置,以避免不一致的情况。
  • 数据类型: 确保存储时间戳的数据类型正确(通常为整数),避免因数据类型转换产生的误差。
  • 错误处理: DateTime 类的操作可能会抛出异常,适当添加 try-catch 块来处理这些异常,确保应用程序的稳定性。
  • 验证: 对输入的时间戳进行验证,例如范围限制、有效性检测,避免非法的输入参数导致错误。

两个方法在处理这种特定的时间范围判断时都能有效运作。开发者可以根据实际需要和对代码简洁度的偏好进行选择。第二种使用diff的方法更为高效。 保持时区设置的一致性、处理数据类型的有效性对系统的稳定运行十分重要。