【问题标题】:What is wrong with my timestamps?我的时间戳有什么问题?
【发布时间】:2013-07-09 14:22:22
【问题描述】:

所以基本上,几天前我使用了我的 MySQL 数据库,我将时区设置为 MST 以与我的 PHP 脚本所在的服务器相匹配(我还将 PHP-server 时区设置为 MST (date_default_timezone_set("MST")) 与数据库匹配)。我有一个需要一个参数的函数,那就是一个 UNIX 时间戳。

function time_elapsed_string($time)
{
    $time = time() - $time;

    $tokens = array(
        31536000 => 'year',
        2592000 => 'month',
        604800 => 'week',
        86400 => 'day',
        3600 => 'hour',
        60 => 'minute',
        1 => 'second'
    );

    foreach($tokens as $unit => $text)
    {
        if($time < $unit) continue;
        $numberOfUnits = floor($time / $unit);
        return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'');
    }
}

这个函数的作用是接受$time。它获取当前时间和 $time 之间的时间差并将$time 设置为该差值。然后它计算$time 可能是的最大可能时间戳并对其进行回显。如果它是复数形式(2 周、5 个月等),它会在末尾添加一个 s

但是,如果我将当前时间作为$time-参数,它将回显“[SPACE]ago”。我通过将行 $time = time() - $time; 替换为 $time = (time() + 80) - $time; 暂时解决了这个问题,我将当前数字 80 增加 5 或 10 以使其准确。

简短说明: 在计算要产生的差异时,我必须在当前时间上加上几秒,这样它就不会只回显“[SPACE]ago”。

当我更新页面上的状态时,我正在使用此功能,插入查询被发送到数据库(没有填充时间列)并且因为时间戳列是默认的当前时间戳设置为当前时间戳。

我该如何解决这个问题,因为我真的明白每天增加 5 或 10 个在未来不会稳定。

PS。我不认为我可以重新启动我的 MySQL 服务器等,因为我在一个虚拟主机上,我想我没有这样做的正确权限。

EDIT1: 看起来插入的时间戳大约提前 1 分钟,然后在现实生活中。会不会是这个原因?

EDIT2: 现在,几周后,我需要额外增加 100 到 200 秒,以确保 time ago 函数是准确的。

【问题讨论】:

  • PHP date_diff()MySQL datediff() 有什么问题
  • 看起来它不支持 UNIX tiemstamps。我想要返回的是以秒为单位的差异。 @Anigel
  • 使用 date() 或 from_unixtime() 来解决这个问题。
  • 它看起来并没有解决它,不过感谢您的回复。我想这与时区有关。 @Anigel
  • 这就是为什么我建议 date_diff 因为它是时区感知的。你服务器上的时间对吗?

标签: php mysql unix timezone timestamp


【解决方案1】:

由于您是根据存储在 MySQL 中的内容进行区分,因此您的选择是:

  1. 在您的服务器上使用网络时间协议(请参阅http://pool.ntp.org/),并希望最好...
  2. 或者在下面的选项中使用UNIX_TIMESTAMP() 从 MySQL 中获取时间:

    • 在您的查询中:SELECT *, UNIX_TIMESTAMP() AS unix_timestamp FROM table
    • 或单独调用:SELECT UNIX_TIMESTAMP() AS unix_timestamp

一个可选的第二个参数是秘密,它可以通过将来自 MySQL 的“unix_timestamp”结果传递到第二个可选参数,编码一个优化的差异(你发现 5 到 10 '工作'),或者简单地允许 PHP 时间戳() 函数在您的服务器同步时成为上面已经实现的默认值。

无论哪种方式,都可以像这样对函数进行简单的更新:

function time_elapsed_string($time, $time0 = NULL)
{
    if ($time0 === NULL) 
    {
        $time0 = time();
    }
    $time = $time0 - $time;
    ...

示例用途:

time_elapsed_string($recordTimeStamp);                         // as-is
time_elapsed_string($recordTimeStamp, time() + 5);             // an "optimized" difference
time_elapsed_string($recordTimeStamp, $recordUnixTimeStamp);   // from MySQL addition
time_elapsed_string($recordTimeStamp, $databaseUnixTimeStamp); // from separate query

【讨论】:

  • 这几乎解决了它。非常感谢!
【解决方案2】:

我不清楚如何您将时间戳存储在 MySQL 中。在 MySQL 中存储日期至少有三种方式:

  1. 作为 int - time() 的输出
  2. 作为DATETIME
  3. 作为TIMESTAMP

前两个选项甚至没有考虑数据库服务器的时区。你甚至没有真正说出你是如何设置时区的。

假设您目前使用的是TIMESTAMP,您可以使用UNIX_TIMESTAMP(date) 函数从 MySQL 获取 unix 时间戳。

在 MySQL 服务器上设置时区设置时,通常最好在每个会话的基础上进行,如下所示:

$sql = "SET names $charset, time_zone='".date("P")."'";

问题的根源似乎是函数本身。每年并不完全有“31536000”秒,一个月也没有“2592000”秒。这是因为一个月的天数、闰年等不同。

这是基于我很久以前写的一个函数(重写它以使其更易于阅读)。希望它对你有用:

function relativeTime($time)
{
    $date = getdate($time);
    $today = getdate();
    $daydiff = $today["mday"]-$date["mday"];
    $text = "";

    if ($today["mon"] === $date["mon"] && $today["year"] === $date["year"] && $daydiff < 2) {
        if ($daydiff === 1) {
            $text =  sprintf('Yesterday, %s', date('g:i A T', $time));
        } else if ($today["hours"] == $date["hours"]) {
            $min = $today["minutes"] - $date["minutes"];
            if ($min > 1) {
                $text = sprintf('%s minutes ago', $min);
            } else {
                $text = 'A minute ago';
            }
        } else {
            $text = sprintf('Today, %s', date('g:i A T', $time));
        }
    } else {
        $text = date('j F Y - g:i A T', $time);
    }
    return $text;
}

上面的函数只执行“# 分钟前”和“今天”、“昨天”,然后是日期。当然,您可以添加它以使其更加精确。

【讨论】:

  • 我在表中有一个字段,其类型设置为 TIMESTAMP,默认值为 CURRENT_TIMESTAMP。您的功能看起来不错,但我认为我的功能没有任何问题。将行插入数据库时​​似乎会出现问题。然后时间戳被设置为未来时间(目前大约比当前时间提前 150 分钟)。这一定与数据库服务器和 php 服务器之间的时区有关。非常感谢您的回答!
  • @MagnusBurton 那么数据库服务器上的日期必须不同步。您始终可以从 time() 中插入一个值。不过,您的函数使用几个月的固定时间,当一个月可能有 28、29、30 或 31 天时,您最终会出错。
【解决方案3】:

当您插入数据时,不要使用 MySQL CURRENT_TIMESTAMP,如果使用 php 从 time() 计算它,它们将匹配,并且您将没有提前 60 秒的发布时间。当您以这种方式插入时,您始终可以使用 time()-1,总会有至少 1 秒的差异。

要从 date 获取 mysql DATETIME 格式,只需使用它,然后将列的值指定为 $date。

$date = date('Y-m-d H:i:s', (time()-1));

在大多数托管环境中,sql server 和 web 服务器是不同的服务器,因此它们之间可能会有一些时间偏差,但肯定不应该在一分钟左右,任何值得他们盐分的 web 主机都会确保它们是全部与同一时间服务器同步,因此任何漂移都应以微秒为单位进行测量

【讨论】:

    【解决方案4】:

    您遇到的问题是,如果 $time 小于 1,那么它的面额就没有小值了。

    我用 if 语句修复了它,还重命名了一个变量

    function time_elapsed_string($time)
    {
        $time_diff = time() - $time;
    
        $tokens = array(
            31536000 => 'year',
            2592000 => 'month',
            604800 => 'week',
            86400 => 'day',
            3600 => 'hour',
            60 => 'minute',
            1 => 'second',
        );
    
        if ($time_diff < 1)
            return '0 seconds';
    
        foreach($tokens as $unit => $text)
        {
            if($time_diff < $unit) continue;
            $numberOfUnits = floor($time_diff / $unit);
            return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'');
        }
    }
    

    您还可以使用 PHP 文档中的以下内容: 来源:http://php.net/manual/en/function.time.php

    以人类可读的形式获取时间流逝的两种快速方法。

    function time_elapsed_A($secs){
        $bit = array(
            'y' => $secs / 31556926 % 12,
            'w' => $secs / 604800 % 52,
            'd' => $secs / 86400 % 7,
            'h' => $secs / 3600 % 24,
            'm' => $secs / 60 % 60,
            's' => $secs % 60
            );
    
        foreach($bit as $k => $v)
            if($v > 0)$ret[] = $v . $k;
    
        return join(' ', $ret);
        }
    
    
    function time_elapsed_B($secs){
        $bit = array(
            ' year'        => $secs / 31556926 % 12,
            ' week'        => $secs / 604800 % 52,
            ' day'        => $secs / 86400 % 7,
            ' hour'        => $secs / 3600 % 24,
            ' minute'    => $secs / 60 % 60,
            ' second'    => $secs % 60
            );
    
        foreach($bit as $k => $v){
            if($v > 1)$ret[] = $v . $k . 's';
            if($v == 1)$ret[] = $v . $k;
            }
        array_splice($ret, count($ret)-1, 0, 'and');
        $ret[] = 'ago.';
    
        return join(' ', $ret);
        }
    
    
    
    
    $nowtime = time();
    $oldtime = 1335939007;
    
    echo "time_elapsed_A: ".time_elapsed_A($nowtime-$oldtime)."\n";
    echo "time_elapsed_B: ".time_elapsed_B($nowtime-$oldtime)."\n";
    
    /** Output:
    time_elapsed_A: 6d 15h 48m 19s
    time_elapsed_B: 6 days 15 hours 48 minutes and 19 seconds ago.
    **/
    

    【讨论】:

    • 我认为您没有理解我的问题,或者我可能还不够清楚,抱歉!我需要一种将 PHP 服务器和 MySQL 服务器同步到秒的方法,因为它们目前彼此不同,大约需要 2 分钟。将新记录插入数据库时​​,与 PHP 服务器相比,时间设置为未来。不过感谢您的功能!
    【解决方案5】:

    你写道:

    " 但是,如果我将当前时间作为 $time 参数,它会回显 '[SPACE]ago'。"

    所以在这种情况下,您的 $time 参数将等于 time()。 在你的函数体中这一行:

    $time = time() - $time;
    

    实际上是 $time = time() - time(); 这是 0,因为两次 time() 调用之间不会经过 1 秒(通常)。

    我认为如果你使用 php DateTime 对象会更好。

    $now = new DateTime();
    
    $timeObject = new DateTime();
    $timeObject->setTimestamp($time);
    
    // DateInterval object created by DateTime::diff()
    $diff = $now->diff($timeObject);
    
    // Number of years.
    echo $diff->y;
    
    // Number of months.
    echo $diff->m;
    
    // Number of days.
    echo $diff->d;
    
    // Number of hours.
    echo $diff->h;
    
    // Number of minutes.
    echo $diff->i;
    
    // Number of seconds.
    echo $diff->s;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-27
      • 2013-11-13
      • 1970-01-01
      • 2011-03-16
      相关资源
      最近更新 更多