Многократное Timezoning в Laravel / Carbon

Мне было интересно, если это было возможно, так скажем, у меня есть такая модель:

MyModel
SomeDate - Carbon

Теперь у меня также есть часовой пояс для текущего пользователя:

User
MyTimezone

часовые пояса, хранящиеся в базе данных, всегда хранятся в формате UTC (чтобы убедиться, что все согласовано), а выходные даты всегда должны быть отформатированы в определенный часовой пояс (но часовой пояс различается для каждого пользователя), например, Америка / Чикаго для пользователя1 и Америка / Денвер для User2.

Есть ли способ автоматически форматировать часовые пояса для каждого экземпляра Carbon до заданного перед выводом, или мне придется циклически просматривать коллекцию и устанавливать каждый из них соответствующим образом?

настройка app.timezone не работает, потому что это также приводит к сохранению экземпляров Carbon в базе данных в app.timezone часовой пояс, тогда как все даты в базе данных должны быть в UTC, поэтому я теряю согласованность.

У меня сейчас app.timezone установите UTC в конфигурации приложения, но я также вынужден конвертировать все экземпляры Carbon в правильный часовой пояс перед выводом. Есть ли лучший способ, возможно, отловить выполнение до того, как Carbon превратится в строку и сделает это там?

РЕДАКТИРОВАТЬ:

Вещи, которые я пробовал:

Переопределить setAttribute & GetAttribute:

public function setAttribute($property, $value) {
if ($value instanceof Carbon) {
$value->timezone = 'UTC';
}

parent::setAttribute($property, $value);
}

public function getAttribute($key) {
$stuff = parent::getAttribute($key);

if ($stuff instanceof Carbon) {
$stuff->timezone = Helper::fetchUserTimezone();
}

return $stuff;
}

переопределение asDateTime:

protected function asDateTime($value)
{
// 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.
$timezone = Helper::fetchUserTimezone();

if (is_numeric($value))
{
return Carbon::createFromTimestamp($value, $timezone);
}

// 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.
elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $value))
{
return Carbon::createFromFormat('Y-m-d', $value, $timezone)->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.
elseif ( ! $value instanceof DateTime)
{
$format = $this->getDateFormat();

return Carbon::createFromFormat($format, $value, $timezone);
}

return Carbon::instance($value);
}

5

Решение

Если я правильно понимаю, то, что вы пытаетесь достичь, это преобразовать часовой пояс из формата A в формат B и отправить его пользователю, где формат A хранится в базе данных, а формат B преобразуется в после извлечения записей из базы данных.

Вот отличный способ сделать это.

В моделях таких как User а также MyModel где необходимо преобразование, добавьте функцию в модель:

public function getConversionAttribute()
{
$conversion = Convert($this->SomeDate);
//Convert is the customized function to convert data format
//SomeDate is the original column name of dates stored in your database
return $conversion;
}

Теперь, если вы запрашиваете модель пользователя или MyModel, используя $user = User::find(1)Теперь вы можете получить конвертированную дату, обратившись к атрибуту конвертации с помощью $user->conversion,

Ура!

Однако добавленный таким образом атрибут не будет включен в преобразованный массив. Вам нужно добавить еще одну функцию в вашу модель.

public function toArray()
{
$array = parent::toArray();
//if you want to override the original attribute
$array['SomeDate'] = $this->conversion;
//if you want to keep both the original format and the current format
//use this: $array['Conversion'] = $this->conversion;
return $array;
}

Общая версия:

public function toArray() {
$array = parent::toArray();
//if you want to override the original attribute
$dates = $this->getDates();

foreach ($dates as $date) {
$local = $this->{$date}->copy();
$local->timezone = ...
$array[$date] = (string)$local;
}
//if you want to keep both the original format and the current format
//use this: $array['Conversion'] = $this->conversion;
return $array;
}
1

Другие решения

Столкнувшись с той же проблемой для моего приложения, где удаленные веб-сайты будут хранить даты в UTC, и мне нужно будет показывать фактические даты, основанные на вошедшем в систему пользователе, я решил переопределить модель Laravel Eloquent.

Просто продлите Illuminate\Database\Eloquent\Model, вот так:

<?php namespace Vendor\Package;

use Illuminate\Database\Eloquent\Model as EloquentModel;

class Model extends EloquentModel
{

/**
* Return a timestamp as a localized DateTime object.
*
* @param  mixed  $value
* @return \Carbon\Carbon
*/
protected function asDateTime($value)
{
$carbon = parent::asDateTime($value);
// only make localized if timezone is known
if(Auth::check() && Auth::user()->timezone)
{
$timezone = new DateTimeZone(Auth::user()->timezone);
// mutates the carbon object immediately
$carbon->setTimezone($timezone);
}

return $carbon;
}
/**
* Convert a localized DateTime to a normalized storable string.
*
* @param  \DateTime|int  $value
* @return string
*/
public function fromDateTime($value)
{
$save = parent::fromDateTime($value);

// only make localized if timezone is known
if(Auth::check() && Auth::user()->timezone)
{
// the format the value is saved to
$format = $this->getDateFormat();

// user timezone
$timezone = new DateTimeZone(Auth::user()->timezone);

$carbon = Carbon::createFromFormat($format, $value, $timezone);
// mutates the carbon object immediately
$carbon->setTimezone(Config::get('app.timezone'));

// now save to format
$save = $carbon->format($format);
}

return $save;
}
}

Возможно, это полезно для других, спотыкающихся в этом вопросе.

В качестве ссылки

  • Laravel 5 (2015-03-18): Освещение \ База данных \ Eloquent \ Модель: 2809-2889
  • Laravel 4.2 (2015-03-18): Подсветка \ База данных \ Eloquent \ Модель: 2583-2662
5

По вопросам рекламы [email protected]