You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel模型实现EPOCH时间转换为用户所属时区的问题

问题根因

你的方案不生效核心是3个问题:

  • scan()方法返回的是原始数组而非File模型实例,访问器仅在模型实例读取属性时生效,原始数组不会触发访问器逻辑
  • 字段名是大写EPOCH,和访问器getEpochAttribute命名不匹配,Laravel默认蛇形属性转换规则下会识别为小写字段,大小写敏感的ORM包会直接跳过访问器
  • 时区查询逻辑写在访问器里,每次调用都要查询3次数据库,性能损耗极大

修复方案

步骤1:修改查询逻辑,将scan结果转为模型实例

// 原代码直接返回原始数组,不会触发模型的属性转换
// $collection = collect(File::filterBetween('EPOCH', $date)->filter('SUI','=',$asset->mac_id)->scan());

// 替换为手动将原始结果转为模型实例
$rawResults = File::filterBetween('EPOCH', $date)->filter('SUI','=',$asset->mac_id)->scan();
$collection = collect($rawResults)->map(fn($item) => new File($item));

步骤2:用自定义Cast实现时区转换(比访问器更规范、可复用)

首先创建自定义Cast类app/Casts/UserTimezoneDatetime.php:

<?php
namespace App\Casts;

use Carbon\Carbon;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Facades\Auth;
use App\Models\ExtraField;
use App\Models\ExtraFieldValue;

class UserTimezoneDatetime implements CastsAttributes
{
    // 缓存当前请求的用户时区,避免重复查询数据库
    protected static ?string $userTimezone = null;

    protected function getTimezone(): string
    {
        if (self::$userTimezone) {
            return self::$userTimezone;
        }

        $user = Auth::user();
        if (!$user) {
            return self::$userTimezone = 'America/Sao_Paulo';
        }

        $extraField = ExtraField::where('description', 'TimeZone')->first();
        if (!$extraField) {
            return self::$userTimezone = 'America/Sao_Paulo';
        }

        $timezoneValue = ExtraFieldValue::where('extra_field_id', $extraField->id)
            ->where('user_id', $user->id)
            ->value('value');

        return self::$userTimezone = $timezoneValue ?: 'America/Sao_Paulo';
    }

    // 读取属性时的转换逻辑
    public function get($model, string $key, $value, array $attributes)
    {
        if (is_null($value)) {
            return null;
        }
        // EPOCH为秒级时间戳直接转Carbon实例,存储统一用UTC避免混乱
        $date = is_numeric($value) ? Carbon::createFromTimestamp($value, 'UTC') : Carbon::parse($value, 'UTC');
        return $date->setTimezone($this->getTimezone())->format('Y-m-d H:i:s');
    }

    // 写入属性时的转换逻辑,转回UTC时间戳
    public function set($model, string $key, $value, array $attributes)
    {
        if (is_null($value)) {
            return null;
        }
        return Carbon::parse($value, $this->getTimezone())->setTimezone('UTC')->getTimestamp();
    }
}

步骤3:修改File模型配置

删掉原有的getEpochAttribute访问器,替换为自定义Cast,同时关闭蛇形属性转换适配大写字段:

class File extends BaseDynamo
{
    protected $table = 'Dados';
    protected $primaryKey = 'SUI';
    protected $sortKey = ['EPOCH','desc'];
    
    // 关闭蛇形属性转换,适配大写的字段名
    public static $snakeAttributes = false;

    protected $casts = [
        'EPOCH' => \App\Casts\UserTimezoneDatetime::class,
    ];
}

验证效果

修改后执行查询即可自动获取用户时区的时间:

$my_datetime = $collection->whereIn($variable->key, [0,1])->pluck('EPOCH')->last();

内容的提问来源于stack exchange,提问作者Marcius Leandro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 09:21:02