Calculating consecutive days - php

when a user logins into his account I want to calculate since how many consecutive days this user has loged in.
For this I store the a timestamp in $loginSince and calculating the consecutive days with this formula:
$consecutiveDays = (int)((time() - $loginSince) / DAY) + 1;
The problem is that this counts by seconds, meaning:
if the user logs in at 17:00 (5pm) and on the next day at 15:00 (3pm) the counter is still 1
if the user logs in at 8:00 (8am) and on the next day at 20:00 (8pm) the counter was resetet (beacause the gap between the two logins is greater than 24h and is again 1
I'd like to get real consectuve days, meaning if a user logs in as the following times:
Day 1: 00:01
Day 2: 23:59
Day 3: 15:00
The counter should say 3, because he loged in at three consectutive days. With my current formular this wont work as the the counter would reset between day 1 and 2.
Can anyone give an advise how to handle this please.

Perhaps use date('z'), as that should just give you the day and not the time?

To post the solution I made. It's based on the idea of Ed Heal to always compare fixed times. I had chosen midnight. After that I build the interval of this to also handle year changes and streaks for more than a year
$loginSince = $this->dailyLoginSince($account);
// Create midnight date of the moment the login-streak started
$loginSinceMidnight = strtotime(date('Y-m-d',$loginSince).' 00:00:00');
// We create the difference between the two dates, both at midnight
$numericDayLoginSince = new DateTime();
$numericDayLoginSince->setTimestamp($loginSinceMidnight);
$numericDayNow = new DateTime();
$timestamp = strtotime('today midnight');
$numericDayNow->setTimestamp($timestamp);
$interval = $numericDayLoginSince->diff($numericDayNow);
$loginSinceDays = $interval->format('%a'); // %a = Number of days
// +1 because it seems to be more thrilling, that we already got one day
// by just logging in for the first time.
return $loginSinceDays + 1;

Related

Retrieve remaining time between two dates considering business hours

I am trying to write a php solution to calculate the planned end time considering the target in business hours.
It shouldn't consider some days (retrieved from setting saved in db) such as holidays.
Also business hours are retrieved from db (morning_from (8:30am), morning_to (1:00pm), evening_from (2:30pm), evening_to (6:30pm)).
I want to develop this script because I want that my page shows the remaining time for technical resolution of an opened ticket every day.
For example:
customer having contract with 10 working hours SLA opens a ticket
today (friday) 31/01/2020 16:00:00, considering that in the
noBusinessDays = array("saturday", "sunday") and businessHours set as mentioned before(8:30-13:00/14:30-18:30), the result will have to
be monday 3/02/2020 17:30:00.
Code example:
$noBusinessDays = array("saturday", "sunday");
$businessHours = array("morning_from" => "8:30", "morning_to" => "13:00", "evening_from" => "14:30", "evening_to" => "18:30");
$SLA = "10"; //hours
$ticketDate = new DateTime();
$ticketDate->setTimestamp(strtotime("31/01/2020 16:00:00"));
// I don't know how to use my arrays to say in this calculation how to use them
$maximumLimit = $ticketDate->add(new DateInterval("PT" . $SLA ."H"));
Thank you in advance.
You may use the following function
// intersection of 2 time intervals
// input - Unix timestamps (start,end)
// output - intersection in seconds
function time_union($b_1,$e_1,$b_2,$e_2)
{
return max(0,$e_1-$b_1 - max(0,$e_1-$e_2) - max(0,$b_2-$b_1));
}
You will start with an empty time interval [X, Y) where X is the timestamp of the ticket creation and Y initially is equal to X.
Then you start adding days to Y - one by one. Each time you expand the time interval to contain another day - you use the above function to check how much of the SLA hours are covered (i.e. overlapping) with the working hours in the day you have just added. If the day is marked as a holiday - you simple skip it and continue with the next date.
If you find out that SLA hours are partially covered with either the morning or evening business hours - you should simply subtract the extra hours.
In the end Y will be equal to the timestamp that you want to show in your application.
I think I'd break down the problem into pieces. After calculating the total number of days in the interval, first dispose of the trivial case that it's all happening in one week.
begin by calculating the number of "whole weeks." Each "whole week" is five business days. Subtract the corresponding interval of time and proceed. Now, look at the day-of-the-week of the start-date: each day adds a certain number of days. Then the day-of-week of the end date, likewise. You can then consider hour-of-the-day as needed.
Holidays are a simple table: if the day falls within the range, subtract one day.
Now ... having said all of that, the very first thing that I would do is to search GitHub and SourceForge! Because I am extremely sure that somebody out there has already done this. :-D
"Actum Ne Agas: Do Not Do A Thing Already Done."

PHP time values when Daylight Saving Time (DST)

I am developing a system to sign up, which should add up all the time someone has been online in my server. I want to take into account also those extra hours that have to be added or subtracted from the total when time changes due to DST.
It is easy to subtract an hour in the third Sunday of March, because I only have to check if the signing time was before 2AM and the sign-out time after 3AM; it is also easy when, in October, someone signs in before 2AM and signs out after 3AM, in that case, I only have to add an hour.
But what about signing in between 2 and 3AM (let's say 2:30) on the last Sunday of October? How would I know if it is the first time it is 2:30, or the second time? In the first case I should add half an hour to the total, whereas in the second case I should leave it as it was before.
Does PHP's time() function give different values when it is 2:30 for the first time than when it is for the second time? I assume it should, as it returns the number of seconds since the Unix Epoch, and it shouldn't be the same for both cases, but I would like to confirm it.
Edit 1:
This is the code to calculate the two cases I mentioned where it was easy enough:
(timeChangingDates is a function coded by myself, which given the year, it returns an array of length 2. In the first position there will be the last Sunday of March, in which time changes, and in the second position, the last Sunday of October)
($startTime and $endTime are DateTime objects in which sign-in and sign-out datetimes are stored)
// If sign-in time is before 2AM and sign-out time after 3AM add an hour(October)
if ((strtotime($startTime->format('Y-m-d H:i:s')) < strtotime(new DateTime($startTime->format("Y").'-10-'.timeChangingDates($startTime->format("Y"))[1].' 1:59:00')->format('Y-m-d H:i:s'))) && (strtotime($endTime->format('Y-m-d H:i:s')) > strtotime(new DateTime($startTime->format("Y").'-10-'.cambiosHorarios($startTime->format("Y"))[1].' 3:00:00')->format('Y-m-d H:i:s')))) {
$totalTime = $totalTime + 60;
// If sign-in time is before 2AM and sign-out time after 3AM subtract an hour(March)
} else if ((strtotime($startTime->format('Y-m-d H:i:s')) < strtotime(new DateTime($startTime->format("Y").'-3-'.timeChangingDates($startTime->format("Y"))[0].' 1:59:00')->format('Y-m-d H:i:s'))) && (strtotime($endTime->format('Y-m-d H:i:s')) > strtotime(new DateTime($startTime->format("Y").'-3-'.cambiosHorarios($startTime->format("Y"))[0].' 3:00:00')->format('Y-m-d H:i:s')))) {
$totalTime = $totalTime - 60;
}
I haven't tried any way to do the third case mentioned yet, as I cannot simulate time change on Windows. That is why I'd like to know if just using time() would work.

Subtract datetime from current date time to get a timer

I have a column in a mysql databse that is expriation time of created post.... I have a page that is trying to show how many hours and seconds left until the time reaches the expiration time of each post. For instance I have this in my database:
2013-03-07 13:15:39
This shows when this post expires....
Now I want to be able to go to the page that displays each post and then show the amount of time left (based on the current time) until each posts expires.
2013-03-07 12:15:39
I have been using this so far:
$now = new DateTime(date("h:i:s",time()));
$exp = new DateTime($row['time_expires']);
$diff = $now->diff($exp);
printf('%d hours, %d minutes, %d seconds', $diff->h, $diff->i, $diff->s);
Where $row is fetching the post from the database
It has been working somewhat but seems to have issues sometimes... I have no clue why but it sometimes works great but then when I go check it again the time says 15 hours to expiration when it really should say 2 hours
h in date() is a 12-hour clock, which will create confusion, because DateTime will interpret it as a 24 hour one. A DateTime object will have the current time by default, so just use:
$now = new DateTime();
$exp = new DateTime($row['time_expires']);
$diff = $now->diff($exp);
But Marc B's suggestion of doing it directly in the query is probably a better one.

find date months ago, will it miss dates if current month is shorter?

I am writing a script to query dates "3 months ago". As I am thinking about making sure I do not miss anything, I am using this for comparison:
$date = date("Y-m-d",mktime(0,0,0,date("m")-$months_ago,date("d")));
I am realizing there is a chance I could be missing dates. For example, if I search on 11/30 for dates three months ago, I will get 8/30. But the next day is 12/1 and three months prior is 9/1. So in this script I have missed 8/31.
I guess the best method is to use days (90 days) instead on months. Is this the best practice for something like this?
Go directly with strtotime('-3 month'); you can also give negatives like -3 on the month param of the mktime and it will work like charm (better the second solution). No, it wont skip days - if "now" is 2011-06-17 it would return timestamp equivalent to 2011-03-17.
Edit:
Well, it might actually be true that you can miss days (I haven't checked your statement) but after all your unit of measurement of time is months, not days. What I'm saying is that in the Gregorian calendar month isn't constant amount of time - it could be 28, 29, 30 or 31 days.
Let's say you want to calculate months for a paid subscription period. If the user pays one month on 2011-02-15, when would his subscription expire? I would guess 2011-03-15, even though there are just 28 days between those two dates And if he pays for subscription on 2011-03-15, he would get full 31 days till 2011-04-15 and this seems perfectly fair to me as the subscription is "one month", which just happens to be different amount of days through the year.
If, in your case you don't want to get "3 months ago" but want to get constant amount of time that relatively represents "3 months", then you can use the medium month length - 88.59 days, or 88 days 14 hours and 10 minutes. That represented with code would be:
strtotime('-88 days -14 hours -10 minutes');
$when = strtotime('-3 months');
If you just need the month/year and don't want to have to calculate days:
$m = 5; // how many months ago, for example
$now = time();
$cm = date("m",$now); // current month
$yr = date("Y",$now) - intval((12 + $m - $cm)/12);
$month = (($cm + 11 - ($m % 12) ) % 12) + 1;
echo "$m months ago: $month yr: $yr\n";

MySQL: Calculate the difference between Date/Times - only during M-F "Work week"

I need to calculate a difference between a starting date/time and an ending date/time. But, I only want to do this for the 5-day work week (exclude Sat/Sun as days). What is the best way to do this? My thought is that from the date, I'll have to get the day of the week and if it is a weekday, then I add to the accumulator. If it's not, then I don't add anything.
I'm sure someone has done this before, but I couldn't seem to find anything searching. Any links or other help would be very useful.
Many thanks,
Bruce
DAYOFWEEK returns 1 for Sunday and 7 for Saturday. I'm not sure how your schema is set up, but this will perform a TIMEDIFF of two dates that are on a Monday - Friday work week.
select TIMEDIFF(date1,date2) from table
where DAYOFWEEK(date1) not in (1,7) and DAYOFWEEK(date2) not in (1,7)
MySQL DATE/TIME functions
EDIT: From Bruce's comment about holidays. If you have a table full of holiday dates, something like this would work to exclude processing those days:
select TIMEDIFF(date1,date2) from table
where date1 not in (select holiday from holiday_table) and
date2 not in (select holiday from holiday_table) and
DAYOFWEEK(date1) not in (1,7) and DAYOFWEEK(date2) not in (1,7)
NETWORKDAYS() "Returns the number of whole working days between start_date and end_date. Working days exclude weekends and any dates identified in holidays. Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days worked during a specific term." according to the Excel 2007 help file.
The "between" description is a bit inaccurate because it includes the start and end dates, i.e. networkdays(21-01-2010. 22-01-2010) = 2. It also takes no account of times.
Here's a function in PHP that will give the same results. It doesn't work properly if the end date is less than the start date, nor does do anything about holidays (see below the function).
function networkdays($startdate, $enddate)
{
$start_array = getdate(strtotime($startdate));
$end_array = getdate(strtotime($enddate));
// Make appropriate Sundays
$start_sunday = mktime(0, 0, 0, $start_array[mon], $start_array[mday]+(7-$start_array[wday]),$start_array[year]);
$end_sunday = mktime(0, 0, 0, $end_array[mon], $end_array[mday]- $end_array[wday],$end_array[year]);
// Calculate days in the whole weeks
$week_diff = $end_sunday - $start_sunday;
$number_of_weeks = round($week_diff /604800); // 60 seconds * 60 minutes * 24 hours * 7 days = 1 week in seconds
$days_in_whole_weeks = $number_of_weeks * 5;
//Calculate extra days at start and end
//[wday] is 0 (Sunday) to 7 (Saturday)
$days_at_start = 6 - $start_array[wday];
$days_at_end = $end_array[wday];
$total_days = $days_in_whole_weeks + $days_at_start + $days_at_end;
return $total_days;
}
To take holidays into account, you'd have to work out the number of days using this function, then use a query like
Select count (holiday_date) from holidays
where holiday_date between start_date and end_date
and DAYOFWEEK(holiday_date) not in (1,7)
Be careful that there isn't a problem with the end_date being treated as 00:00 (i.e. first thing in the morning) - you may have to condition it to be 23:59:59 so that it works properly. It all depends on how your holidays are stored.
To return the holidays in the same time period and subtract that from the number you first thought of.

Categories