I was wondering if this was possible, so let's say I have a model like so:
MyModel
SomeDate - Carbon
Now, I also have a timezone for the current user like so:
User
MyTimezone
the timezones stored in the database are always stored in UTC format (to ensure everything is consistent), and the outputted dates should always be formatted to a specific Timezone (but timezone differs per user), for example America/Chicago for User1 and America/Denver for User2.
Is there a way to automatically format the timezones per Carbon instance to a given one before outputting, or will I have to loop through the collection and set each one accordingly?
Setting app.timezone
doesn't work because it also causes Carbon instances to be saved to the database in the app.timezone
timezone, whereas all dates in the database should be in UTC, therefore I lose consistency.
I currently have app.timezone
set to UTC in the App config but I'm also forced to convert all Carbon instances to the correct timezone before outputting. Is there a better way, maybe by trapping execution before Carbon gets turned into a string and doing it there?
EDIT:
Things i've tried:
Override 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;
}
overriding 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);
}
Running into the same issue for my application where remote websites would store dates in UTC and I'd have to show the actual dates based on the logged in user, I came up with overriding the Laravel Eloquent Model.
Just extend the Illuminate\Database\Eloquent\Model
, like so:
<?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;
}
}
Perhaps this is useful for others stumbling upon this question.
As a reference
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With