Users' timezone issue onYii2 with PostgreSQL - php

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.

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

Related

How to define globally serialization date in lavarel?

Something good with laravel is "$casts" attribute. Exemple with dates :
protected $casts = [
'date_rec' => 'datetime:Y-m-d',
'date_liv' => 'datetime:Y-m-d',
];
It works well if you return result of eloquent's query.
But if you don't want return full object with all relations it's the hell.
Examples :
protected $casts = [
'date_rec' => 'datetime:Y-m-d',
'date_liv' => 'datetime:Y-m-d',
];
public function asListItem()
{
return [
"try_1" => $this->date_rec, // return : "2021-10-19T22:00:00.000000Z"
"try_2" => $this->date_rec->format('Y-m-d'), // return : "2021-10-19" or error/crash if date is null
"try_3" => substr($this->date_rec ?? '', 0, 10), // work always but boring
"try_4" => json_encode($this->date_rec) // infinite loading and then error timeout
];
}
Is it possible to define how I want laravel parse date globally at serialization ?
Thx
You can use Carbon::setToStringFormat('your format'); in your provider to set a default string format

Laravel 8 with Backpack - datetime formatting on the frontend?

I am currently trying to format a datetime object that I access like this:
Blade:
{{ $event->start }} and {{ $event->end}}
this outputs something like this on the frontend blades:
2021-12-02 22:30:00 ($event->start) 2021-2021-12-02 23:00:00 ($event->end)
the formatting above is also how it is stored as a DATETIME object in the database.
Because I use other elements, like a fullcalendar, I dont want to change the way the database stores the dates, just formatting the dates on the frontend/controller directly.
Controller:
if ($course == 'course') {
$view = 'pages.course.current_course';
$id = '8';
}
// get the course data from the database
$events = DB::table('eventaries')
// map the current view $id to the database query
->where('category', $id)
// check if event is expired
->where('start', '>', now())
->get();
// pass through the data to the correct views
return view($view, [
"events" => $events
]);
But I need the following formatting: Sunday. 12th December 2021, 22:30 ($event->start) and 23:00 ($event->end)
I already got the formatting in the BackPack Backend right by adding 'format' => to the CrudController, like this:
CrudController:
CRUD::addColumn([
'name' => 'start',
'label' => 'Start',
'type' => 'datetime',
'format' => 'DD.MM.Y - H:mm',
]);
CRUD::addColumn([
'name' => 'end',
'label' => 'End',
'type' => 'datetime',
'format' => 'DD.MM.Y - H:mm',
]);
If you have Model defined for that particular table. you can define accessor for those two column, like
public function getStartAttribute($value)
{
return Carbon::parse($value)->toDayDateTimeString();
}
It will return output something like Mon, Nov 29, 2021 05:45 PM
Define these type of function for end column.
Refer this Carbon Document for other format and date operation
If you don't have Model you can use Carbon class in blade or controller directly
We will use createFromFormat() and format(), createFromFormat() will take two argument first give format of date and second one date and format() take one argument give formate as you want. you can see bellow examples:
Example 1:
$post->created_at->format('d-m-Y');
Example 2:
\Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $date)->format('d-m-Y')

Yii2: display dates in user time zone

In my DB I store all datetime fields in UTC format. Also, I have the ability to change default time zone by users. Each user can have own time zone, different from UTC.
How shall I display all model datetime fields in this case?
I have an idea. To do this action for each ActiveRecord model:
public function init()
{
parent::init();
$this->on(ActiveRecord::EVENT_AFTER_FIND, function($event) {
$this->created_date = (new \DateTime('now', new \DateTimeZone("Europe/Kiev")))->format("Y-m-d H:i:s");
});
}
But I'm not sure it's the best way for big amount of models...
if the dates are stored in UTC why not append the string UTC along the time and display it
$time = strtotime($this->created_at.' UTC');
date("Y-m-d H:i:s", $time);
Your code will look like this
public function init()
{
parent::init();
$this->on(ActiveRecord::EVENT_AFTER_FIND, function($event) {
$time = strtotime($model->create_at.' UTC');
$this->created_date = date("Y-m-d H:i:s", $time);
});
}
if I would do it I would just create a separate Helper and use it to display the date in the local format rather than EVENT_AFTER_FIND
Another alternative is to use this Extension
Search for frontend/config/main.php
Try putting in
return [
...
'components' => [
...
a FORMATTER part like
'formatter' => [
'dateFormat' => 'dd/MM/yyyy',
'datetimeFormat' => 'dd/MM/yyyy H:i:s',
'timeFormat' => 'H:i:s',
'locale' => 'it-IT',
'decimalSeparator' => ',',
'thousandSeparator' => '.',
'currencyCode' => 'EUR',
'numberFormatterSymbols' => [
NumberFormatter::CURRENCY_SYMBOL => '€',
],
'timeZone' => 'Europe/Rome',
],
Set your parameters like TimeZone, Currency, etc...
NB: I dont remember but maybe the NumberFormatter part need some other setup so delete the numberFormatterSymbols part if it give to you an error

How to validate array of date that have to be inside two other date relative to a model in Laravel 51

I'm using boostrap-datepicker, with "multiple-date" option activated.
My dates looks like this: [date1, date2, ...].
I'm also using an hidden input to retrieve an "id". This "id" help me to retrieve "startdate" and "enddate" inside my model.
So I'm looking for a validation rule who can tell me if my dates are valid and if they are inside two other dates that correspond to the "id" of my model.
For now my id and dates rules are like this:
$rules = [
'id' => 'required|numeric|exists:event,id,isactive,1',
'dates' => 'array'
]
You can do this in very simple manner like this
Here is the documentation about writing rules for before and after
protected $rules = array(
'afterdate' => 'after:'.$yourDateForm,
'beforedate' => 'before:'.$yourDateTo
);
Note :
You shall also extend your date validation like this
'start_date' => 'required|date|after:tomorrow'
'finish_date' => 'required|date|after:start_date'
Update :
As the OP wants to get the date from db,
You shall do like this
$yourStartDate = Call to get Start Date from Table;
$yourEndDate = Call to get End Date from Table;
and the rule shall be
$validator = Validator::make(
array('date' => $yourStartDate),
array('date' => 'after:'.$yourEndDate)
);

log4php and timezone

Does anyone know if it is possible to set timezone in the log4php library configuration?
I did not see any information regarding this in the official docs, but log4j has this implemented.
Right now, I am relying on php's *date_default_timezone_set* function to do the trick, but I wanted to leave log4php to handle this on its own... I wonder if there is a to-do list for this or we are supposed to rely on the built-in function by ourselves.
Here is the code I have:
date_default_timezone_set("America/New_York");
require_once (dirname(__FILE__) . '/lib/log4php/Logger.php');
Logger::configure(
array(
'appenders' => array(
'default' => array(
'class' => 'LoggerAppenderRollingFile',
'layout' => array(
'class' => 'LoggerLayoutPattern',
'params' => array(
'conversionPattern' => '%d{Y-m-d H:i:s.u} [%t] %-5p - %m%n'
)
),
'params' => array(
'file' => '/var/log/myapp/myapp.' . date('Y-m-d') . '.log',
'maxFileSize' => '1MB',
'maxBackupIndex' => 10,
),
),
),
'rootLogger' => array(
'appenders' => array('default'),
),
)
);
$logger = Logger::getLogger('myapp');
for( $i=0; $i<5000; $i++ ) {
$logger->info("This is a test [${i}].");
}
In case this code serves someone else with similar issue.
Be safe,
A defined default timezone belongs to a fully working php application.
Apart from that, you are not very clear in stating what you expect log4php to do. The project has everything from mailing list to issue tracker - you are welcome to send your wishes.
I had a similar problem. You can log the date and time for UTC/GMT in the log4php by changing one line of code in the module.
Here is how I did. Go to the module and find the file LoggerPatternConverterDate.php.
cd log4php/pattern/
vim LoggerPatternConverterDate.php
Find the private function date($format, $utimestamp) (Line 84 for me) and change the line of code that returns.
This:
return date(preg_replace('`(?<!\\\\)u`', $ms, $format), $timestamp);
Becomes:
return gmdate(preg_replace('`(?<!\\\\)u`', $ms, $format), $timestamp);
Also, find the file: log4php/appenders/LoggerAppenderDailyFile.php and change the following line:
This:
return date($this->datePattern, $timestamp);
Becomes:
return gmdate($this->datePattern, $timestamp);
NOTE: The only thing changed is the function used to format the date string. date() depends on timezone that you set using date_default_timezone_set whereas gmdate() formats the date and time in UTC/GMT irrespective of default timezone.

Categories