Временные метки Laravel для отображения миллисекунд

Мне нужно сохранить метку timestamp updated_at с высокой точностью в приложении laravel, используя формат "m-d-Y H: i: s.u" (включая milisseconds)

Согласно документации laravel, я могу настроить формат даты, установив свойство $dateFormat в классе, но...

Основная проблема заключается в том, что построитель схем Laravel добавляет столбец типа timestamp в базу данных, когда я использую $table- > nullableTimestamps(). И согласно документации mysql, столбцы типа TIMESTAMP разрешают только точность до нескольких секунд.

Любые идеи о том, как я могу это достичь?

Ответ 1

Вы не можете, потому что драйвер PHP PDO не поддерживает доли секунды в метках времени. $query->selectRaw(DB::raw("CONCAT(my_date_column) as my_date_column")) - вместо этого выбрать метку времени в виде строки, чтобы драйвер 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, вне обратного вызова Schema выполните:

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

Обратите внимание, что этот пример был для 3 десятичных разрядов, однако вы можете иметь до 6, если хотите, изменив 3 на 6 в двух местах, в таблице изменения и в sprintf, а также скорректировав множитель * 1000 до 1000000 для 6.

Надеюсь, когда-нибудь PHP PDO будет обновлен, чтобы это исправить, но прошло уже более 5 лет, и ничего не изменилось, поэтому у меня не осталось надежд. Если вас интересуют подробности, см. Этот отчет об ошибке: http://grokbase.com/t/php/php-bugs/11524dvh68/php-bug-bug-54648-new-pdo-forces-format-of- Поля datetime Я нашел эту ссылку в этом другом ответе, который может помочь вам лучше понять проблему: fooobar.com/questions/412013/...

В последнее время PHP действительно показывает свой возраст, и я считаю эту проблему одной из причин, по которой стоит перейти на более современный Node.js.

Ответ 2

Основываясь на malhal answer, я смог получить дробную метку времени для работы здесь. Вставка ответа здесь для удобства:

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

Здесь много чего происходит, потому что он получает запрос с примененными областями, а затем добавляет конец для столбца updated_at в конец, который позже перезаписывает любой ранее загруженный столбец updated_at, в то время как Laravel увлажняет модель из запроса. Для того, чтобы быть уродливым взломом, он работал удивительно хорошо в первый раз.

Временные метки хранятся внутри Carbon в Laravel:

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

Вывод:

2017-04-14 22:37:47.426131

Также не забудьте выполнить миграцию, чтобы преобразовать ваши столбцы в дробные временные метки. Точность Microsecond повышает размер временных меток с 4 байтов до 7 байтов, но это 2017 год, не позволяйте сохранять байты или два, выбирая миллисекундную точность, стоить вам удачу позже, когда вы обнаружите, что используете устаревшие записи кэша:

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

К сожалению, я не нашел способ изменить функцию схемы миграции timestamps(), чтобы сделать это.