Laravel 时间戳显示毫秒

问题描述 投票:0回答:3

我需要在 Laravel 应用程序上以高精度存储 Updated_at 时间戳,使用格式 “m-d-Y H:i:s.u”(包括毫秒)

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

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

关于如何实现这一目标有什么想法吗?

php mysql laravel laravel-5
3个回答
17
投票

不能,因为 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';
}

最后在迁移中而不是 nullableTimestamps 中,在架构回调之外执行以下操作:

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

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

希望有一天 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 最近确实显示出了它的年龄,我认为这个问题是我考虑转向更现代的 Node.js 的原因之一。


6
投票

根据malhal的回答,我能够在这里获取小数时间戳读数。为了方便起见,将答案粘贴在这里:

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;
    }
}

这里发生了很多事情,因为它获取应用了范围的查询,然后在末尾添加 update_at 列的选择,这会覆盖任何先前加载的 Updated_at 列,同时 Laravel 从查询中水合模型。虽然是一个丑陋的黑客,但它第一次的效果却出奇的好。

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

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

输出:

2017-04-14 22:37:47.426131

另请务必运行迁移以将列转换为小数时间戳。微秒精度将时间戳的大小从 4 字节提高到 7 字节,但这是 2017 年,当您发现自己正在提供陈旧的缓存条目时,不要因为选择毫秒精度而节省一两个字节,从而让您付出巨大代价:

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

遗憾的是,我还没有找到修改迁移模式

timestamps()
函数来执行此操作的方法。


0
投票

Laravel 7 (3/2020) 中添加了对小数秒的支持。这是一个示例,我们假设我们希望假设的用户模型上的

created_at
updated_at
banned_at
具有微秒精度:

在迁移的 up() 方法中:

public function up(): void
{
    Schema::table('users', function (Blueprint $table) {
        // other columns
        $table->timestamp('created_at', 6);
        $table->timestamp('updated_at', 6);
        $table->timestamp('banned_at', 6)->nullable();
    });
}

然后在用户模型中:

class User extends Model
{
    protected $dateFormat = 'Y-m-d H:i:s.u';
    protected $casts = [
        'banned_at' => 'datetime:Y-m-d H:i:s.u',
    ];
}

如果您需要更改现有时间戳,您可以创建迁移并执行以下操作:

    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->timestamp('banned_at', 6)->nullable()->change();
        });
    }

用途:

dd(User::first(1)->banned_at->format('Y-m-d H:i:s.u'));

输出:

"2023-12-02 06:52:46.549267"
© www.soinside.com 2019 - 2024. All rights reserved.