PHP datediff overday issue - php

I wish to get datediff between two times: first is in the evening (like 23:59:59) and the second is on new day (like 02:02:02). When using datediff, it doesn't show correct difference:
echo date_diff(date_create("02:02:02"), date_create("23:59:59"))->format('%H:%I:%S');
response: 21:57:57 (IS WRONG SOMEHOW)
echo date_diff(date_create("02:02:02"), date_create("00:00:00"))->format('%H:%I:%S');
response: 02:02:02 (ECHOS CORRECT TIME)
How could I get it work?

If the date has changed, then you have to tell it that, or it will assume today. You can check it like this:
echo date_diff(date_create("tomorrow 02:02:02"), date_create("23:59:59"))->format('%H:%I:%S');
// 02:02:03
You can verify what the date_create is creating by just dumping it:
var_dump(date_create("02:02:02"));
// object(DateTime)(
// 'date' => '2019-08-16 02:02:02.000000',
// 'timezone_type' => 3,
// 'timezone' => 'America/New_York'
// )
var_dump(date_create("tomorrow 02:02:02"));
// object(DateTime)(
// 'date' => '2019-08-17 02:02:02.000000',
// 'timezone_type' => 3,
// 'timezone' => 'America/New_York'
// )
var_dump(date_create("00:00:00")); // 00:00 being start of day, not end
// object(DateTime)(
// 'date' => '2019-08-16 00:00:00.000000',
// 'timezone_type' => 3,
// 'timezone' => 'America/New_York'
// )

Related

Eloquent Model saved to DB with day and month inverted

I am trying to save my Laravel Eloquent model to the database.
All date properties are saved in wrong format to DB (even created_at and updated_at).
In example below, my updated_at has been renamed as S_MODDATE but it's still the "native" field.
Reading the date fields works fine.
Example
My date in PHP : 2021-12-07 (7th of December)
When saved to database : 2012-07-12 (12th of July)
When read again from database : 2012-07-12
Minimal example of my code :
$currentDatetime = new \DateTime();
Log::debug($currentDatetime->format("Y-m-d h:i:s")); // Looks correct
$approRecord = T_Appro::find($approvalId);
$approRecord->DATESTATUT = $currentDatetime;
DB::enableQueryLog();
$approRecord->save();
Log::debug(DB::getQueryLog());
Corresponding log
[2021-12-07 13:17:25] local.DEBUG: 2021-12-07 01:17:25
[2021-12-07 13:17:28] local.DEBUG: array (
0 =>
array (
'query' => 'update [t_appro] set [DATESTATUT] = ?, [t_appro].[S_MODDATE] = ? where [T_APPROID] = ?',
'bindings' =>
array (
0 =>
DateTime::__set_state(array(
'date' => '2021-12-07 13:17:25.981319',
'timezone_type' => 3,
'timezone' => 'Europe/Brussels',
)),
1 => '2021-12-07 13:17:28.035',
2 => 'S000000029',
),
'time' => 468.81,
),
)
Tested solutions
Add protected $dateFormat = 'Y-m-d H:i:s'; to my model
Add following code to my model
public function getDateFormat()
{
return 'Y-m-d H:i:s.v';
}
Set app.php timezone to Europe/Brussels

Correct epoch to DateTime conversion

Whit this code:
$epoch= '1609455600';
$date = new DateTime( '#'.$epoch);
echo $date-> format( 'Y-m-d');
I see this result 2020-12-31. The server timezone is reported as Europe/Zurich (with date_default_timezone_get). But in this time zone that date should be 2021-1-1.
What is going on here?
In addition to the comment from #tuckbros. The output with var_export shows that the DateTime object has the time zone 00:00 (UTC).
date_default_timezone_set('Europe/Zurich');
$epoch= '1609455600';
$date = new DateTime( '#'.$epoch);
var_export($date);
/*
DateTime::__set_state(array(
'date' => '2020-12-31 23:00:00.000000',
'timezone_type' => 1,
'timezone' => '+00:00',
))
*/
The clean way to get the local time is to transfer the object to the desired time zone (and not to add any offset times).
$date->setTimeZone(new DateTimeZone(date_default_timezone_get()));
var_export($date);
/*
DateTime::__set_state(array(
'date' => '2021-01-01 00:00:00.000000',
'timezone_type' => 3,
'timezone' => 'Europe/Zurich',
))
*/
You can now continue to work with the DateTime object, since it has the correct time zone in addition to the correct local time.

Get day from date Laravel

I got a date with format 'Y-m-d', and want to get the day from it. Like if I have 2021.01.01, I want for example Friday, or Thursday depending on what day it actually is. I already got the date stored as $date and I want the day stored as $day.
I've already tried this, without any error, and without anything happening:
$day = Carbon::createFromFormat('Y-m-d', $date)->format('1');
var_dump($day);
I've found another solution for you :
$timestamp = strtotime('2009-10-22');
$day = date('l', $timestamp);
echo $days;
output:
Thursday
You can try this ,
$d=unixtojd(mktime(0,0,0,6,20,2007));
var_dump(cal_from_jd($d,CAL_GREGORIAN));
output :
array (size=9)
'date' => string '6/20/2007' (length=9)
'month' => int 6
'day' => int 20
'year' => int 2007
'dow' => int 3
'abbrevdayname' => string 'Wed' (length=3)
'dayname' => string 'Wednesday' (length=9)
'abbrevmonth' => string 'Jun' (length=3)
'monthname' => string 'June' (length=4)
And for your code , you just need to add your date like this
$d=unixtojd(mktime(0,0,0,month,days,year));
$calendar = cal_from_jd($d,CAL_GREGORIAN);
var_dump($calendar['dayname']);
It seems like you used a 1 (one) instead of an l (lowercase L). If you change that, it works fine.
$day = Carbon::createFromFormat('Y-m-d', $date)->format('l');
var_dump($day);
This works, you seem to be using 1 instead of l
$today = Carbon::now();
$dayName = $today->format('l');
When using Carbon, ->dayName is the obvious and more explicit way:
Carbon::createFromFormat('Y-m-d', $date)->dayName
It also allow you to have it in any language:
Carbon::createFromFormat('Y-m-d', $date)->locale('fr_FR')->dayName

PHP Bug day of year [duplicate]

This question already has answers here:
Convert day of the year to datetime in php
(2 answers)
Closed 2 years ago.
I'm trying to convert day of year into date.
So no problem i use DateTime::createFromFormat with z and Y.
DateTime::createFromFormat('z Y', '199 2020');
/*RESULT*/
object(DateTime)[3]
public 'date' => string '2020-07-19 12:45:24.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Paris' (length=12)
But when i test the result i get the wrong day of year...
date('z Y', strtotime('2020-07-19'));
/*RESULT*/
'198 2020' (length=8)
So i trying this
DateTime::createFromFormat('z Y', date('z Y', strtotime('2020-07-19')));
/*RESULT*/
object(DateTime)[3]
public 'date' => string '2020-07-20 12:52:10.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Paris' (length=12)
if i check day of year 199 of 2020 on this website: https://www.calendrier.best/numero-de-jour-2020.html
I get 2020-07-17
What i'm doing wrong ??
If you know the year, you can use something like this. Outputs:
string(10) "2020-07-17"
Code:
<?php
$dayOfYearNumber = 199;
$year = 2020;
// Add day of year number to the first day of the year, substracting 1 because Jan 1st will be the first day.
$date = (new DateTime("$year-1-1"))->add(new DateInterval('P' . $dayOfYearNumber - 1 . 'D'));
var_dump($date->format('Y-m-d')); // string(10) "2020-07-17"
PHP only detects some standards like you can see here https://www.php.net/manual/de/datetime.formats.date.php
In Your first example you defined your format but strtotime() has no such parameter
If you have these values as variable you could use mktime() instead https://www.php.net/manual/de/function.mktime
But I recommend to always give PHP a month e.x. January

Is this a bug in PHP's DateTime:diff?

>> $start_dt = new DateTime()
DateTime::__set_state(array(
'date' => '2012-04-11 08:34:01',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $end_dt = new DateTime()
DateTime::__set_state(array(
'date' => '2012-04-11 08:34:06',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $start_dt->setTimestamp(strtotime('31-Jan-2012'))
DateTime::__set_state(array(
'date' => '2012-01-31 00:00:00',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $end_dt->setTimestamp(strtotime('1-Mar-2012'))
DateTime::__set_state(array(
'date' => '2012-03-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $interval = $start_dt->diff($end_dt)
DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 30,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 30,
))
>> $interval->format('%mm %dd')
'0m 30d'
i.e., 31-Jan-2012 to 1-Mar-2012 yields less than a month! I'd expect the output to be 1 month, 1 day. It shouldn't matter the number of days in February; that's the point of using a time library -- it's supposed to handle these things. WolframAlpha agrees.
Should I file a bug to PHP? Is there a hack/fix/workaround to get months to work as expected?
Updated answer
This behavior of DateTime::diff is certainly unexpected, but it's not a bug. In a nutshell, diff returns years, months, days etc such that if you did
$end_ts = strtotime('+$y years +$m months +$d days' /* etc */, $start_ts);
you would get back the timestamp that corresponds to end original end date.
These additions are performed "blindly" and then date correction applies (e.g. Jan 31 + 1 month would be Feb 31, corrected to Mar 2 or Mar 3 depending on the year). In this specific example you cannot add even one month as salathe also explains.
Should I file a bug to PHP?
No.
The "month" part of the interval means that the month part of the start date can be incremented by that many months. The behaviour in PHP, taking your start date of 31-Jan-2012 and incrementing the month (literally, 31-Feb-2012) and then correcting for a valid date (PHP does this for you) would give 02-Mar-2012 which is later than the target date that you are working with.
To demonstrate this, take your start date and add n months for a few months to see the behaviour.
31-Jan-2012 (Interval)
02-Mar-2012 (P1M)
31-Mar-2012 (P2M)
01-May-2012 (P3M)
31-May-2012 (P4M)
01-Jul-2012 (P5M)
You can see that the month is being incremented, then adjusted to make a valid date.

Categories