I've been trying to count the age of something in weekdays. I've tried the method detailed in this question, Given a date range how to calculate the number of weekends partially or wholly within that range? but it doesn't seem to fit my usecase.
An item has a created DATETIME in the database, and I need to mark it as old if the created date is over 2 days old. However, the client has requested that age only count week days (Mon to Fri) and exclude Sat+Sun.
So far, my pseudo code looks like the following,
now - created_datetime = number_of_days
for(i number_of_days)
if(created_datetime - i)
is a weekday, then age++
There must be a cleaner way of achieving this? As if an item were to get very old, looping through each day of it's age, looking for a weekend day would impact speed quite a bit.
Any ideas would be great! Thanks
You only have to check the last 7 days to know the exact age in weekdays.
Here's the intuition:
Subtract from the total age of the object, the number of weekends in its lifetime. Note that every 7 days there are exactly 2 weekends. Add to this the number of weekend days in the remainder days (the ones that aren't in a full week) and you have to total number of weekend days in its lifetime. Subtract from the real age to get the weekday age.
int weekendCount = 0;
// Check the remainder days that are not in a full week.
for (int i = totalAge % 7; i < 7; i++) {
if (created_datetime - i is weekend) {
weekendCount++;
}
}
weekdayAge = totalNumberOfDays - (((totalNumberOfDays / 7) * 2) + weekendCount);
Note that the division is an integer division.
I'm sure you can do this with some math and a bit of careful thought. You need to check what day of the week the item was created and what day of the week it currently is. First, you calculate the number of days old it is, as I'm sure you were doing at first.
// if today is saturday, subtract 1 from the day. if its sunday, subtract 2
if (now() is sunday) $now = now() - 2 days;
if (now() is saturday) $now = now() - 1 day;
// same thing for $date posted. but adding 1 or 2 days
if ( $date is saturday) $date = $date + 2;
if ( $date is sunday) $date = $date + 1;
// get days difference
$days = $today - $date;
// have to subtract 2 days for every 7
$days = $days - floor($days/7)*2
You'd have to check if that works. Maybe you can do the calculation without moving your date to before/after the weekend. It may not be perfect, but its the right idea. No need to iterate.
Related
I have a problem with php that i don't really know how to solve. I have an array full of unix timestamps coming from a mysql query.
These timestamps are events that repeat every week ( For example, every Tuesday and Thursday ). They can repeat various days or just one.
Knowing the days that repeat, which day will be the next one.
For example:
In the Array I have :
1595289600 --> 2020/07/21 (Tuesday)
1595116800 --> 2020/07/19 (Sunday)
Today we are at 1595376000 (Wednesday) , so it should return 1595116800 + 604800 (Sunday).
In 5 days ( next monday) it should return 1595289600 + 604800 = 1595721600 (First tuesday + one week )
in one week (next Wednesday) , it should return the next Sunday (2020/08/02 ): 1596326400
And so on...
Thank you!
For every timestamp you have - calculate next timestamp (add a week) until it is after current timestamp. Then return lowest from those as that one will be the closest to now (but also in the future).
So lets say it is 2020-07-22 Wednesday.
Your 2020-07-21 Tuesday is in the past, so add a week: 2020-07-28 Tuesday - its in the future, so its our candidate.
Your 2020-07-19 Sunday is also in the past, so add a week: 2020-07-26 Sunday - its in the future so its out second candidate.
Now pick lower from 2 candidates: 2020-07-26 Sunday.
If the dates are more in the past then you will need more a week to them more times.
Something like this:
<?php
// those are your timestamps: $timestamps = [1595289600, 1595116800];
// $time is optional int for when you want to perform the calculation. defaults to current timestamp
function nextOccurence(array $timestamps, $time = null) {
$now = $time ?? time();
$nextTimestamps = [];
foreach ($timestamps as $timestamp) {
while ($timestamp < $now) {
$timestamp += 604800;
}
$nextTimestamps[] = $timestamp;
}
return min($nextTimestamps);
}
I'm having problem when getting the exact number of days. Given I have date/time which consider hours in counting number of days below the code give me zero days
$fisrstDate = new DateTime("2018-03-07 04:46:00");
$secondDate = new DateTime("2018-03-07 11:10:00");
$days=$fisrstDate->diff($secondDate)->days;
another example is this it should give me 2 days but shows only 1 days my idea is when 24 hours exceed I want to add another 1 days so that it would give me an output of 2 days
$fisrstDate = new DateTime("2018-03-07 04:46:00");
$secondDate = new DateTime("2018-03-08 05:00:00");
$days=$fisrstDate->diff($secondDate)->days;
You can use strtotime to get the exact seconds between two time stamps and then convert it to days followed by ceil to make it work. Eg:
$fisrstDate = strtotime("2018-03-07 04:46:00");
$secondDate = strtotime("2018-03-07 11:10:00");
$days = abs(ceil((abs($fisrstDate - $secondDate)/ (60 * 60 * 24)) - (1 / 24)));
echo $days;
Isn't it just date2 - date1 + 1?
I've just discovered a problem with the usually used method to sum months to a PHP Date. If you search on google or this forum, you usually find somethings like these:
$date = strtotime(date("Y-m-d", strtotime($date)) . " +1 month");
or
$months = DateInterval::createFromDateString('1 month');
$dateInDateTime->add($months);
Both approach are not correct, in my opinion.
For example in my code I have to increment 3 times the month of a starting date beginning with last day of April and return the last day of that months.
So my code generates this results:
2017-04-30
2017-05-31
2017-07-31
The second time the script add +1 month to date, goes from 2017-05-31 to 2017-07-01 because 31-05 + 30 days is over the last day of JUNE.
What Im expecting is 06-30 because you are summing MONTHS not DAYS and if you have an overflow, the code has to correct it, not me.
This is a common error that explode when you manage February or December (due to change of year).
Im expecting a script that increment month. So if I have 2017-03-23 and sum +1 month, I get 2017-04-23 and if I sum +1 month to 2017-03-31 I got 2017-04-30.
So. Pay attention when using this functions.
I think you are trying something dangerous.
What s going on for february? if you want all the time to change only month number it will break for latest days of this month, same for months with 30 days instead of 31...
You have to think about your approach in another way, because changing the month alone won't make an existing date sometimes.
+30 days seems to be the best thing to do
What Im expecting is 06-30 because you are summing MONTHS not DAYS and if you have an overflow, the code has to correct it, not me.
PHP corrects it, indeed. It never returns 31st of June as such a date doesn't exist. It corrects it to 1st of July.
What you apparently expect is that when you add 1 month to the last day of a month to get the last day of the next month. But this doesn't make any sense.
What should strtotime('2017-06-30 +1 month') return?
2017-07-31, because you are adding 1 month to the last day of June or 2017-07-30 because you are adding 1 month to the 30th day of June?
The times runs forward, counting the days from the end of the month is not natural. Sometimes it's useful but not that many times. And there always is a better solution: subtract 1 day from the first day of the next month. This way you don't have to do any correction or care about months with different number of days or even about leap years.
This is the function I wrote:
//it accept negative month value
public static function realAddMonthsToDate($month,$dateToModify,
$dateFormatInput = DEFAULT_SQL_DATE_FORMAT, $dateFormatOutput = DEFAULT_SQL_DATE_FORMAT)
{
$currentDate = DateTime::createFromFormat($dateFormatInput, $dateToModify);
$cDay = $currentDate->format('d');
$cMonth = $currentDate->format('m');
$cYear = $currentDate->format('Y');
$monthRest = $month;
$yearOffset = 0;
if ($month > 12)
{
$yearOffset = floor($month / 12);
$monthRest = $month - ($yearOffset * 12);
}
$cMonth += $monthRest;
if ($cMonth > 12) {
$cMonth = $cMonth - 12;
$cYear += 1;
}
if ($cMonth <= 0)
{
$cMonth = 12 + $cMonth;
$cYear -= 1;
}
$cYear += $yearOffset;
$arrivalMonthDays = cal_days_in_month(CAL_GREGORIAN, $cMonth, $cYear);
if ($cDay >= $arrivalMonthDays) $cDay = $arrivalMonthDays;
$newDate = new DateTime($cYear.'-'.$cMonth.'-'.$cDay);
return $newDate->format($dateFormatOutput);
}
Example:
$difference = strtotime($to) - strtotime($from);
$months = ($difference / 86400 / 30 );
Problem: But this way I never get exact average. Because I can’t sure for 30 days there can be 31 and 28 days months also.
Even I tried to divide by 12 month average but that also can’t work in every month selection cases
read first and
change according to ur own
You can get number of days for certain month in certain year using this function:
PHP Manual - cal_days_in_month
You can get number of days for whole year using, for example, this solution:
Finding the total number of days in year
Also, if you just want to get number of days for current month, you can use this:
date("t");
Are you after the number of months in a date range? If so, you could modify this previous answer to handle what you want:
PHP: Loop through all months in a date range?
To what I think you're after, you'd do something like this
$date_from = strtotime("2013-08-01");
$date_to = strtotime("2013-10-01");
$month_count = 0;
while ($date_from < $date_to) {
$date_from = strtotime('+1 month', $date_from);
$month_count++;
}
// month_count = number of months covered in the date range
Or, if you're just looking for the number of days in a date range, you could do something like this:
$date_from = strtotime("2013-08-01");
$date_to = strtotime("2013-08-28");
$diff = $date_to - $date_from;
$days_in_range = floor($diff / (60*60*24));
//days_in_range = 27
Not entirely sure what you're after from your question.
How would you go about calculating the amount of months between two arbitrary dates? Given that even if just one day falls on a month, it is considered a full month.
Examples:
2010-01-01 - 2010-03-31 = three months
2010-06-15 - 2010-09-01 = four months
Et cetera. I thought of just dividing the difference of timestamps with 2592000 (average number of seconds in a month) but that seems hacky and prone to errors. And I'd like to keep it as fast as possible (needs to run thousands of times quick), so I guess using strtotime isn't optimal either?
If I am reading your question correctly, you would want to return "2" for January 31st and February 1st, because it spans both January and February, even though they are only 1 day apart.
You could work out (psuedocode):
monthno1 = (date1_year * 12) + date1_month;
monthno2 = (date2_year * 12) + date2_month;
return (monthno2 - monthno1) + 1;
This assumes that the second date is the later date.
Assuming the dates are in a known format:
function getMonths($start, $end) {
$startParsed = date_parse_from_format('Y-m-d', $start);
$startMonth = $startParsed['month'];
$startYear = $startParsed['year'];
$endParsed = date_parse_from_format('Y-m-d', $end);
$endMonth = $endParsed['month'];
$endYear = $endParsed['year'];
return ($endYear - $startYear) * 12 + ($endMonth - $startMonth) + 1;
}
This gives:
print(getMonths('2010-01-01', '2010-03-31')); // 3
print(getMonths('2010-06-15', '2010-09-01')); // 4