Search code examples
phppostgresqlyii2timestamp-with-timezone

Users' timezone issue onYii2 with PostgreSQL


I would like to ask for a better approach. I am using Yii2 with PostgreSQL, and set all timestamp fields for all table to timestamp with timezone data type.

timestamp with time zone NOT NULL DEFAULT now()

I have created setting which will be called for every response,

Yii::$app->setTimezone(MallSettings::getSetting('timezone', Yii::$app->session['mall_id']));

which will return something like Asia/Makassar or Asia/Jakarta (it depends on user's setting).

I extend the \yii\18n\Formatter, using a class named ZeedFormatter below, and make a new function to display the datetime value in user's preferred timezone.

class ZeedFormatter extends \yii\i18n\Formatter
{
    /**
     * Format timestamp with timezone value to user's preferred timezone.
     * (extends the asDatetime() functionality)
     * 
     * @param  [type] $value  [description]
     * @param  [type] $format [description]
     * @return [type]         [description]
     */
    public function asLocaldatetime($value, $format = null)
    {
        $originalDateTime = \DateTime::createFromFormat('Y-m-d H:i:s.uO', $value);
        $originalDateTime->setTimezone(new \DateTimeZone(date_default_timezone_get()));

        if ($format === null)
            $format = 'Y-m-d H:i:s.uO';

        $localDateTime = $originalDateTime->format($format);

        return $localDateTime;

    }
}

The idea is that whenever I need a 'local' datetime format, I can call it like so:

    Yii::$app->formatter->asLocaldatetime('2019-08-29 19:52:21.02886+07');
    // will return 2019-08-29 20:52:21.02886+08 for Asia/Makassar timezone

Or, if used inside a GridView,


       'position',
       'created_at:localdatetime',

       ['class' => 'backend\widgets\ActionColumn'],

Can I get this functionality only using Yii2's feature? Or (another option) is calling created_at AT TIME ZONE 'Asia/Makassar' from the Psql query? I hope I can get an enlightment. Thank you.


Solution

  • My (another) approach is to keep using the asDatetime() function.

    I just realized that I need to remove / comment the timezone from config file. If the value is set there, no matter how I set the timezone at other place, Yii always gets the value from the config file (common/config/main.php) if we are using the asDatetime formatter.

        'components' => [
            'formatter' => [
                // 'class' => '\common\modules\ZeedFormatter',
                'locale' => 'id-ID',
                // 'timeZone' => 'Asia/Jakarta',
                'defaultTimeZone' => 'Asia/Jakarta',
                'dateFormat' => 'php:j M Y',
                'decimalSeparator' => ',',
                'thousandSeparator' => '.',
                'currencyCode' => 'Rp ',
                'nullDisplay' => '<em style="color:#d8d8d8">null</em>',
                'numberFormatterOptions' => [
                    NumberFormatter::MIN_FRACTION_DIGITS => 0,
                    NumberFormatter::MAX_FRACTION_DIGITS => 0,
                ],
            ],
        ]
    

    If we need another (or customized) format, we can still use the ZeedFormatter as above and add some desired functions. For example :

        public function asMyformat($value)
        {
            $timestamp = strtotime($value);
            $fmt = new \IntlDateFormatter('id-ID', \IntlDateFormatter::NONE, \IntlDateFormatter::LONG);
    
            $fmt->setPattern('hh:mm z');// will return something like '09:47 WITA' 
    
            return $fmt->format($timestamp);
        }