Starting with a date in this format: 2011-05-01 09:00:00, how can I create an array that contains all office hours (09:00 to 17:00) for all working days of the year (so excluding all Saturday and Sundays). What I want to arrive to is something like this:
2011-05-01 09:00:00
2011-05-01 10:00:00
2011-05-01 11:00:00
2011-05-01 12:00:00
2011-05-01 13:00:00
2011-05-01 14:00:00
2011-05-01 15:00:00
2011-05-01 16:00:00
2011-05-01 17:00:00
//next day, starting at 09:00 and ending at 17:00
2011-05-02 09:00:00
...
2011-05-02 17:00:00
//until the last day of the year from 09:00 to 17:00
2011-12-31 09:00:00
...
2011-12-31 17:00:00
The start date will be the first of the current month at with 09:00 as time and the very last date (last element of the array) will always be 17:00 on the last day of the year.
Again, weekends should be excluded.
Pseudocode idea:
I thought of something like strtotime($start, "+1 one hour") with a check for "if smaller than 17:00" but it doesn't seem to be that simple.
How about this:
$start = strtotime('2011-05-01');
$end = strtotime('2011-12-31');
$times = array();
for ($i = $start; $i <= $end; $i += 24 * 3600)
{
if (date("D", $i) == "Sun" || date("D", $i) == "Sat")
{
continue;
}
for ($j = 9; $j <= 17; $j++)
{
$times []= date("Y-m-d $j:00:00", $i);
}
}
The outer loop iterates through all the days in the given time period. In the outer loop, we check to see if the day is either Saturday or Sunday (a weekend), and if it is, we skip that day. If it's not a weekend, we loop through all the valid hours, adding the full date and time to the array as we go.
Some tips:
date("G", $some_timestamp) gives you the hour of the day in 24-hour format
date("N", $some_timestamp) gives you the day of the week, 1 (for Monday) through 7 (for Sunday)
Take a look at the php manual for date.
Edit: You can pick any start timestamp and add 3600 to add one hour, if your hour is greater than 17, you can add a bigger step to go right to the next morning, same for a weekend, and just do a while ($timestamp < $end_timestamp) {}
I'd encourage you to use the wonderful DateTime class and its related classes. Here, you can make good use of DatePeriod:
<?php
$now = new DateTime('today'); // starting time 0.00 this morning
$endOfYear = new DateTime('31 December this year 23:00'); // end time
$interval = new DateInterval('PT1H'); // frequency -- every hour
$times = array();
foreach (new DatePeriod($now, $interval, $endOfYear ) as $datetime) {
// $datetime is a DateTime object for the hour and time in question
$dow = $datetime->format('w'); // 0 is Sunday
if (($dow == '0') || ($dow == '6')) {
continue; // miss Saturday and Sunday out
}
$time = $datetime->format('G'); // hour without leading 0
if (($time < '9') || ($time > '17')) {
continue;
}
$times[] = $datetime->format('r'); // output format
}
var_dump($times);
Obviously there are various aspects of this that you can configure, especially the output format. Depending on your purpose, you may prefer to put the DateTime objects themselves into the array.
$datetime = new DateTime(); // Set your start date here
do { // Iterate over .. dont know, a long time?
do { // Iterate over the week ...
$datetime->setTime(9,0,0);
do { // Iterate over the hours ...
echo $datetime->format('c') . PHP_EOL; // Do something with $datetime here
$datetime->add(new DateInterval('PT1H'));
} while ($datetime->format('G') < 18); // .. till < 18
$datetime->add(new DateInterval('P1D')); // next day
} while (!in_array($datetime->format('w'), array('0','6'))); // until we hit sunday or saturday
$datetime->add(new DateInterval('P2D')); // next monday
} while (true); // replace with your "end" expression
Currently untested.
You can use the common interval-strings (like 1 hour and so on) too http://php.net/dateinterval.createfromdatestring
You could calculate your dates with two nested loops and generate the string with date().
I would just loop through all dates, incremented by hour, from now until the end of the year, as follows (pseudocode, obviously):
for n = now until end of year
if (date(n) is between 9:00 and 17:00) AND (if date(n) is not sat or sun)
add to array
end if
increment n by 1 hour
end
Here is a solution which should be reasonably fast since it uses no string comparisons and has only two function calls inside the loops:
function hours()
{
$start = mktime(0, 0, 0, date('n'), 1, date('Y'));
$end = mktime(0, 0, 0, 1, 1, date('Y') + 1);
$wday = date('w', $start);
$result = array();
for ($t = $start; $t < $end; $t += 3600 * 24) {
if (($wday > 0) && ($wday < 6)) {
for ($hour = 9; $hour <= 17; $hour++) {
$result[] = date('Y-m-d', $t) . sprintf(' %02d:00:00', $hour);
}
}
$wday = ($wday + 1) % 7;
}
return $result;
}
Related
I need to calculate end-date/SLA for a given start-date considering the completion-time in minutes within the business hours/days.
For instance :
start-date = 2019-03-29 15:00:00
completion-time = 720 (minutes)
Considering 8 working hours per day(9am to 5pm) and weekend on Sunday. The end-date should be 2019-04-01 11:00:00.
So the total hours are 12,
on 2019-03-29 from 15:00:00 to 17:00:00 (2 hours)
on 2019-03-30 from 09:00:00 to 17:00:00 (8 hours)
on 2019-04-01 from 09:00:00 to 11:00:00 (2 hours)
Any help with regards to this would be much appreciated.
So far i can able to get the total working days excluding the weekends but getting exact end time is where i'm struggling.
Thank you.
Update:
$bookingDateTime = Carbon::parse('2019-03-29 15:00:00');
$i = 0;
$completion_in_days = (720/60)/8; //converted minutes in no of days
$working_days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
while($i < ($completion_in_days)){
if(array_search(strtolower($bookingDateTime->englishDayOfWeek), $working_days) !== false){
$i++;
}
$bookingDateTime->addDay();
}
This will give me the end date.
Please find the snippet below, I wrote line by line commenting for understanding
$endDate = $startDate = '2019-03-29 15:00:00';
$officeStart = '09:00:00';
$officeEnd = '17:00:00';
$totalHours = 12;
$i = 1;
$flag = false;
while ($totalHours > 0) {
$day = date('D', strtotime($endDate)); // fetching day of week
if ($day == 'Sun') { // checking if sunday thenskip by adding 1 day to end date
$endDate = date('Y-m-d', strtotime($endDate . " +1 Day")) . ' ' . $officeStart; // adding one day if sunday
continue;
}
$diff = strtotime($officeEnd) - strtotime(date("H:i:s", strtotime($endDate))); // getting difference of time of office end date and result end date
$hours = $diff / (3600); // difference in minutes
if ($hours > $totalHours) {
$hours = $totalHours;
$flag = true; // to break loop if last loop comes
} else {
$totalHours = $totalHours - $hours; // substracting hours from total hours left
}
$endDate = date('Y-m-d H:i:s', strtotime("+$hours Hour", strtotime($endDate))); // adding hours which are substracted
if (!$flag) {
$endDate = date('Y-m-d', strtotime($endDate . " +1 Day")) . ' ' . $officeStart; // if not last loop add day to result end date
} else {
break;
}
}
Output
2019-04-01 11:00:00
Demo.
Using PHP, I'd like to get the dates for specific weekdays within a given number of weeks. For example, I want to get the dates for Monday, Wednesday and Friday from 10 weeks.
The pseudo code for what I want is like this:
function (monday, wednesday, friday, 10) {
// 10 is week numbers
week1 5,7,9 oct 2015
week2 12,14,16 oct 2015
...
week10
}
i write a solution for this. Thanks for all answers.
<?
$dates = array();
$i = 0;
while(true) {
$i++;
$time = strtotime("+$i days");
$dayOfWeek = date('w', $time);
if(
/*
0-sun
1-mon***
2-tue
3-wed***
4-thu
5-fri***
6-sat
*/
($dayOfWeek == 0) or
($dayOfWeek == 2) or
($dayOfWeek == 4) or
($dayOfWeek == 6)
) {
continue;
}
$dates[] = date('Y-m-d', $time);
if( count($dates) > 30 ) {
break;
}
echo json_encode($dates );
}
?>
Sounds like you want to work with schedules. I suggest you to have a look at library called When, it's "Date / Calendar recursion library for PHP 5.3+".
What you want to do is MWF schedule for next 10 weeks:
$now = new DateTime('NOW');
$after10Weeks = clone $now;
$after10Weeks->modify('+10 week');
$r = new When();
$r->startDate($now)
->freq("weekly")
->until($after10Weeks)
->byday(array('MO', 'WE', 'FR'))
->generateOccurrences();
$occurrences = $r->occurrences;
Use PHP DateTime ISO date:
$date = new DateTime(); //The object
$year=2015; // Desired year
$days = array('Mon', 'Wed', 'Fri'); // Desired days of week
for ($yearDay=1; $yearDay<=366; $yearDay++) {
$date->setISODate($year,null, $yearDay); // call ISO Date
$week=$date->format('W'); // Get the week resulted
$day=$date->format('D'); // Get the day name resulted
if($week==10 && in_array($day, $days)) // return only dates of desired days of week 10
echo $date->format('M d Y')."<br>";
}
More information about ISO Date here
You can try this code with PHPTester
How would I structure the conditions to add two hours only to dates between 08:30 in the morning until 18:30 of the evening, excluding Saturday and Sunday?
In the case that a time near the border (e.g. 17:30 on Tuesday) is given, the left over time should be added to the beginning of the next "valid" time period.
For example: if the given date was in 17:30 on Tuesday, the two hour addition would result in 9:30 on Wednesday (17:30 + 1 hour = 18:30, 8:30 + the remainder 1 hour = 9:30). Or if the given date was in 17:00 on Friday, the result would be 9:00 on Monday (17:00 Friday + 1.5 hours = 18:30, 8:30 Monday + the remainder .5 hours = 9:00)
I know how to simply add two hours, as follows:
$idate1 = strtotime($_POST['date']);
$time1 = date('Y-m-d G:i', strtotime('+120 minutes', $idate1));
$_POST['due_date'] = $time1;
i have tried this this function and it works great except when i use a date like ( 2013-11-26 12:30 ) he gives me ( 2013-11-27 04:30:00 )
the problem is with 12:30
function addRollover($givenDate, $addtime) {
$starttime = 8.5*60; //Start time in minutes (decimal hours * 60)
$endtime = 18.5*60; //End time in minutes (decimal hours * 60)
$givenDate = strtotime($givenDate);
//Get just the day portion of the given time
$givenDay = strtotime('today', $givenDate);
//Calculate what the end of today's period is
$maxToday = strtotime("+$endtime minutes", $givenDay);
//Calculate the start of the next period
$nextPeriod = strtotime("tomorrow", $givenDay); //Set it to the next day
$nextPeriod = strtotime("+$starttime minutes", $nextPeriod); //And add the starting time
//If it's the weekend, bump it to Monday
if(date("D", $nextPeriod) == "Sat") {
$nextPeriod = strtotime("+2 days", $nextPeriod);
}
//Add the time period to the new day
$newDate = strtotime("+$addtime", $givenDate);
//print "$givenDate -> $newDate\n";
//print "$maxToday\n";
//Get the new hour as a decimal (adding minutes/60)
$hourfrac = date('H',$newDate) + date('i',$newDate)/60;
//print "$hourfrac\n";
//Check if we're outside the range needed
if($hourfrac < $starttime || $hourfrac > $endtime) {
//We're outside the range, find the remainder and add it on
$remainder = $newDate - $maxToday;
//print "$remainder\n";
$newDate = $nextPeriod + $remainder;
}
return $newDate;
}
I don't know if you still need this but here it is anyway. Requires PHP 5.3 or higher
<?php
function addRollover($givenDate, $addtime) {
$datetime = new DateTime($givenDate);
$datetime->modify($addtime);
if (in_array($datetime->format('l'), array('Sunday','Saturday')) ||
17 < $datetime->format('G') ||
(17 === $datetime->format('G') && 30 < $datetime->format('G'))
) {
$endofday = clone $datetime;
$endofday->setTime(17,30);
$interval = $datetime->diff($endofday);
$datetime->add(new DateInterval('P1D'));
if (in_array($datetime->format('l'), array('Saturday', 'Sunday'))) {
$datetime->modify('next Monday');
}
$datetime->setTime(8,30);
$datetime->add($interval);
}
return $datetime;
}
$future = addRollover('2014-01-03 15:15:00', '+4 hours');
echo $future->format('Y-m-d H:i:s');
See it in action
Here's an explanation of what's going on:
First we create a DateTime object representing our starting date/time
We then add the specified amount of time to it (see Supported Date and Time Formats)
We check to see if it is a weekend, after 6PM, or in the 5PM hour with more than 30 minutes passed (e.g. after 5:30PM)
If so we clone our datetime object and set it to 5:30PM
We then get the difference between the end time (5:30PM) and the modified time as a DateInterval object
We then progress to the next day
If the next day is a Saturday we progress to the next day
If the next day is a Sunday we progress to the next day
We then set our time to 8:30AM
We then add our difference between the end time (5:30PM) and the modified time to our datetime object
We return the object from the function
Given a Month and a weekday, I need to build a function that can retrieve the day number of all Mondays, Tuesdays, Wednesdays, Thursdays and Fridays.
Let's say I give the function this month, September 2012 and weekday number 1. The function should retrieve all the Mondays in September 2012 which are: 3, 10, 17 and 24
Please note that to me weekday number 1 is Monday, number 2 Tuesday, 3 is Wednesday, 4 Thursday and 5 Friday.
So far I've have done: getting the first day of the week given today's date (I post the function below). But I don't know how to follow from here in a simple way, I've been many hours on it and I suspect there's a better way to do it. Can you please show me how?
function getFirstDayOfWeek($date) {
$getdate = getdate($date);
// How many days ahead monday are we?
switch ( $getdate['wday'] ) {
case 0: // we are on sunday
$days = 6;
break;
default: // any other day
$days = $getdate['wday']-1;
break;
}
$seconds = $days*24*60*60;
$monday = date($getdate[0])-$seconds;
return $monday;
}
Thanks a ton
Not very smart, but would works for you:
// sept. 2012
$month = 9;
// loop through month days
for ($i = 1; $i <= 31; $i++) {
// given month timestamp
$timestamp = mktime(0, 0, 0, $month, $i, 2012);
// to be sure we have not gone to the next month
if (date("n", $timestamp) == $month) {
// current day in the loop
$day = date("N", $timestamp);
// if this is between 1 to 5, weekdays, 1 = Monday, 5 = Friday
if ($day == 1 OR $day <= 5) {
// write it down now
$days[$day][] = date("j", $timestamp);
}
}
}
// to see if it works :)
print_r($days);
All the users are in the United States. I need to be able to list all weekdays besides today. So say that it's Thursday, October 7. It should start by listing Friday, October 8 and then Monday, October 11.
I know how to make sure I'm only listing weekdays when looping through, but the trouble I have is making sure tomorrow is tomorrow. In the past it's changed at about 8:00 at night eastern time. I'm thinking I'd like to have is so when it's maybe 12:00 pacific time to count it as the next day.
<?php
$current = new DateTime('now');
$last = new DateTime('saturday');
while ($current < $last) {
echo $current->format('l, F j'), "\n";
$current->modify('+1 day');
}
?>
You can use strtotime to get the date of the next days and you can use date to determine if a date is a week day :
<?php
$reference = time(); // We set today as the first day //
for ($i=0, $j=0; $i<5; $i++, $j++) {
$nextDay = strtotime('+' . $j . ' days', $reference);
if (date('w', $nextDay) > 0 && date('w', $nextDay) < 6) {
echo date('r', $nextDay), "\n";
} else {
$i--;
}
}
?>