Zend_Date::sub() and ISO_8601 calculating hour difference wrong - php

I want to calculate time difference between two Zend_Date objects (for countdown calculator):
$now = new Zend_Date($now_datetime, Zend_Date::ISO_8601);
$end= new Zend_Date($end_datetime, Zend_Date::ISO_8601);
echo $now->getIso();
echo $end->getIso();
$expires=array();
$expires['expired']=false;
if($end->isEarlier($now)){
$expires['expired']=true;
return $expires;
}
$dif=$end->sub($now);
$expires['days']=($dif->getDay()->toValue()/(60*60*24));
$expires['hours']=($dif->getHour()->toValue()/(60*60));
$expires['minutes'] = $dif->getMinute()->toValue()/60;
$expires['seconds'] = $dif->getSecond()->toValue();
var_dump($expires);
For $now_datetime ='2012-06-30 01:01:01' and $end_datetime='2012-06-30 23:59:59', the result is
2012-06-30T01:01:01+02:00
2012-06-30T23:59:59+02:00
//array
'expired' => boolean false
'days' => int 0
'hours' => int 22
'minutes' => int 58
'seconds' => int 58
and it is OK.
But for For $now_datetime ='2012-06-30 00:01:01' and $end_datetime='2012-06-30 23:59:59', the result is
2012-06-30T00:01:01+02:00
2012-06-30T23:59:59+02:00
//array
'expired' => boolean false
'days' => int 1
'hours' => int -1
'minutes' => int 58
'seconds' => int 58
and it is NOT OK. I expect 'hours' to be 23, not -1 ?!
I am running MAMP with php 5.3, Zend_Framework 1.10.
What is wrong with that? ISO_8601 is used for MySQL 'datetime' data and I don't wanto to change to mktime()...

try to set
date_default_timezone_set('UTC');
in your index.php.. it will work fine..

Related

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

get datetime difference breakdown in laravel php

I'm trying to get the difference between two dates like this
[
'years' : 4, // 0 if the difference is not above a year
'months': 4, // 0 if the difference is not of above a month
'weeks': 4, // 0 if the difference is not of above a week
'days': 4, // 0 if the difference is not of above a day
'hours' : 4 // 0 if the difference is not of above a hour
'minutes': 54 // 0 if the difference is not of above a minute
'seconds': 5 // 0 if the difference is not of above a second
]
Is there any utility function that gives me an output something like above in laravel PHP
this is my code at the moment
$date1 = new Carbon('2018-08-18 11:09:12');
$date2 = new Carbon('2018-04-02 08:15:03');
// dd($date1->diffForHumans($date2, false, false, 6));
$p = $date2->diffForHumans($date1, false, false, 6);
You could use the diffAsCarbonInterval()
$p = $date2->diffAsCarbonInterval($date1);
Then you can access the above values with:
$p->years //year
$p->months //month
$p->weeks //week
$p->daysExcludeWeeks //day
$p->hours //hour
$p->minutes //minute
$p->seconds //second
Or to take it one step further you could create a macro. One way to do this would be to add the following to the register method of your app service provider:
\Carbon\Carbon::macro('diffAsArray', function ($date = null, $absolute = true) {
$interval = $this->diffAsCarbonInterval($date, $absolute);
return [
'year' => $interval->years,
'month' => $interval->months,
'week' => $interval->weeks,
'day' => $interval->daysExcludeWeeks,
'hour' => $interval->hours,
'minute' => $interval->minutes,
'second' => $interval->seconds,
];
});
Then you can call:
$p = $date2->diffAsArray($date1);
Obviously, feel free to change the method name of the macro to something else if you want to.

Carbon: displaying hours and minutes?

I have recently been using Carbon to display humanized time strings, but for some reason I can only get it to show the main factor, for example, I have a date that it needs to show how long until that date is. So for example, if its 6 days, 4 hours and 32 minutes away from now, it currently only displays '6 days'.
How would I go about getting it to display the hours too, and possibly the minutes? It's kind of horrible looking when it only gives you the days, and many people may want to know more like the hours and seconds?
I can't find anything on the Carbon documentation for this. I am using it inside a laravel 5.3 view if that's even relevant.
Heres my code:
{{ \Carbon\Carbon::createFromTimeStamp(strtotime($item->created_at))->diffForHumans() }}
You shouldn't use diffForHumans()in this case, because it returns only a string on which one can no longer work. This is an end result.
It is better to work with a Carbon object, like this :
$created = \Carbon\Carbon::createFromTimeStamp(strtotime($item->created_at));
And then, add this in the view :
{{ $created ->diff(\Carbon\Carbon::now())->format('%d days, %h hours and %i minutes'); }}
You can extend for a larger period :
$created ->diff(\Carbon\Carbon::now())->format('%y year, %m months, %d days, %h hours and %i minutes');
EDIT (according to the comments) :
If you do :
$diff = $created->diff(Carbon::now());
var_dump($diff);
You get this result :
object(DateInterval)[113]
public 'y' => int 0
public 'm' => int 0
public 'd' => int 6
public 'h' => int 4
public 'i' => int 32
public 's' => int 29
public 'f' => float 0.397424
public 'weekday' => int 0
public 'weekday_behavior' => int 0
public 'first_last_day_of' => int 0
public 'invert' => int 0
public 'days' => int 6
public 'special_type' => int 0
public 'special_amount' => int 0
public 'have_weekday_relative' => int 0
public 'have_special_relative' => int 0
// results depend of the current time
From there, you browse the elements to create the best answer to your needs.
Show only Hours:Minutes (11:11)
To display only Hours:Minutes you can use:
$curr = \Carbon\Carbon::now()->format('H:i');
echo $curr; // 11:11
If you have to set a timezone you have to use the timezone function. You can pass to these function your timezone timezone('Your/Zone')
$curr = `\Carbon\Carbon::now()->timezone('Europe/Amsterdam')->format('H:i');
echo $curr; // 12:11

Code loops optimization in PHP

Is there any way to optimize this piece of code to work faster? I'd appreciate any suggestions!
This piece of code processes the transferring of edges during graph creation.
foreach($times_arrival as $city_id => $time_points) {
// if city is not prohibited for transfers and there is and exists any departure times for this city
if (isset($times_departure[$city_id]) && isset($cities[$city_id]))
{
foreach($times_arrival[$city_id] as $t1_info)
{
foreach($times_departure[$city_id] as $t2_info)
{
if ($t1_info[0] != $t2_info[0]) //transfers are allowed only for different passages
{
$t1 = $t1_info[1];
$t2 = $t2_info[1];
$vertex_key = new Vertex($city_id, $t1, 1);
$vertex_key = $vertex_key->toString();
//minimum transfer time is 10 min.
if (date('H:i', strtotime($t2)) > date('H:i', strtotime('+ 10 minutes', strtotime($t1))))
{
$this->graph[$vertex_key][] = new Edge(
NULL,
$vertex_key,
new Vertex($city_id, $t2, 0),
(float) 0,
$f((strtotime($t2) - strtotime($t1)) / 60, 0, 1) //edge weight
);
}
//if transfer is on the bound of the twenty-four hours
else if (date('H:i', strtotime('+ 24 hours', strtotime($t2))) > date('H:i', strtotime('+ 10 minutes', strtotime($t1))))
{
$this->graph[$vertex_key][] = new Edge(
NULL,
$vertex_key,
new Vertex($city_id, $t2, 0),
(float) 0,
$f(strtotime('+ 24 hours', strtotime($t2)) - strtotime($t1) / 60, 0, 1) //edge weight
);
}
}
}
}
}
}
example of variables:
var_dump($times_arrival); //$times_departure have the same structure
array
3 =>
array
0 =>
array
0 => string '1' (length=1)
1 => string '08:12' (length=5)
1 =>
array
0 => string '2' (length=1)
1 => string '08:40' (length=5)
41 =>
array
0 =>
array
0 => string '21' (length=2)
1 => string '12:40' (length=5)
Thank you all!
The reason of slow speed was coz of using functions strtotime() and date().
In that case only you can say whether you chose a good or bad algorithm. In my point of view your code not has no extra computations.
Only one recommendation - use Xdebug to profile your code and find out where the bottleneck is, if possible.

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