【问题标题】:PHP: producing relative date/time from timestampsPHP:从时间戳产生相对日期/时间
【发布时间】:2011-02-11 00:59:38
【问题描述】:

我基本上是在尝试将 Unix 时间戳(time() 函数)转换为与过去和未来日期兼容的相对日期/时间。所以输出可能是:

2 周前

1 小时 60 分钟前

15 分 54 秒前

10 分 15 秒后

首先我尝试编写此代码,但创建了一个无法维护的巨大功能,然后我在互联网上搜索了几个小时,但我只能找到只产生一部分时间的脚本(嗯:“1 小时之前”没有分钟)。

你有一个已经这样做的脚本吗?

【问题讨论】:

标签: php time timestamp relative-date


【解决方案1】:

此功能可在“现在”和“特定时间戳”之间为您提供“1 小时前”或“明天”之类的结果。

function time2str($ts)
{
    if(!ctype_digit($ts))
        $ts = strtotime($ts);

    $diff = time() - $ts;
    if($diff == 0)
        return 'now';
    elseif($diff > 0)
    {
        $day_diff = floor($diff / 86400);
        if($day_diff == 0)
        {
            if($diff < 60) return 'just now';
            if($diff < 120) return '1 minute ago';
            if($diff < 3600) return floor($diff / 60) . ' minutes ago';
            if($diff < 7200) return '1 hour ago';
            if($diff < 86400) return floor($diff / 3600) . ' hours ago';
        }
        if($day_diff == 1) return 'Yesterday';
        if($day_diff < 7) return $day_diff . ' days ago';
        if($day_diff < 31) return ceil($day_diff / 7) . ' weeks ago';
        if($day_diff < 60) return 'last month';
        return date('F Y', $ts);
    }
    else
    {
        $diff = abs($diff);
        $day_diff = floor($diff / 86400);
        if($day_diff == 0)
        {
            if($diff < 120) return 'in a minute';
            if($diff < 3600) return 'in ' . floor($diff / 60) . ' minutes';
            if($diff < 7200) return 'in an hour';
            if($diff < 86400) return 'in ' . floor($diff / 3600) . ' hours';
        }
        if($day_diff == 1) return 'Tomorrow';
        if($day_diff < 4) return date('l', $ts);
        if($day_diff < 7 + (7 - date('w'))) return 'next week';
        if(ceil($day_diff / 7) < 4) return 'in ' . ceil($day_diff / 7) . ' weeks';
        if(date('n', $ts) == date('n') + 1) return 'next month';
        return date('F Y', $ts);
    }
}

【讨论】:

  • 但只返回小时和分钟(例如),对吧?
【解决方案2】:
function relativeTime($time) {

    $d[0] = array(1,"second");
    $d[1] = array(60,"minute");
    $d[2] = array(3600,"hour");
    $d[3] = array(86400,"day");
    $d[4] = array(604800,"week");
    $d[5] = array(2592000,"month");
    $d[6] = array(31104000,"year");

    $w = array();

    $return = "";
    $now = time();
    $diff = ($now-$time);
    $secondsLeft = $diff;

    for($i=6;$i>-1;$i--)
    {
         $w[$i] = intval($secondsLeft/$d[$i][0]);
         $secondsLeft -= ($w[$i]*$d[$i][0]);
         if($w[$i]!=0)
         {
            $return.= abs($w[$i]) . " " . $d[$i][1] . (($w[$i]>1)?'s':'') ." ";
         }

    }

    $return .= ($diff>0)?"ago":"left";
    return $return;
}

用法:

echo relativeTime((time()-256));
4 minutes 16 seconds ago

【讨论】:

    【解决方案3】:

    这是我写的。显示相对于今天日期的过去日期。

    /**
     * @param $date integer of unixtimestamp format, not actual date type
     * @return string
     */
    function zdateRelative($date)
    {
        $now = time();
        $diff = $now - $date;
    
        if ($diff < 60){
            return sprintf($diff > 1 ? '%s seconds ago' : 'a second ago', $diff);
        }
    
        $diff = floor($diff/60);
    
        if ($diff < 60){
            return sprintf($diff > 1 ? '%s minutes ago' : 'one minute ago', $diff);
        }
    
        $diff = floor($diff/60);
    
        if ($diff < 24){
            return sprintf($diff > 1 ? '%s hours ago' : 'an hour ago', $diff);
        }
    
        $diff = floor($diff/24);
    
        if ($diff < 7){
            return sprintf($diff > 1 ? '%s days ago' : 'yesterday', $diff);
        }
    
        if ($diff < 30)
        {
            $diff = floor($diff / 7);
    
            return sprintf($diff > 1 ? '%s weeks ago' : 'one week ago', $diff);
        }
    
        $diff = floor($diff/30);
    
        if ($diff < 12){
            return sprintf($diff > 1 ? '%s months ago' : 'last month', $diff);
        }
    
        $diff = date('Y', $now) - date('Y', $date);
    
        return sprintf($diff > 1 ? '%s years ago' : 'last year', $diff);
    }
    

    【讨论】:

      【解决方案4】:

      我喜欢 xdebug 的 relativeTime 函数。问题是我需要它有一些粒度。

      换句话说,如果我决定在几秒钟或几分钟内停止。 所以现在,

      echo fTime(strtotime('-23 hours 5 minutes 55 seconds'),0); 
      

      会显示,

      23 小时 5 分钟前

      代替

      23 小时 5 分 55 秒前

      如果它达到较高的时间量之一,我还希望它不会在数组中降低。 所以如果它显示年份,我只想显示年份和月份。 所以现在,

      echo fTime(strtotime('-1 year 2 months 3 weeks 4 days 16 hours 15 minutes 22 seconds'),0); 
      

      会显示

      1 年零 2 个月前

      代替

      1 年 2 个月 3 周 4 天 16 小时 15 分钟 22 秒前

      以下代码更改满足了我的需要。道具当然首先去 xdebug。 希望其他人可能会发现它有用:

      function fTime($time, $gran=-1) {
      
          $d[0] = array(1,"second");
          $d[1] = array(60,"minute");
          $d[2] = array(3600,"hour");
          $d[3] = array(86400,"day");
          $d[4] = array(604800,"week");
          $d[5] = array(2592000,"month");
          $d[6] = array(31104000,"year");
      
          $w = array();
      
          $return = "";
          $now = time();
          $diff = ($now-$time);
          $secondsLeft = $diff;
          $stopat = 0;
          for($i=6;$i>$gran;$i--)
          {
               $w[$i] = intval($secondsLeft/$d[$i][0]);
               $secondsLeft -= ($w[$i]*$d[$i][0]);
               if($w[$i]!=0)
               {
                  $return.= abs($w[$i]) . " " . $d[$i][1] . (($w[$i]>1)?'s':'') ." ";
                   switch ($i) {
                      case 6: // shows years and months
                          if ($stopat==0) { $stopat=5; }
                          break;
                      case 5: // shows months and weeks
                          if ($stopat==0) { $stopat=4; }
                          break;
                      case 4: // shows weeks and days
                          if ($stopat==0) { $stopat=3; }
                          break;
                      case 3: // shows days and hours
                          if ($stopat==0) { $stopat=2; }
                          break;
                      case 2: // shows hours and minutes
                          if ($stopat==0) { $stopat=1; }
                          break;
                      case 1: // shows minutes and seconds if granularity is not set higher
                          break;
                   }
                   if ($i===$stopat) { break 0; }
               }
          }
      
          $return .= ($diff>0)?"ago":"left";
          return $return;
      }
      

      马库斯

      【讨论】:

      • 自 PHP 5.4 起,break 0 不再受支持。这应该更新为break 1
      【解决方案5】:

      这是我过去使用的:

      function zdateRelative($date)
      {
        $diff = time() - $date;
        $periods[] = [60, 1, '%s seconds ago', 'a second ago'];
        $periods[] = [60*100, 60, '%s minutes ago', 'one minute ago'];
        $periods[] = [3600*70, 3600, '%s hours ago', 'an hour ago'];
        $periods[] = [3600*24*10, 3600*24, '%s days ago', 'yesterday'];
        $periods[] = [3600*24*30, 3600*24*7, '%s weeks ago', 'one week ago'];
        $periods[] = [3600*24*30*30, 3600*24*30, '%s months ago', 'last month'];
        $periods[] = [INF, 3600*24*265, '%s years ago', 'last year'];
        foreach ($periods as $period) {
          if ($diff > $period[0]) continue;
          $diff = floor($diff / $period[1]);
          return sprintf($diff > 1 ? $period[2] : $period[3], $diff);
        }
      }
      

      【讨论】:

      • 非常感谢您。对于那些想使用它但只显示小时数限制为 24 的人,请在 period[] 的第三项中将 70 替换为 24。
      【解决方案6】:

      我需要一个给我如下结果,所以我自己写了。希望这会对某人有所帮助。

      示例用法:

      $datetime = "2014-08-13 12:52:48";  
      echo getRelativeTime($datetime);    //10 hours ago  
      echo getRelativeTime($datetime, 1); //10 hours ago  
      echo getRelativeTime($datetime, 2); //10 hours and 50 minutes ago  
      echo getRelativeTime($datetime, 3); //10 hours, 50 minutes and 50 seconds ago  
      echo getRelativeTime($datetime, 4); //10 hours, 50 minutes and 50 seconds ago  
      

      代码:

      public function getRelativeTime($datetime, $depth=1) {
      
          $units = array(
              "year"=>31104000,
              "month"=>2592000,
              "week"=>604800,
              "day"=>86400,
              "hour"=>3600,
              "minute"=>60,
              "second"=>1
          );
      
          $plural = "s";
          $conjugator = " and ";
          $separator = ", ";
          $suffix1 = " ago";
          $suffix2 = " left";
          $now = "now";
          $empty = "";
      
          # DO NOT EDIT BELOW
      
          $timediff = time()-strtotime($datetime);
          if ($timediff == 0) return $now;
          if ($depth < 1) return $empty;
      
          $max_depth = count($units);
          $remainder = abs($timediff);
          $output = "";
          $count_depth = 0;
          $fix_depth = true;
      
          foreach ($units as $unit=>$value) {
              if ($remainder>$value && $depth-->0) {
                  if ($fix_depth) {
                      $max_depth -= ++$count_depth;
                      if ($depth>=$max_depth) $depth=$max_depth;
                      $fix_depth = false;
                  }
                  $u = (int)($remainder/$value);
                  $remainder %= $value;
                  $pluralise = $u>1?$plural:$empty;
                  $separate = $remainder==0||$depth==0?$empty:
                                  ($depth==1?$conjugator:$separator);
                  $output .= "{$u} {$unit}{$pluralise}{$separate}";
              }
              $count_depth++;
          }
          return $output.($timediff<0?$suffix2:$suffix1);
      }
      

      【讨论】:

      • 谢谢,我喜欢你的功能并使用它。
      • 我也在我的项目中使用了一个稍微修改过的版本。
      【解决方案7】:

      你可以通过 packagist 使用 Carbon,太棒了 :) https://github.com/briannesbitt/Carbon#api-humandiff

      【讨论】:

        【解决方案8】:

        为什么不抄袭 drupal 的做法 - http://api.drupal.org/api/drupal/includes%21common.inc/function/format_interval/7

        <?php
        function format_interval($interval, $granularity = 2, $langcode = NULL) {
          $units = array(
            '1 year|@count years' => 31536000, 
            '1 month|@count months' => 2592000, 
            '1 week|@count weeks' => 604800, 
            '1 day|@count days' => 86400, 
            '1 hour|@count hours' => 3600, 
            '1 min|@count min' => 60, 
            '1 sec|@count sec' => 1,
          );
          $output = '';
          foreach ($units as $key => $value) {
            $key = explode('|', $key);
            if ($interval >= $value) {
              $output .= ($output ? ' ' : '') . format_plural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
              $interval %= $value;
              $granularity--;
            }
        
            if ($granularity == 0) {
              break;
            }
          }
          return $output ? $output : t('0 sec', array(), array('langcode' => $langcode));
        }
        ?>
        

        您可能不需要替换 t() 并且您可以很容易地为 format_plural 做自己的事情,因为您(可能)不必支持多种语言。 http://api.drupal.org/api/drupal/includes%21common.inc/function/format_plural/7

        【讨论】:

        • Drupal 的功能不会受到版权/开源许可的保护吗?
        • @Rhino,这完全取决于您打算如何处理您的代码。如果您不分发源代码,您可以使用 Drupal 中的 GPL 代码做您喜欢的事情。如果您分发源代码(收费或免费),那么使用 Drupal 的一部分会影响您可以重新分发的许可证(即您必须自己根据 GPL 许可证分发)-drupal.org/licensing/faq。跨度>
        • 是的,我了解 GPL,只是指出您可能想在答案中提及限制。
        【解决方案9】:

        应该很容易适应不同的格式。这个简单的函数只适用于过去的时间戳。

        // return relative date/time string from timestamp
        // [n yrs] [n mos] [n days] h:i:s
        function relative_time(int $time): string
        {
                $dt = new DateTime();
                $dt->setTimestamp($time);
                $diff = (new DateTime())->diff($dt);
        
                $s = "";
                if ($diff->y) $s .= " {$diff->y} " . (($diff->y > 1) ? "yrs" : "yr");
                if ($diff->m) $s .= " {$diff->m} " . (($diff->m > 1) ? "mos" : "mo");
                if ($diff->d) $s .= " {$diff->d} " . (($diff->d > 1) ? "days" : "day");
                $s .= sprintf(" %02d:%02d:%02d", $diff->h, $diff->i, $diff->s);
                return trim($s);
        }
        

        【讨论】:

          【解决方案10】:

          PHP 8.0.0 现在通过 IntlDateFormatter::format 添加了 IntlDateFormatter::RELATIVE_* 常量,实现了相对日期的相当弱的实现。

          这在撰写本文时可能不是超级有用,因为它输出字符串“昨天”、“今天”和“明天”......然后它会回退到完整的/long/medium/short 日期适用于这些范围之外的任何内容。

          最大的吸引力在于它是完全国际化的;因此,使用 de_DEko_KRpa_IN 等其他语言环境将为您提供相关脚本中的翻译字符串......这可能是值得妥协的。

          $formatter = new IntlDateFormatter(
              'en_US',
              IntlDateFormatter::RELATIVE_FULL,
              IntlDateFormatter::NONE,
              'America/Los_Angeles',
              IntlDateFormatter::GREGORIAN
          );
          
          echo $formatter->format( time() - 86400 );
          echo $formatter->format( time() );
          echo $formatter->format( time() + 86400 );
          

          未来的读者也有可能会使用具有更全面的相对时间格式的更新版本的 PHP。

          【讨论】:

            猜你喜欢
            • 2013-06-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-28
            • 1970-01-01
            • 2011-08-07
            • 2012-01-27
            相关资源
            最近更新 更多