Laravel 时间戳显示毫秒

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31227069/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 11:52:53  来源:igfitidea点击:

Laravel timestamps to show milliseconds

phpmysqllaravellaravel-5

提问by raphadko

I need to store updated_at timestamp with high precision on a laravel application, using the format "m-d-Y H:i:s.u"(including milisseconds)

我需要在 laravel 应用程序上以高精度存储 updated_at 时间戳,使用格式“mdY H:i:su”(包括毫秒)

According to laravel documentation, I can customize the date format by setting the $dateFormat property on a class, but...

根据 laravel 文档,我可以通过在类上设置 $dateFormat 属性来自定义日期格式,但是...

The main problem is that Laravel's schema builder adds a column of type timestamp in the database when I use $table->nullableTimestamps()And according to mysql documentation, columns of type TIMESTAMP only allow the precision up to seconds..

主要问题是,当我使用$table->nullableTimestamps()时,Laravel 的架构构建器在数据库中添加了一个时间戳类型的列,并且根据 mysql 文档,TIMESTAMP 类型的列只允许精度高达秒..

Any ideas on how I could achieve that?

关于我如何实现这一目标的任何想法?

回答by malhal

You can't because the PHP PDO driver doesn't support fractional seconds in timestamps. A work around is to select the timestamp as a string instead so the PDO driver doesn't know its really a timestamp, simply by doing $query->selectRaw(DB::raw("CONCAT(my_date_column) as my_date_column"))however this means you can't use the default select for all fields so querying becomes a real pain. Also you need to override getDateFormat on the model.

不能,因为 PHP PDO 驱动程序不支持时间戳中的小数秒。一种解决方法是选择时间戳作为字符串,这样 PDO 驱动程序不知道它真的是时间戳,只是这样做$query->selectRaw(DB::raw("CONCAT(my_date_column) as my_date_column"))意味着您不能对所有字段使用默认选择,因此查询变得非常痛苦。您还需要覆盖模型上的 getDateFormat 。

// override to include micro seconds when dates are put into mysql.
protected function getDateFormat()
{
    return 'Y-m-d H:i:s.u';
}

Finally in your migration rather than nullableTimestamps, outside of the Schema callback do:

最后在您的迁移而不是 nullableTimestamps 中,在 Schema 回调之外执行:

DB::statement("ALTER TABLE `$tableName` ADD COLUMN created_at TIMESTAMP(3) NULL");

Note this example was for 3 decimal places however you can have up to 6 if you like, by changing the 3 to a 6 in two places, in the alter table and in the sprintf and also adjusting the multiplier * 1000 to 1000000 for 6.

请注意,此示例为 3 个小数位,但是如果您愿意,您最多可以有 6 个,通过在更改表和 sprintf 中的两个位置将 3 更改为 6,并将乘数 * 1000 调整为 1000000 为 6。

Hopefully some day PHP PDO will be updated to fix this, but its been over 5 years and nothings changed so I don't have my hopes up. In case you are interested in the details, see this bug report: http://grokbase.com/t/php/php-bugs/11524dvh68/php-bug-bug-54648-new-pdo-forces-format-of-datetime-fieldsI found that link in this other answer which might help you more understand the issue: https://stackoverflow.com/a/22990991/259521

希望有一天 PHP PDO 会被更新来解决这个问题,但它已经超过 5 年了,没有任何改变,所以我不抱希望。如果您对详细信息感兴趣,请参阅此错误报告:http: //grokbase.com/t/php/php-bugs/11524dvh68/php-bug-bug-54648-new-pdo-forces-format-of- datetime-fields我在另一个答案中找到了这个链接,它可能会帮助您更了解这个问题:https: //stackoverflow.com/a/22990991/259521

PHP is really showing its age lately, and I would consider this issue one of my reasons for considering moving to the more modern Node.js.

PHP 最近真的很老了,我认为这个问题是我考虑迁移到更现代的 Node.js 的原因之一。

回答by Zack Morris

Based on malhal's answer, I was able to get fractional timestamp reading to work here. Pasting the answer here for convenience:

根据马尔哈尔的回答,我能够在这里获得分数时间戳读数。为方便起见,将答案粘贴在此处:

class BaseModel extends Model
{
    protected $dateFormat = 'Y-m-d\TH:i:s.u';

    protected function asDateTime($value)
    {
        try {
            return parent::asDateTime($value);
        } catch (\InvalidArgumentException $e) {
            return parent::asDateTime(new \DateTimeImmutable($value));
        }
    }

    public function newQuery()
    {
        $query = parent::newQuery();

        if($this->usesTimestamps()) {
            $table = $this->getTable();
            $column = $this->getDeletedAtColumn();

            $query->addSelect(DB::raw("concat($table.$column) as $column"));
        }

        return $query;
    }
}

There is a lot going on here because it gets the query with scopes applied and then adds a select for the updated_at column to the end, which overwrites any previously loaded updated_at column later while Laravel hydrates the Model from the query. For being an ugly hack, it worked surprisingly well the very first time.

这里发生了很多事情,因为它获取应用了作用域的查询,然后在最后添加了一个对 updated_at 列的选择,这会在 Laravel 从查询中混合模型时覆盖任何先前加载的 updated_at 列。作为一个丑陋的黑客,它第一次运行得非常好。

Timestamps are stored internally as Carbonin Laravel:

时间戳在 Laravel 中作为Carbon内部存储:

dd(MyModel->first()->updated_at->format('Y-m-d H:i:s.u'));

Output:

输出:

2017-04-14 22:37:47.426131

Also be sure to run a migration to convert your columns to fractional timestamps. Microsecond precision raises the size of timestamps from 4 bytes to 7 bytes but this is 2017, don't let saving a byte or two by choosing millisecond precision cost you a fortune later when you find yourself serving stale cache entries:

还要确保运行迁移以将您的列转换为小数时间戳。微秒精度将时间戳的大小从 4 个字节增加到 7 个字节,但这是 2017 年,当您发现自己提供过时的缓存条目时,不要让通过选择毫秒精度来节省一两个字节会让您损失一大笔钱:

\DB::statement("ALTER TABLE my_table MODIFY updated_at TIMESTAMP(6) NULL DEFAULT NULL");

Sadly I haven't found a way to modify the migration schema timestamps()function to do this.

遗憾的是,我还没有找到修改迁移模式timestamps()功能的方法来执行此操作。