How to format DateInterval results - php

I have a task module, in it I have two fields
t_due_on(task complete date)
t_completed_on (task completed date)
Example data:
id task_name t_due_on t_completed_on
1 PF Module 2017-03-14 10:15 PM 2017-03-13 23:29 PM
The given task's due date is tomorrow but it was actually finished today, so I need to display the results like:
Task finished: 1 day before

You can use the DateTime::diff() or DateTimeImmutable::diff() function. All you have to do is to compare the two dates. I have given a very basic example here:
$dt1 = new DateTimeImmutable('2017-03-14 00:00:00');
$dt2 = new DateTimeImmutable('2017-03-16 12:00:00');
$di = $dt2->diff($dt1);
$outputStr = "Task finished: {$di->days} days ";
$suffix = $di->invert == 1 ? 'before' : 'after';
$outputStr .= $suffix . " deadline";
echo $outputStr;
Now it is important to note that:
the diff() function will produce a DateInterval class instance. This does not provide methods but rather explains the time interval in terms of seconds, minutes, hours, days, weeks etc.
notice that $di->invert is high (== 1) when the interval is -2 days for eg. $di->invert will be 0 for positive differences
the order of comparison is important! So calling $dt2->diff($dt1) is not the same as $dt1->diff($dt2) because of how the dates are spaced in time
you have to take care of the output. I.e. IF the difference between the two dates is less than a day $di->days will be 0! But $di->h will be set. Thus your functionality has to intelligently handle this also

Related

Why the difference between two DateTime objects does not work?

I've got a problem with my "DateTime difference code":
$timeStart = new DateTime('2015-11-28');
$timeEnd = new DateTime('2016-11-28');
$interval = $timeEnd->diff($timeStart);
$result = $interval->format('%d');
echo $result." day(s)";
When I visualize $result, PHP show me 0. But between those two dates there are more days than 0 day...
php does not calculate the difference between two dates that are not in the same year?
Because there are 0 days difference. There is however a 1 year difference. If you changed %d to %y you'd get 1. So there's a difference of 1 year, 0 months and 0 days.
What you can use instead is the days property on DateInterval, as such:
$result = $interval->days;
Okay, I'm aware the answer was given already. But below is just a bit explanation.
In fact, DateInterval::format() does makes sense when you have a fixed amount of time (in years, months, days, hours), like this:
$interval = new DateInterval('P2Y4DT6H8M');
echo $interval->format('%d days');
That isn't your case!
where you have a relative time (2016-11-28 related to 2015-11-28) at all. In this specific case you want the days amount past since 28-11-2015.
That's why DateInterval::days (DateTime::diff() returns a DateInterval object) makes sense:
$start = new DateTime('2015-11-28');
$end = new DateTime('2016-12-28');
var_dump($end->diff($start)->days);

Monthly Commission Calculation PHP

I'm running into a coder's block with PHP dates. Let me first paint the picture of what I want to happen. ex:
$user_join_date = new DateTime('2015-01-31');
$today_date = new DateTime('2015-04-30');
Every day a cron will be run and for this example, on every 31st (or 30th - 28th depending on the month) the system will calculate commission for this user based on orders and volume from the past month BETWEEN '2015-03-31' AND '2015-04-29'.
So what I need is two fold. First, I need to make sure I'm calculating the commission on the correct day ie: the monthly anniversary of their join date OR that same month's equivalent. Second, I need to find the time frame in between which I'll calculate commissions as demonstrated in the mysql snippit above.
For obvious reasons I can't just say:
if ($user_join_date->format('d') == $today_date->format('d')){
calc_commission();
}
Because this wouldn't get run every month. Let me know if I'm unclear on anything.
I think you're saying you want to credit each user on an integral number of months since her signup date. There's an aspect of MySQL's date arithmetic you will find very convenient -- INTERVAL n MONTH addition.
DATE('2015-01-30') + INTERVAL 1 MONTH ==> '2016-02-28'
DATE('2016-01-30') + INTERVAL 1 MONTH ==> '2016-02-29'
This feature deals with all the oddball Gregorian Calendar trouble around weekdays, quite nicely. I'm going to call the date the renewal date.
Now, let us say that the column signup contains the date/time stamp for when the user signed up. This expression determines the most recent monthly renewal date. In particular, if it is equal to CURDATE(), today is the renewal date.
DATE(signup) + INTERVAL TIMESTAMPDIFF(MONTH, DATE(signup), CURDATE()) MONTH
Next: This closely related (duh!) expression is equal to the previous month's renewal date.
DATE(signup) + INTERVAL TIMESTAMPDIFF(MONTH, DATE(signup), CURDATE())-1 MONTH
You could just take CURDATE() - INTERVAL 1 MONTH but this is much more accurate around Gregorian month-end monkey business. Try it: it works right even at the end of February 2016 (leap year day).
Now, you'll want to use transactions that happened beginning on the previous renewal date >=, up until the end of the day before < the present renewal date, in your computation. In MySQL filtering by that date range looks like this.
WHERE transdate >= DATE(signup) +
INTERVAL TIMESTAMPDIFF(MONTH, DATE(signup), CURDATE())-1 MONTH
AND transdate < DATE(signup) +
INTERVAL TIMESTAMPDIFF(MONTH, DATE(signup), CURDATE()) MONTH
$base_dt is your joining date. So If you pass it to checkIt function It will provide you true or false accordingly today's day is billing day or not.
$base_dt = new DateTime('2015-04-30', new DateTimeZone("America/Chicago"));
if(checkIt($base_dt)) {
echo "true";
//this is the day.
} else {
echo "false";
//this is not the day.
}
So the check it function should be like this .....
function checkIt($base_dt) {
$base_d = $base_dt->format('j'); //without zeroes
echo $base_d;
$today = new DateTime('now');
$d = $today->format('j');
echo $d;
if($base_d == $d) {
return true;
} else if($base_d>28) {
$base_dt->modify('last day of this month');
$dif_base = $base_dt->format('j') - $base_d;
//echo $dif_base;
$today->modify('last day of this month');
$diff_today = $today->format('j') - $d;
//echo $diff_today;
if(($base_d==31 && $diff_today==0)||($base_d==30 && $diff_today==0 && $d<30)||($base_d==29 && $diff_today==0 && $d<29)) {
return true;
}
if($dif_base==0 && $diff_today==0) {
return true;
}
}
return false;
}

Generating a date based on a weekinterval, a day, and an existing date

I have a database with different workdates, and I have to make a calculation that generates more dates based on a weekinterval (stored in the database) and the (in the database stored) days on which the workdays occur.
What my code does now is the following:
Read the first two workdates -> Calculate the weeks inbetween and save the week interval
Read all the workdates -> fill in the days on which a workdate occurs and save it in a contract.
Generate workdates for the next year, based on the week interval.
The point is: for each week with a week interval of 1, more days of the week should be saved as a workdate. I've used this code to do this, but it doesn't work.
// Get the last workdate's actdate.
$workdate_date = $linked_workdate['Workdate']['workdate_actdate'];
// Calculate the new workdate's date
$date = date("Y-m-d", strtotime($workdate_date . "+" . $interval . " week"));
// If 'Monday' is filled in for this contract, calculate on which day the
// Monday after the last interval is. Same for each day, obviously.
// The days are boolean.
if ($contract['Contract']['contract_maandag'] = 1){
$date = date("Y-m-d", strtotime($date, "next Monday"));
}
if ($contract['Contract']['contract_dinsdag'] = 1){
$date = date("Y-m-d", strtotime($date, "next Tuesday"));
}
// After this, save $date in the database, but that works.
Here is the error that i get:
strtotime() expects parameter 2 to be long, string given
I'm quite stuck right now, so help is appreciated!
if ($contract['Contract']['contract_maandag'] = 1){
if ($contract['Contract']['contract_dinsdag'] = 1){
This won't work. You're doing an assignment (=), so it's always true. But you want a comparison (===). It is recommended to do always (except required otherwise) to use strict (===) comparison.
Well, the = doesn't seem to be the problem, since the error is about the part that's after the comparison. Try
strtotime("$date next Monday");

PHP adding exact weekdays to a timestamp

I want to add an x number of week days (e.g. 48 weekday hours) to the current timestamp. I am trying to do this using the following
echo (strtotime('2 weekdays');
However, this doesn't seem to take me an exact 48 hours ahead in time. For example, inputting the current server time of Tuesday 18/03/2014 10:47 returns Thursday 20/03/2014 00:00. using the following function:
echo (strtotime('2 weekdays')-mktime())/86400;
It can tell that it's returning only 1.3 weekdays from now.
Why is it doing this? Are there any existing functions which allow an exact amount of weekday hours?
Given you want to preserve the weekdays functionality and not loose the hours, minutes and seconds, you could do this:
$now = new DateTime();
$hms = new DateInterval(
'PT'.$now->format('H').'H'.
$now->format('i').'M'.
$now->format('s').'S'.
);
$date = new DateTime('2 weekdays');
$date->add($hms);//add hours here again
The reason why weekday doesn't add the hours is because, if you add 1 weekday at any point in time on a monday, the next weekday has to be tuesday.
The hour simply does not matter. Say your date is 2014-01-02 12:12:12, and you want the next weekday, that day starts at 2014-01-03 00:00:00, so that's what you get.
My last solution works though, and here's how: I use the $now instance of DateTime, and its format method to construct a DateInterval format string, to be passed to the constructor. An interval format is quite easy: it starts with P, for period, then a digit and a char to indicate what that digit represents: 1Y for 1 Year, and 2D for 2 Days.
However, we're only interested in hours, minutes and seconds. Actual time, which is indicated using a T in the interval format string, hence we start the string with PT (Period Time).
Using the format specifiers H, i and s, we construct an interval format that in the case of 12:12:12 looks like this:
$hms = new DateInterval(
'PT12H12M12S'
);
Then, it's a simple matter of calling the DateTime::add method to add the hours, minutes and seconds to our date + weekdays:
$weekdays = new DateTime('6 weekdays');
$weekdays->add($hms);
echo $weekdays->format('Y-m-d H:i:s'), PHP_EOL;
And you're there.
Alternatively, you could just use the basic same trick to compute the actual day-difference between your initial date, and that date + x weekdays, and then add that diff to your initial date. It's the same basic principle, but instead of having to create a format like PTXHXMXS, a simple PXD will do.
Working example here
I'd urge you to use the DateInterface classes, as it is more flexible, allows for type-hinting to be used and makes dealing with dates just a whole lot easier for all of us. Besides, it's not too different from your current code:
$today = new DateTime;
$tomorrow = new DateTime('tomorrow');
$dayAfter = new DateTime('2 days');
In fact, it's a lot easier if you want to do frequent date manipulations on a single date:
$date = new DateTime();//or DateTime::createFromFormat('Y-m-d H:i:s', $dateString);
$diff = new DateInterval('P2D');//2 days
$date->add($diff);
echo $date->format('Y-m-d H:i:s'), PHP_EOL, 'is the date + 2 days', PHP_EOL;
$date->sub($diff);
echo $date->format('Y-m-d H:i:s'), PHP_EOL, 'was the original date, now restored';
Easy, once you've spent some time browsing through the docs
I think I have found a solution. It's primitive but after some quick testing it seems to work.
The function calculates the time passed since midnight of the current day, and adds it onto the date returned by strtotime. Since this could fall into a weekend day, I've checked and added an extra day or two accordingly.
function weekDays($days) {
$tstamp = (strtotime($days.' weekdays') + (time() - strtotime("today")));
if(date('D',$tstamp) == 'Sat') {
$tstamp = $tstamp + 86400*2;
}
elseif(date('D',$tstamp) == 'Sun') {
$tstamp = $tstamp + 86400;
}
return $tstamp;
}
Function strtotime('2 weekdays') seems to add 2 weekdays to the current date without the time.
If you want to add 48 hours why not adding 2*24*60*60 to mktime()?
echo(date('Y-m-d', mktime()+2*24*60*60));
The currently accepted solution works, but it will fail when you want to add weekdays to a timestamp that is not now. Here's a simpler snippet that will work for any given point in time:
$start = new DateTime('2021-09-29 15:12:10');
$start->add(date_interval_create_from_date_string('+ 3 weekdays'));
echo $start->format('Y-m-d H:i:s'); // 2021-10-04 15:12:10
Note that this will also work for a negative amount of weekdays:
$start = new DateTime('2021-09-29 15:12:10');
$start->add(date_interval_create_from_date_string('- 3 weekdays'));
echo $start->format('Y-m-d H:i:s'); // 2021-09-24 15:12:10

Calculate passed time with PHP

I got the time difference between dates like this :
$time1 = "2013-02-25 12:00:00";
$time2 = "2013-01-01 12:00:00";
$tdiff = strtotime($time1) - strtotime($time2);
I want extract days, hours and minutes from $tdiff. Output should like : 35 days 6 hours 14 minutes
I really searched and try to do something by myself. But I can't get true value.
---- EDIT ----
I can found date diff. I want extract days, hours, minutes from calculated time...
---- EDIT 2 ----
Here is my complete mysql code
select (
select avg(UNIX_TIMESTAMP(tarih)) from table1
where action in (6) and who = '".$user."' and dates between '".$date1."' and '".$date."'
) - (
select avg(UNIX_TIMESTAMP(tarih_saat)) from table2
where action in (6) and active = 1 and dates between '".$date1."' and '".$date2."
)
This query returns to me true value of time. This query is working correctly for me. Result is like : 215922. result type of UNIX_TIMESTAPM. So I want to learn how many days, hours and minutes in this timestamp.
PHP has a DateInterval class which can be used like so:
$date1 = new DateTime('2013-02-25 12:00:00');
$date2 = new DateTime('2013-01-01 12:00:00');
$diff = $date2->diff($date1); // Get DateInterval Object
echo $diff->format('%d Day and %h hours and %i minutes');
you could use date object to get more accurate.
the default var type doesnt give the time difference.
most often they are considered as string types when assinged like you did it

Categories