【问题标题】:timestampTz fields in LaravelLaravel 中的 timestampTz 字段
【发布时间】:2018-09-24 23:59:22
【问题描述】:

Laravel 5.4 在迁移中支持 Postgres TIMESTAMP WITH TIME ZONE 字段类型:

$table->timestampTz('scheduled_for');

Laravel 可以设置为将日期字段(DATEDATETIMETIMESTAMP)转换为 Carbon 对象(默认情况下,created_atupdated_atTIMESTAMP 字段会这样做),但是将scheduled_for 放入$dates 字段会导致时区感知版本出错:

InvalidArgumentException with message 'Trailing data'

查看数据库和修补程序,该字段的值似乎类似于2017-06-19 19:19:19-04。是否有从这些字段类型之一中获取 Carbon 对象的本机方法?还是我使用访问器卡住了?

【问题讨论】:

    标签: laravel php-carbon timestamp-with-timezone


    【解决方案1】:

    复活这个问题,希望有一个有用的答案被接受。

    Laravel 采用 Y-m-d H:i:s 数据库时间戳格式。如果您使用的是 Postgres timestampz 列,那显然是不同的。您需要告诉 Eloquent 如何让 Carbon 解析该格式。

    只需像这样在模型上定义 $dateFormat 属性:

    Class MyModel extends Eloquent {
    
        protected $dateFormat = 'Y-m-d H:i:sO';
    
    }
    

    信用到期:我在GitHub issue 中找到了这个解决方案

    【讨论】:

    • Neato,真优雅!
    • 如果我在同一个表/模型/上同时拥有timestamptimestampTz 列类型怎么办?
    • @Inigo 我已经有一段时间没有开始处理这个问题了——但我假设你必须写一些更广泛的东西,包括制作一个包含column -> format 映射的哈希值,然后覆盖查找$dateFormat 的方法并自行执行查找,然后返回正确/所需的值。
    • 感谢您的回复,@Jim。我最终只是将时区存储在不同的字段中,但我会记住这一点。
    【解决方案2】:

    把它放在你的模型中

    protected $casts = [
        'scheduled_for' => 'datetime'   // date | datetime | timestamp
    ];
    

    使用$dates 更可能过时,因为$casts 做同样的事情(可能除了$dateFormat 属性,它只能用于$dates 字段iirc,但我看到一些抱怨)

    编辑

    我曾经在 Laravel 5.4 上测试过 Carbon,并为它创建了一个 trait

    这还不是生产级代码,因此请将其包含在您的模型中,风险自负

    <?php namespace App\Traits;
    
    use Carbon\Carbon;
    
    trait castTrait
    {
        protected function castAttribute($key, $value)
        {
            $database_format        = 'Y-m-d H:i:se';   // Store this somewhere in config files
            $output_format_date     = 'd/m/Y';          // Store this somewhere in config files
            $output_format_datetime = 'd/m/Y H:i:s';    // Store this somewhere in config files
    
            if (is_null($value)) {
                return $value;
            }
    
            switch ($this->getCastType($key)) {
                case 'int':
                case 'integer':
                    return (int) $value;
                case 'real':
                case 'float':
                case 'double':
                    return (float) $value;
                case 'string':
                    return (string) $value;
                case 'bool':
                case 'boolean':
                    return (bool) $value;
                case 'object':
                    return $this->fromJson($value, true);
                case 'array':
                case 'json':
                    return $this->fromJson($value);
                case 'collection':
                    return new BaseCollection($this->fromJson($value));
                case 'date':
                    Carbon::setToStringFormat($output_format_date);
                    $date = (string)$this->asDate($value);
                    Carbon::resetToStringFormat();  // Just for sure
                    return $date;
                case 'datetime':
                    Carbon::setToStringFormat($output_format_datetime);
                    $datetime = (string)$this->asDateTime($value);
                    Carbon::resetToStringFormat();
                    return $datetime;
                case 'timestamp':
                    return $this->asTimestamp($value);
                default:
                    return $value;
            }
        }
    
        /**
         * Return a timestamp as DateTime object with time set to 00:00:00.
         *
         * @param  mixed  $value
         * @return \Carbon\Carbon
         */
        protected function asDate($value)
        {
            return $this->asDateTime($value)->startOfDay();
        }
    
        /**
         * Return a timestamp as DateTime object.
         *
         * @param  mixed  $value
         * @return \Carbon\Carbon
         */
        protected function asDateTime($value)
        {
            $carbon = null;
            $database_format = [ // This variable should also be in config file
                'datetime'  => 'Y-m-d H:i:se',      // e -timezone
                'date'      => 'Y-m-d'
            ];
    
            if(empty($value)) {
                return null;
            }
    
            // If this value is already a Carbon instance, we shall just return it as is.
            // This prevents us having to re-instantiate a Carbon instance when we know
            // it already is one, which wouldn't be fulfilled by the DateTime check.
            if ($value instanceof Carbon) {
                $carbon = $value;
            }
    
             // If the value is already a DateTime instance, we will just skip the rest of
             // these checks since they will be a waste of time, and hinder performance
             // when checking the field. We will just return the DateTime right away.
            if ($value instanceof DateTimeInterface) {
                $carbon = new Carbon(
                    $value->format($database_format['datetime'], $value->getTimezone())
                );
            }
    
            // If this value is an integer, we will assume it is a UNIX timestamp's value
            // and format a Carbon object from this timestamp. This allows flexibility
            // when defining your date fields as they might be UNIX timestamps here.
            if (is_numeric($value)) {
                $carbon = Carbon::createFromTimestamp($value);
            }
    
            // If the value is in simply year, month, day format, we will instantiate the
            // Carbon instances from that format. Again, this provides for simple date
            // fields on the database, while still supporting Carbonized conversion.
            if ($this->isStandardDateFormat($value)) {
                $carbon = Carbon::createFromFormat($database_format['date'], $value)->startOfDay();
            }
    
            // Finally, we will just assume this date is in the format used by default on
            // the database connection and use that format to create the Carbon object
            // that is returned back out to the developers after we convert it here.
            $carbon = Carbon::createFromFormat(
                $database_format['datetime'], $value
            );
    
            return $carbon;
        }
    }
    

    【讨论】:

    • $casts 似乎不起作用。它似乎仍然通过 Carbon 运行它(不成功)。
    • 原因是来自数据库的格式错误。 Laravel 正在尝试使用不正确的格式掩码解析您的输入日期时间字符串
    • 鉴于 Laravel 在迁移中支持timestampTz,它不能真正被称为“错误”格式。到目前为止,Laravel 似乎并没有完全支持它,因此我的问题是——在迁移中实现它似乎很奇怪,但它在实际代码中并不特别有用。
    • 我更新了我的答案,你可以测试我提供的特质
    • 我倾向于只使用timestamp 字段并将scheduled_tz 作为字符串存储在旁边,这样我就可以使用public function getScheduledForAttribute($value) { return Carbon::parse($value)-&gt;setTimezone($this-&gt;scheduled_tz); } 之类的访问器
    猜你喜欢
    • 2021-03-18
    • 1970-01-01
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    • 2018-06-29
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多