Cakephp CakeTime DST handling - cakephp

I store all dates in UTC in my database. Cakephp runs in UTC and communicates with mysql in UTC. Now i have following scenario:
debug(CakeTime::format('Y-m-d H:i', '2013-03-22 03:00', false,
new DateTimeZone('Europe/Berlin')));
//output is 2013-03-22 04:00
debug(CakeTime::format('Y-m-d H:i', '2013-04-05 03:00', false,
new DateTimeZone('Europe/Berlin')));
//output is 2013-04-05 05:00
As you can see CakeTime added 2 hours of offset in the second example, i guess that is because it considers DST (starting at 31.03.2013).
However what i am trying to do is displaying recurring events in a calendar and this event starts each second Friday at 4am - always, even in summer. Therefore the calendar may not display it to be at 5am!
/EDIT: the first example is corret. the event has to be at 4am. but also in summer

I solved the problem using these two functions for time converting.
Keep in mind that this is for recurring events, that start always at the same time of day(e.g. 4am) regardless of DST.
public function dateTimeToSever($date, $user_timezone) {
$DateTime = new DateTime($date, $user_timezone);
$dst = $DateTime->format('I');
$toServerDateTime = CakeTime::toServer($date, $user_timezone, 'Y-m-d H:i');
if ($dst) {
$toServerDateTime = date('Y-m-d H:i', strtotime($toServerDateTime . ' + 1 Hours'));
}
return $toServerDateTime;
}
public function dateTimeToUser($date, $user_timezone) {
$DateTime = new DateTime($date, new DateTimeZone('UTC'));
$DateTime->setTimezone($user_timezone);
$dst = $DateTime->format('I');
$userDateTime = CakeTime::format('Y-m-d H:i', $date, false, $user_timezone);
if ($dst) {
$userDateTime = date('Y-m-d H:i', strtotime($userDateTime . ' - 1 Hours'));
}
return $userDateTime;
}
This should work for all timezones that use positive DST. AFAIK some timezones, e.g. somewhere in North India, have some kind of negative timezone. I guess in that case the "+ 1 Hours" has to become "- 1 Hours" and vice versa.

In your example you are getting the wrong time in both cases. If you want to store all your dates in UTC then you need to make sure you store the correct UTC values for the timezone the user is in. For example, if you want a reoccurring event every Friday at 3pm in the Europe/Berlin timezone, then do the following.
// save your date like this
$date = new DateTime('2013-04-05 03:00', new DateTimeZone('Europe/Berlin'));
$utc = $date->getTimestamp();
// print them correctly like this
$utc = CakeTime::format('Y-m-d H:i', $utc, false, new DateTimeZone('Europe/Berlin'));
On caveat to this approach, if a user changes their timezone from one with DST to one without (or vice-versa), they may encounter the same problem.

Related

Laravel timestamp columns saving with wrong date format

I recently updated an old laravel 5.7 project to 8.0 and the created_at and updated_at model timestamps get created with the wrong format. Back in the day i used this code in all models that are from a SQL Server database to get it working between my local and production environment.
public function getDateFormat()
{
if (PHP_OS_FAMILY == 'Windows') {
return 'Y-d-m H:i:s.v';
} else {
return 'Y-m-d H:i:s';
}
}
I use windows to develop the application and a Linux server to run it with apache, but after updating the project the timestamps invert the day and month of the date. For example, if i create a model in the date '2022-06-07 13:00:00' the created_at timestamp will be '2022-07-06 13:00:00'.
Of course, changing the getDateFormat() method to only return 'Y-d-m H:i:s.v' in all environments works, but create another problem with php date function, for example, if i call <p> updated at: {{ date('d/m/Y H:i:s', strtotime($model->updated_at)) }}</p> the desired result would be updated at: 07/06/2022 13:00:00 but instead i get updated at: 06/07/2022 13:00:00.
I really dont know if this is a php timezone issue or something related to laravel, since the problem shows at saving/updating rows or displaying formatted data information.
try
date("Y-d-m H:i:s", time());
Please use:
updated at: {{ date('m/d/Y H:i:s', strtotime($model->updated_at)) }}
for the desired result as the above m shows month, d shows days similarly y shows year.
Laravel have Carbon so use it for datetime stuff
use Carbon\Carbon;
...
if(! function_exists('format_date') {
function format_date(string $date): string
{
return Carbon::parse($date)->format('d-m-Y H:i:s');
// something like 31-12-2022 12:00:00, just change format as you need
}
}
From your view:
{{ format_date($model->date) }}
Make sure to check Ref

Laravel AJAX call returns a date of 1 day before

On my Laravel app, I have created an AJAX call to search bookings based on a given name. In the database, every booking contains a date field. For some reason, the date I receive from my controller query is always the day before. So for example, when the date field in the database shows '2021-05-18', the collected date from the query shows '2021-05-17'.
Does anyone have an idea what can be the cause of this and how I can solve it?
I use the same date field in other controller functions and on other views as well, and there the date field is displaying correctly.
Also, in my config > app file I have set the 'timezone' to 'Europe/Brussels'.
A screenshot of my database:
The code in my controller is as follows:
public function searchReservation(Request $request)
{
$users = User::where('name', 'LIKE', '%'.$request->name.'%')
->select('id', 'name')
->get();
foreach($users as $user) {
$user['reservations'] = Booking::where('user_id', $user['id'])->get();
}
return $users;
}
My code in the controller, where I 'transform' the date, before I create a new booking:
if (Carbon::hasFormat($request->date, 'd-m-Y')) {
$request->date = Carbon::createFromFormat("d-m-Y", $request->date);
} else {
$request->date = Carbon::createFromFormat("d-n-Y", $request->date);
}
A screenshot of my database output, where the date is always a day before the real date:
When you are using Carbon for formatting a time, you need to explicitly set the time zone if you intend to process with a time zone other than UTC. Default values like created_at will be automatically saved properly by Laravel respecting the timezone set in config/app.php. For other date/time column you need to explicitly set the timezone for formatting with Carbon. Therefore, your code should be like:
if (Carbon::hasFormat($request->date, 'd-m-Y'))
{ $request->date = Carbon::createFromFormat("d-m-Y", $request->date, config('app.timezone')); //pass timezone value from config as the third parameter
}
else {
$request->date = Carbon::createFromFormat("d-n-Y", $request->date,config('app.timezone'));
}

angular-pickadate timezone - selectedDate is one day before from modal date for other timezones

angular-pickadate works for my local time. To check global Times, I have changed my time zone to "America/Denver". Now selected date is taken one day before today's date (passed modal date), so it applies "pickadate-active" class to yesterday.
I tried passing modal date with local timezone and also with UTC timezone. I don't know why dateHelper.parseDate calls again with stripping Timezone value earlier passed, now my understanding is $locale is converting stripped date assuming it a UTC date to local date. Hence, being GMT-06:00, selected date comes to one date before.
HTML DIV - <div pickadate ng-model="vm.date" ng-model-options="{ debounce: 0 }" header="true" select="true" date-highlight-list="vm.dateList" ></div>
Controller - vm.date = moment().tz(timeZoneName).format();
can someone suggest a way to handle different timezones with angular-pickadate?? Thanks !
GIT directive URL - https://github.com/restorando/angular-pickadate
The parseDate function was giving date object as per GMT timezone, so date was becoming one day less.So I removed this condition which was directly returning GMT date for passed date strings with timezone.
if (angular.isDate(dateString) || angular.isDate(new Date(dateString))) {
new Date(dateString); }
Now it goes to next if block to format date with regex and added this condition there to handle date strings and date objects -
if(typeof dateString == 'object') {
dateString = (dateString['_d'])? dateString['_d']: dateString['_i']; // being private (_d, _i) should be avoided but only way in mine case
if(typeof dateString == 'object') // returns Object by dateString['_d'] else string
dateString = dateString.getDate() +'-'+ dateString.getMonth() +'-' +dateString.getFullYear();
}
dateParts = dateString.split(separator); // separator was "-" every time so making dateString with "-" in case it was object

Angular adds a day when displaying the date (dd/MM/yyyy)

I have a datepicker where the user picks the date without choosing the time, the date is saved correctly in my data base but when I display it, it shows one day ahaid, when I looked up the issue it turns out that it's a timezone issue that lots of people face but i didn't quite understand how to fix it in my code :
<tbody>
<tr ng-repeat="ft in ftListesorted">
<td>{{ft.NomProjet}}</td>
<td>{{ft.NomTache}}</td>
<td>{{ft.Datefeuillestemps | date : 'dd/MM/yyyy' }}</td>
</tr>
</tbody>
I get the "ft.Datefeuillestemps" from my database where the default time is always set to T00:00:00.
For example here's how my date is stored in the data base : 2017-05-17 00:00:00.0000000 and this is what I get in my view : 18/05/2017 so a day is added.
How can I solve this issue ?
$scope.passdate = function (dt) {
var targetTime = new Date(dt);
var timeZoneFromDB = -7.00; //time zone value from database
//get the timezone offset from local time in minutes
var tzDifference = timeZoneFromDB * 60 + targetTime.getTimezoneOffset();
//convert the offset to milliseconds, add to targetTime, and make a new Date
var offsetTime = new Date(targetTime.getTime() + tzDifference * 60 * 1000);
$scope.ISODateString(offsetTime);
}
check this if helps
You could subtract (new Date()).getTimezoneOffset() from your object (assuming the date in the database is on GMT), but this is not perfect. You will still have issues when timezones change differently because of daylight saving time.

Angular show wrong time with unknown timezone

flight.departure_at is a UTC format 2016-05-04T19:00:00.000Z
When I display this expression in the page,
It's format is totally out of expectation.
Why does the 12:00 come out? How come?
I also want to know how could I keep all the time format in UTC globally without adding options everywhere. It will make the whole App vulnerable
code
Departure_time: {{flight.departure_at}} ||| {{flight.departure_at | date: 'HH:mm'}}
output
Departure_time: 2016-05-04T19:00:00.000Z ||| 12:00
This standard is called ISO-8601 and the format is: YYYY-MM-DDTHH:mm:ss.sssZ
'T' is just a separator between date and time in ISO-8601 format.
'Z' is zero time zone ( your getting 12:00 , because it convert to your timezone)
we can use parse like this ,
var date = new Date('2016-05-04T19:00:00.000Z');
console.log(date.getUTCHours()); // Hours - 19
console.log(date.getUTCMinutes()); //0
console.log(date.getUTCSeconds());// 0
console.log(Date.parse(date));
console.log(new Date(Date.parse(date)));
OR
Date.parse(DATE) for getting time in standard time format
UTC
As stated in a different thread about personalizing timezones to the user - I've found this to be very helpful when coming to dealing with timezones in Angular. (https://stackoverflow.com/a/35161107/3585278)
module.config(['$provide', function($provide) {
var DEFAULT_TIMEZONE = 'GMT';
$provide.decorator('dateFilter', ['$delegate', '$injector', function($delegate, $injector) {
var oldDelegate = $delegate;
var standardDateFilterInterceptor = function(date, format, timezone) {
if(angular.isUndefined(timezone)) {
timezone = DEFAULT_TIMEZONE;
}
return oldDelegate.apply(this, [date, format, timezone]);
};
return standardDateFilterInterceptor;
}]);
}]);
I suggest you use Angular moment for all date time related stuff for the frontend and moment for the controller part.
You should store all time as UTC in backend and then on frontend you can set user's timezone globally as
app.constant('angularMomentConfig', {
'timezone' : <user's timezone>
});
Render UTC time now on frontend without worrying about timezone(make sure all variables are either moment or Date object):
<td ng-bind="flight.departure_at| amDateFormat:'HH:mm'"></td>

Resources