Datetime adds an extra day - php

Hello in the code bellow I'm not really sure how but instead of getting 32 days it returns me 33 days.
Any advices on how to fix it seems to be because of the datetime objects or timestamps.
<?php
$pricesByMonths = [
1 => 3,
2 => 4,
3 => 3,
4 => 5,
5 => 10,
6 => 15,
7 => 20,
8 => 15,
9 => 13,
10 => 10,
11 => 8,
12 => 3,
];
$pickupDate = new DateTime('12/12/2021 19:00', new DateTimezone('UTC'));
$returnDate = new DateTime('1/13/2022 22:00', new DateTimezone('UTC'));
$initialReturnDate = $returnDate;
if($pickupDate->diff($returnDate)->d <= 1) {
$returnDate->add(new DateInterval('P1D'));
}
$pickupDateTimestamp = $pickupDate->getTimestamp();
// get pickup time
$pickup_time = date('H:i:s', $pickupDateTimestamp);
[$hours, $minutes, $seconds] = explode(':', $pickup_time);
$returnDate->setTime($hours, $minutes, $seconds);
$newReturnDate = $returnDate->getTimestamp() + 7200;
if($newReturnDate < $initialReturnDate->getTimestamp()) {
$newReturnDate += 79200;
}
$totalFee = [];
$days = 1;
$periodPrice = 15;
for ($i = $pickupDateTimestamp; $i <= $newReturnDate; $i += 3600) {
$month = date('n', $i);
if (!array_key_exists($month, $totalFee)) {
$totalFee[$month]['fee'] = $periodPrice + $pricesByMonths[$month];
$days = 1;
}
$totalFee[$month]['days'] = $days++;
}
echo '<pre>';print_r($totalFee);die;

Solved it in the end the check to force minimum 2 days was a bit wrong.
$pickupDate = new DateTime($fields['calc_pickup_date'], new DateTimezone('UTC'));
$returnDate = new DateTime($fields['calc_return_date'], new DateTimezone('UTC'));
$dateDiff = $pickupDate->diff($returnDate);
if($dateDiff->m === 0 && $dateDiff->y === 0 && $dateDiff->h === 0 && $dateDiff->d <= 1) {
$returnDate->add(new DateInterval('P1D'));
}
$pickupDateTimestamp = $pickupDate->getTimestamp();
// get pickup time
$pickup_time = $pickupDate->format('H:m:s');
[$hours, $minutes, $seconds] = explode(':', $pickup_time);
$returnDate->setTime($hours, $minutes, $seconds);
$newReturnDate = $returnDate->getTimestamp() + 7200;
if($newReturnDate <= strtotime($fields['calc_return_date'])) {
$newReturnDate += 79200;
}
$totalFee = [];
$days = 1;
$periodPrice = dpd_get_period_price($varId, $fields['order_days']);
for ($i = $pickupDateTimestamp; $i <= $newReturnDate; $i += 3600) {
$month = date('n', $i);
if (!array_key_exists($month, $totalFee)) {
$totalFee[$month]['fee'] = $periodPrice + $pricesByMonths[$month];
$days = 1;
}
$totalFee[$month]['days'] = $days++;
}

Related

Counting the (year)quarters between two dates

I have project built using laravel and a I have to build a function that counts all the complete quarters that are in the selected date range - the dates used are inserted via input.
Here are the quarters(i used numerical representations for the months)
01 - 03 first quarter
04 - 06 second quarter
07 - 09 third quarter
10 - 12 forth quarter
I would really appreciate your help,because I've been at it for an entire day now and basically have nothing to show for it,i thing I've been trying so hard i'm actually at the point where i'm so tired, i can t think straight.
I do have some code but it;s worthless, because it doesn't work, and any kind of idea or snippet of code is welcomed.
Thanks for your help in advance.
I managed to do this using multiple functions; basically, if this is needed for chart statistics, then a more specific approach might be the case.
I have done this in Laravel with timestamp dates as input (this code can be adapted for getting semesters also :) , it works and is already tested):
public static function getQuartersBetween($start_ts, $end_ts)
{
$quarters = [];
$months_per_year = [];
$years = self::getYearsBetween($start_ts, $end_ts);
$months = self::getMonthsBetween($start_ts, $end_ts);
foreach ($years as $year) {
foreach ($months as $month) {
if ($year->format('Y') == $month->format('Y')) {
$months_per_year[$year->format('Y')][] = $month;
}
}
}
foreach ($months_per_year as $year => $months) {
$january = new Date('01-01-' . $year);
$march = new Date('01-03-' . $year);
$april = new Date('01-04-' . $year);
$june = new Date('01-06-' . $year);
$july = new Date('01-07-' . $year);
$september = new Date('01-09-' . $year);
$october = new Date('01-10-' . $year);
$december = new Date('01-12-' . $year);
if (in_array($january, $months) && in_array($march, $months)) {
$quarter_per_year['label'] = 'T1 / ' . $year;
$quarter_per_year['start_day'] = $january->startOfMonth();
$quarter_per_year['end_day'] = $march->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
if (in_array($april, $months) && in_array($june, $months)) {
$quarter_per_year['label'] = 'T2 / ' . $year;
$quarter_per_year['start_day'] = $april->startOfMonth();
$quarter_per_year['end_day'] = $june->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
if (in_array($july, $months) && in_array($september, $months)) {
$quarter_per_year['label'] = 'T3 / ' . $year;
$quarter_per_year['start_day'] = $july->startOfMonth();
$quarter_per_year['end_day'] = $september->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
if (in_array($october, $months) && in_array($december, $months)) {
$quarter_per_year['label'] = 'T4 / ' . $year;
$quarter_per_year['start_day'] = $october->startOfMonth();
$quarter_per_year['end_day'] = $december->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
}
return $quarters;
}
and getting the years between:
public static function getYearsBetween($start_ts, $end_ts, $full_period = false)
{
$return_data = [];
$current = mktime(0, 0, 0, date('m', $start_ts), date('d', $start_ts), date('Y', $start_ts));
while ($current < $end_ts) {
$temp_date = $current;
$year = new Date($temp_date);
$return_data[] = $year;
$current = strtotime("+1 year", $current); // add a year
}
if ($full_period) {
$return_data[] = $end_ts;
}
return $return_data;
}
, also getting the months needed
public static function getMonthsBetween($start_ts, $end_ts, $full_period = false)
{
$return_data = $month_list = [];
$current = mktime(0, 0, 0, date('m', $start_ts), date('d', $start_ts), date('Y', $start_ts));
while ($current <= $end_ts) {
$temp_date = $current;
$date = new Date($temp_date);
$month_list[] = $date;
$current = strtotime("+1 month", $current); // add a month
}
$start_date_last_month = new Date(array_first($month_list));
$start_date_last_month = $start_date_last_month->startOfMonth()->format('m-d');
$temp_end_date = new Date($start_ts);
$temp_end_date = $temp_end_date->format('m-d');
if ($start_date_last_month < $temp_end_date) {
array_shift($month_list);
}
$end_date_last_month = new Date(end($month_list));
$current_day_month = $end_date_last_month->endOfMonth()->format('m-d');
$temp_end_date = new Date($end_ts);
$end_day_of_month = $temp_end_date->format('m-d');
if ($end_day_of_month < $current_day_month) {
array_pop($month_list);
}
if (count($month_list) == 0) {
$month_list[] = $end_date_last_month->subMonth();
}
$return_data = $month_list;
if ($full_period) {
$return_data[] = $end_ts;
}
return $return_data;
}
You can do something like in this example:
$February = 2;
$October = 10;
$completedQuarters = ceil($October/3) - ceil($February/3); // = 3
What about the quarter in which the date range starts, should it also count? If it should only count if it begins in the first month of a quarter you can check for it like this:
$completedQuarters = ceil($October/3) - ceil($February/3) -1; // = 2
if($February-1%3 == 0) $completedQuarters += 1;
You´re description is not very clear, let me know if that´s what you had in mind.
Not sure if the following is what you are meaning but might be useful
$date_start='2015/03/12';
$date_end='2017/11/14';
$timezone=new DateTimeZone('Europe/London');
$start=new DateTime( $date_start, $timezone );
$end=new DateTime( $date_end, $timezone );
$difference = $end->diff( $start );
$months = ( ( $difference->format('%y') * 12 ) + $difference->format('%m') );
$quarters = intval( $months / 3 );
printf( 'Quarters between %s and %s is %d covering %d months', $start->format('l, jS F Y'), $end->format('l, jS F Y'), $quarters, $months );
/*
This will output
----------------
Quarters between Thursday, 12th March 2015 and Tuesday, 14th November 2017 is 10 covering 32 months
*/
Something like this in the function and you should be set.
use Carbon\Carbon;
$first = Carbon::parse('2012-1-1'); //first param
$second = Carbon::parse('2014-9-15'); //second param
$fY = $first->year; //2012
$fQ = $first->quarter; //1
$sY = $second->year; //2014
$sQ = $second->quarter; //3
$n = 0; //the number of quarters we have counted
$i = 0; //an iterator we will use to determine if we are in the first year
for ($y=$fY; $y < $sY; $y++, $i++) { //for each year less than the second year (if any)
$s = ($i > 0) ? 1 : $fQ; //determine the starting quarter
for ($q=$s; $q <= 4; $q++) { //for each quarter
$n++; //count it
}
}
if ($sY > $fY) { //if both dates are not in the same year
$n = $n + $sQ; //total is the number of quarters we've counted plus the second quarter value
} else {
for ($q=$fQ; $q <= $sQ; $q++) { //for each quarter between the first quarter and second
$n++; //count it
}
}
print $n; //the value to return (11)

Get the number of weeks in a month and the corresponding days

I'm pretty sure I'm overcomplicating this but atm I have no clue how to do it in another way.
I want to create an array which contains the number of a week in a month as a key and the days of this week as the value.
I'm using this function to get all days of a week:
public function getDaysOfWeek($year, $month, $day)
{
$firstMondayThisWeek = new DateTime($year . '/' . $month . '/' . $day, new DateTimeZone("Europe/Berlin"));
$firstMondayThisWeek->modify('tomorrow');
$firstMondayThisWeek->modify('last Monday');
$nextSevenDays = new DatePeriod(
$firstMondayThisWeek,
DateInterval::createFromDateString('+1 day'),
6
);
return $nextSevenDays;
}
And this function to build the array:
public function getWeekAndDays($year, $month)
{
$weeksInMonth = Carbon::createFromDate($year, $month)->endOfMonth()->weekOfMonth;
$weekBegin = Carbon::createFromDate($year, $month)->startOfMonth();
$weeks = [];
for($i=1; $i<=$weeksInMonth; $i++)
{
$collection = new \stdClass();
$collection->week = $i;
$collection->days = $this->getDaysOfWeek($year, $month, $weekBegin->day);
$weekBegin->addWeek(0);
$weeks[] = $collection;
}
return $weeks;
}
For all months except february I'm getting 5 weeks. For February I'm getting 4 weeks and so I'm not able to save the month-overlapping days.
Am I completely wrong here? What is a possible way to solve this task?
My friend I feel your pain, working with calendar data is annoying. Here's a function I wrote a while back that builds an array of weeks and days, separated by months. It's not the cleanest code but it works.
It will start from the date you pass in $today as "Y-m-d" (or default to the current date), then work back to the first week of the current month, start there, and then go forward for $scheduleMonths months building an array indexed first by month and then by week.
It's a bit hard to explain here, but it's self-contained so you can just copy/paste it into your code and then dd() the output to see what it looks like and if it works for you, and modify it from there. There's some formatting you may need to adjust as it was done that way for my specific use case, but the business logic should be sound.
You should be able to extract the number of weeks in a given month from the output (so if that's all you need you can prob simplify this once you confirm it's working).
public function getWeeks($today = null, $scheduleMonths = 6) {
$today = !is_null($today) ? Carbon::createFromFormat('Y-m-d',$today) : Carbon::now();
$startDate = Carbon::instance($today)->startOfMonth()->startOfWeek()->subDay(); // start on Sunday
$endDate = Carbon::instance($startDate)->addMonths($scheduleMonths)->endOfMonth();
$endDate->addDays(6 - $endDate->dayOfWeek);
$epoch = Carbon::createFromTimestamp(0);
$firstDay = $epoch->diffInDays($startDate);
$lastDay = $epoch->diffInDays($endDate);
$week=0;
$monthNum = $today->month;
$yearNum = $today->year;
$prevDay = null;
$theDay = $startDate;
$prevMonth = $monthNum;
$data = array();
while ($firstDay < $lastDay) {
if (($theDay->dayOfWeek == Carbon::SUNDAY) && (($theDay->month > $monthNum) || ($theDay->month == 1))) $monthNum = $theDay->month;
if ($prevMonth > $monthNum) $yearNum++;
$theMonth = Carbon::createFromFormat("Y-m-d",$yearNum."-".$monthNum."-01")->format('F Y');
if (!array_key_exists($theMonth,$data)) $data[$theMonth] = array();
if (!array_key_exists($week,$data[$theMonth])) $data[$theMonth][$week] = array(
'day_range' => '',
);
if ($theDay->dayOfWeek == Carbon::SUNDAY) $data[$theMonth][$week]['day_range'] = sprintf("%d-",$theDay->day);
if ($theDay->dayOfWeek == Carbon::SATURDAY) $data[$theMonth][$week]['day_range'] .= sprintf("%d",$theDay->day);
$firstDay++;
if ($theDay->dayOfWeek == Carbon::SATURDAY) $week++;
$theDay = $theDay->copy()->addDay();
$prevMonth = $monthNum;
}
$totalWeeks = $week;
return array(
'startDate' => $startDate,
'endDate' => $endDate,
'totalWeeks' => $totalWeeks,
'schedule' => $data,
);
}
I hope this helps you at least get started!
Something like this?
function weekOfMonth($date) {
$firstOfMonth = strtotime(date("Y-m-01", $date));
$lastWeekNumber = (int)date("W", $date);
$firstWeekNumber = (int)date("W", $firstOfMonth);
if (12 === (int)date("m", $date)) {
if (1 == $lastWeekNumber) {
$lastWeekNumber = (int)date("W", ($date - (7*24*60*60))) + 1;
}
} elseif (1 === (int)date("m", $date) and 1 < $firstWeekNumber) {
$firstWeekNumber = 0;
}
return $lastWeekNumber - $firstWeekNumber + 1;
}
function weeks($month, $year){
$lastday = date("t", mktime(0, 0, 0, $month, 1, $year));
return weekOfMonth(strtotime($year.'-'.$month.'-'.$lastday));
}
$result = [];
for ($year = 2017; $year < 2020; $year++){
for ($month = 1; $month < 13; $month++) {
$numOfWeeks = weeks($month, $year);
$result[$year][$month]['numOfWeeks'] = $numOfWeeks;
$daysInFirstWeek = 8 - date('N', strtotime($year.'-'.$month.'-01'));
$result[$year][$month]['daysPerWeek']['week_1'] = $daysInFirstWeek;
$startDay = date('d', strtotime('next Monday', strtotime($year.'-'.$month.'-01')));
for ($i = 1; $i < ($numOfWeeks - 1); $i++) {
$startDay += 7;
$result[$year][$month]['daysPerWeek']['week_'.($i+1)] = 7;
}
//last week
$result[$year][$month]['daysPerWeek']['week_'.($i+1)] = date('t', strtotime($year.'-'.$month.'-01')) - $startDay + 1;
}
}
echo json_encode($result)."\n";

Increment variable with number in variable + PHP

I have some variables like this:
$_8 = 0; $_9 = 0; $_10 = 0; $_11 = 0; $_12 = 0; $_13 = 0; $_14 = 0; $_15 = 0; $_16 = 0; $_17 = 0; $_18 = 0; $data = array();
Then I do the following:
$vistoday = $app['db']->fetchAll('SELECT `Datum Bezoek 1` FROM `psttodo-uit` WHERE CAST(`Datum Bezoek 1` AS DATE) = CURRENT_DATE AND PB = 1');
foreach($vistoday as $v){
$date = strtotime($v['Datum Bezoek 1']);
$hours = (int)date('h', $date);
$minutes = (int)date('i', $date);
if($minutes > 30)
{
$hours = $hours + 1;
$count = ${"_" . $hours} + 1;
$data[$hours] = $count;
}
else
{
$data[$hours] = ${"_" . $hours}+1;
}
}
These are the dates I pull from the database:
2014-02-27 08:25:34
2014-02-27 08:50:34
2014-02-27 08:55:34
When I output $data I get this:
array (size=2)
8 => int 1
9 => int 1
But Normally the value of key 9 should be 2. So he doesn't increment, he just adds 1 to nothing. Can somebody help me with this?
Yoг don't need variables like $_8
foreach($vistoday as $v){
$date = strtotime($v['Datum Bezoek 1']);
$hours = (int)date('h', $date);
$minutes = (int)date('i', $date);
$hours = round($hours + $minutes / 61); // if $minutes > 30 ceil else floor
$data[$hours] = isset($data[$hours]) ? $data[$hours] + 1 : 1;
}

Calculate number of days,hours,minutes,seconds only working hours

I have a php function to calculate timestamp (start date) to current (end) date that will return 1hr 30min 2s format. What I want to achieve is to calculate only from 8AM to 5PM of a working day. Anything that goes beyond will not be counted. Here is the php code I have.
class duration_computation {
function duration( $time ) {
$d[0] = array(1, "s");
$d[1] = array(60, "min");
$d[2] = array(3600, "hr");
$d[3] = array(86400, "dy");
$d[4] = array(604800, "wk");
$d[5] = array(2592000, "mth");
$d[6] = array(31104000, "yr");
$numbers = array();
$result = "";
$now = time();
$time_difference = ( $now - $time );
$seconds_left = $time_difference;
for ( $i = 6; $i > -1; $i-- ) {
$numbers[$i] = intval( $seconds_left / $d[$i][0] );
$seconds_left -= ( $numbers[$i] * $d[$i][0] );
if ( $numbers[$i] != 0 ) {
$result.= abs($numbers[$i]) . "" . $d[$i][1] . (($numbers[$i]>1)?'':'') ." ";
}
}
return $result;
}
}
$duration = new duration_computation();
echo $duration->duration($trail->duration);
Forget about date(), strtotime(), time(), etc. function, use DateTime :
Use example :
$from = '2013-09-06 15:45:32';
$to = '2013-09-14 21:00:00';
echo some_func_name($from, $to);
Output :
1 day, 22 hours, 14 minutes, 28 seconds
Function :
function some_func_name($from, $to) {
$workingDays = [1, 2, 3, 4, 5]; # date format = N
$workingHours = ['from' => ['08', '00'], 'to' => ['17', '00']];
$start = new DateTime($from);
$end = new DateTime($to);
$startP = clone $start;
$startP->setTime(0, 0, 0);
$endP = clone $end;
$endP->setTime(23, 59, 59);
$interval = new DateInterval('P1D');
$periods = new DatePeriod($startP, $interval, $endP);
$sum = [];
foreach ($periods as $i => $period) {
if (!in_array($period->format('N'), $workingDays)) continue;
$startT = clone $period;
$startT->setTime($workingHours['from'][0], $workingHours['from'][1]);
if (!$i && $start->diff($startT)->invert) $startT = $start;
$endT = clone $period;
$endT->setTime($workingHours['to'][0], $workingHours['to'][1]);
if (!$end->diff($endT)->invert) $endT = $end;
#echo $startT->format('Y-m-d H:i') . ' - ' . $endT->format('Y-m-d H:i') . "\n"; # debug
$diff = $startT->diff($endT);
if ($diff->invert) continue;
foreach ($diff as $k => $v) {
if (!isset($sum[$k])) $sum[$k] = 0;
$sum[$k] += $v;
}
}
if (!$sum) return 'ccc, no time on job?';
$spec = "P{$sum['y']}Y{$sum['m']}M{$sum['d']}DT{$sum['h']}H{$sum['i']}M{$sum['s']}S";
$interval = new DateInterval($spec);
$startS = new DateTime;
$endS = clone $startS;
$endS->sub($interval);
$diff = $endS->diff($startS);
$labels = [
'y' => 'year',
'm' => 'month',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
];
$return = [];
foreach ($labels as $k => $v) {
if ($diff->$k) {
$return[] = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
}
}
return implode(', ', $return);
}
This function can be shorter/better; but that is your job now ;)

Calculating the number of Saturdays and Sundays

How can I calculate the number of Saturdays and Sundays between two dates in php?
Is there any inbuilt function for that purpose?
There is a related question here already, Calculate business days
You can use this to subtract from 7 to get the weekend days, or similar.
I don't think there is a built in for that, but this should do the job :
$startTime = START_TIMESTAMP;
$endTime = END_TIMESTAMP;
$time = $startTime;
$count = 0;
while(date('w', $time) != 0) { // 0 (for Sunday) through 6 (for Saturday)
$time += 86400;
}
while($time < $endTime) {
$count++;
$time += 7 * 86400;
}
Let us all KISS (Keep It Simple Stupid). Why make it so complicated?
function countWeekendDays($start, $end)
{
// $start in timestamp
// $end in timestamp
$iter = 24*60*60; // whole day in seconds
$count = 0; // keep a count of Sats & Suns
for($i = $start; $i <= $end; $i=$i+$iter)
{
if(Date('D',$i) == 'Sat' || Date('D',$i) == 'Sun')
{
$count++;
}
}
return $count;
}
You can calculate it mathematically like this - Based on Roland's Answer
private function getNumberOfWeekendDays(\DateTimeInterface $startDate, \DateTimeInterface $endDate): int
{
$startNumber = (int) $startDate->format('N');
$endNumber = (int) $endDate->format('N');
$daysBetweenStartAndEnd = $endDate->diff($startDate)->d;
$weekendDays = (int) (2 * ($daysBetweenStartAndEnd + $startNumber) / 7);
$weekendDays = $weekendDays - ($startNumber == 7 ? 1 : 0) - ($endNumber == 7 ? 1 : 0);
return $weekendDays;
}
<?php
date_default_timezone_set("Europe/Lisbon");
$d1 = new DateTime("2009-06-01"); /* inclusive */
$d2 = new DateTime("2009-07-01"); /* exclusive */
$interval = $d2->diff($d1);
$number_of_days = $interval->format("%d");
$number_of_weekends = $number_of_days / 7;
$remainder = $number_of_days % 7;
if ($remainder >=2 && $d1->format("D") == "Sat")
$number_of_weekends++;
elseif ($d1->format("w") + $remainder >= 8)
$number_of_weekends++;
I may have missed by one in the last condition, be sure to check it with different starting dates. (Feel free to edit this answer if you spot an error).
there's definitely no built in function for that but you can use strtotime to loop days
$start = strtotime('2010-01-01');
$end = strtotime('2010-01-09');
function numWeekdays( $start_ts, $end_ts, $day, $include_start_end = false ) {
$day = strtolower( $day );
$current_ts = $start_ts;
// loop next $day until timestamp past $end_ts
while( $current_ts < $end_ts ) {
if( ( $current_ts = strtotime( 'next '.$day, $current_ts ) ) < $end_ts) {
$days++;
}
}
// include start/end days
if ( $include_start_end ) {
if ( strtolower( date( 'l', $start_ts ) ) == $day ) {
$days++;
}
if ( strtolower( date( 'l', $end_ts ) ) == $day ) {
$days++;
}
}
return (int)$days;
}
echo numWeekDays( $start, $end, 'saturday', false );
I searched for awhile for a simple solution that worked and decided to write my own and came up with this
$start = date('Y-m-d');
$end = date('Y-m-d', strtotime($start.' +1 year'));
$current = $start;
$count = 0;
while($current != $end){
if(date('l', strtotime($current)) == 'Saturday'){
$count++;
}
$current = date('Y-m-d', strtotime($current.' +1 day'));
};
echo $count;

Categories