Get the nearest date in the future - php

I would like to get the first date in the future from an array of dates. I've tried to write my own function, which did not get the job done.
private static function getClosestDate($date, array $dates, $last) {
$interval = array();
$now = strtotime(date('Y-m-d'));
foreach ($dates as $d) {
$dateTime = strtotime($date);
$toTime = strtotime($d);
// Do not parse dates older than today
if (strtotime($d) < $now) { continue 1; }
// Only do dates in the future
if ($toTime < $dateTime) { continue 1; }
$interval[] = abs($dateTime - $toTime);
}
// If there is no interval, use the latest date
if (!count($interval)) {
return $last;
}
asort($interval);
$closest = key($interval);
return $dates[$closest];
}
This code works but it also works the other way around. Next to that, when there is no last date, it should use the $last parameter which is the last date.
The reason I want to know this is because I have a range of dates on which people can book nights on. I want to know the difference in days to calculate the amount of nights they can book. Calculating a difference in dates is easy, but I do need to know when the next booking appears. These are provided in the $dates parameter.
What should I change in my code to get it fixed (I've tried to continue the foreach whenever the date was in the past based on the $date parameter) or can someone provide me the code?

I've fixed the problem myself. Instead of using the dates array I'm now using the date as key in the interval to retrieve the date again. This does work properly.
private static function getClosestDate($date, array $dates, $last) {
$interval = array();
$now = strtotime(date('Y-m-d'));
foreach ($dates as $d) {
$dateTime = strtotime($date);
$toTime = strtotime($d);
// Do not parse dates older than today
if (strtotime($d) < $now) { continue 1; }
// Only do dates in the future
if ($toTime < $dateTime) { continue 1; }
$interval[$d] = abs($dateTime - $toTime);
}
// If there is no interval, use the latest date
if (!count($interval)) {
return $last;
}
asort($interval);
$closest = key($interval);
return $closest;
}

Related

Check if any of the value in resultant array similar to values of existing array with codeigniter

I really need help to display a month list view in which deposit received month should be green in color and no-deposit month should be in red color.
I have stuck in comparing the array values. and I am using codeigniter.
I have a table with start-date and end-date by using this I have created an array to display all months in between these dates. Please find the code which I have used to do it below:
$datefrom = strtotime($showrangecalendar['chitdate_start']);
$dateto = strtotime($showrangecalendar['chitdate_end']);
$start_date = date('Y-m-d', $datefrom);
$end_date = date('Y-m-d', $dateto);
$day = 2.628e+6; // Day in Months
$format = 'Y-F'; // Output format (see PHP date funciton)
$sTime = strtotime($start_date); // Start as time
$eTime = strtotime($end_date); // End as time
$numDays = round(($eTime - $sTime) / $day) + 1;
$days = array();
for ($d = 0; $d < $numDays; $d++) {
$days[] = date($format, ($sTime + ($d * $day)));
}
This code got me the Days list and I have displayed this using foreach statement to a table.
Now I have another table (accounts) with columns such as ID, Book_number, Deposit_Amount and Deposit_Month. Here the problem starts I have fetched data from "accounts" and loaded to my view using the below code from controller
$data['show_account']= $this->Accounts_model->get_all_accounts($sess_data);
My Model (Accounts_Model) is as shown below:
public function get_all_accounts($id)
{
$this->db->select("*");
$this->db->order_by('book_id','desc');
$this->db->from("accounts");
$this->db->where('bh_id',$id);
$query = $this->db->get();
return $query->result();
}
If I run the belowcode:
foreach($show_account as $values){
echo $values->deposit_month;
}
Its getting me the array result of all deposit month. Suppose I have data as 2018-Sep and 2018-Oct, These 2 months column should turn green in the above mentioned $days array.
Hope I have explained my requirement clearly. Please help me with this as I am already spent long hours in this.
Thanks in Advance.
Updated:
Now could you please check my model as follows:
public function get_dep_month($bh_id)
{
$this->db->select('deposit_month');
$this->db->from('accounts');
$this->db->where('bh_id',$_SESSION['bh_id']);
$this->db->order_by('deposit_month','asc');
$query = $this->db->get();
return $query->result_array();
}
And My Controller is as follows:
$sess_data = $this->session->userdata('bh_id');
$data['depM_array'] = $this->Accounts_model->get_dep_month($sess_data);
Now please check my View as follows:
<?php
// option 2
foreach($days as $day) { // using your already exist $day for-loop for display
if (!in_array($day, $depM_array)){
$calhilight = "calgreen";
}
else{
$calhilight = "calred";
}
?>
<li class="<?php echo $calhilight; ?>"><?= $day ?></li>
<?php
}
?>
But as my doposit_month column having only 2 values ie: 2018-Sep and 2018-Oct, Instead of getting green color for those 2 values, I am getting Green for who li element. Not getting where I have done wrong.
This is the Current page view which I am getting, Actually I am expecting a calendar view with 2 green fields for 2018-Sep and 2018-Oct and all other fields in Red
Performed Var Dumb in arrays:
Pls check this screenshot
FYI:
Below is the code from where I am getting $days array.
<?php
$datefrom = strtotime($showrangecalendar['chitdate_start']);
$dateto = strtotime($showrangecalendar['chitdate_end']);
$start_date = date('Y-m-d', $datefrom);
$end_date = date('Y-m-d', $dateto);
$day = 2.628e+6; // Day in Months
$format = 'Y-F'; // Output format (see PHP date funciton)
$sTime = strtotime($start_date); // Start as time
$eTime = strtotime($end_date); // End as time
$numDays = round(($eTime - $sTime) / $day) + 1;
$days = array();
for ($d = 0; $d < $numDays; $d++) {
$days[] = date($format, ($sTime + ($d * $day)));
}
?>
Here $start_date & $end_date is fetching from another table named chitdate.
Thanks Again.
You can use array-diff or simple for-loop.
Consider the following simple example:
$days = array("2018-September", "2018-October", "2018-November", "2018-December");
$deposit_month = array("2018-September", "2018-October");
// option 1:
$diff = array_diff($days, $deposit_month);
// now $diff has: "2018-November" and "2018-December"
// option 2
foreach($days as $day) { // using your already exist $day for-loop for display
if (!in_array($day, $deposit_month))
// color in red as not found in "deposit_month"
else
// color in green
}
Nicer writing for that if can be as:
$color = in_array($day, $deposit_month) ? "Green" : "Red";

Date & time comparison operators >= not showing dates that are equal

I am trying to show events that occur either today or on a later date where today is specifically the problem.
public function getInspirationsMeetingIds()
{
$ids = [];
if (($inspirationMeetings = $this->getCustomField('meetings'))) {
foreach ($inspirationMeetings as $meeting) {
$row = new Inspiration($meeting['meeting']);
$dateFrom = $row->getCustomField('date');
if (strtotime($dateFrom) >= time()) {
$ids[] = $row->getId();
}
}
}
return $ids;
}
For some reason this will only show events that are greater than time() and not the events that are today, but then when i try this:
if (strtotime($dateFrom) <= time()) {
$ids[] = $row->getId();
}
Today's and older events are shown.
I think you need to add a timestamp to your datefrom.
Strtotime will add noon if time is omitted.
See this example https://3v4l.org/cYKO4
if (strtotime($dateFrom ) >= strtotime(date("Y-m-d 00:00"))) {
Will make it show all of datefrom
Edit added the 00:00 at the wrong side
Use the DateTime class http://php.net/manual/en/class.datetime.php
time() gives seconds since Jan 1st 1970. The chance that you hit the exact second is very small, so it will hardly ever match.
Instead, create a date with the time.
$date = new DateTime($dateFrom); // or DateTime::createFromFormat($format, $dateFrom);
$today = new DateTime();
if ($date >= $today) {
// should work
}

how may days are between two dates in specific year

I'm solving following task>
I have two dates - $start and $end and target year as $year.
dates are php DateTime objects, year is string.
add:dates comes acutaly from MySql field from this format 2017-02-01 15:00:00 ...
add2: if end date is null, I use todays date ...
I need to figure out how many days are between these two dates for specific year.
Also I need to round it for whole days, even if one minute in day should be counted as whole day ...
I can solve it by many many following ifs.
Expected results for values I used in example are
2016 is 0 days
2017 is 31 days
2018 is 32 days
2019 is 0 days
But are there any elegant php functions which can help me with this ?
What I did it seems to be wrong way and giving bad results - seems it counts full days only ...
Please see my code here >
<?php
$diff = True;
$start = DateTime::createFromFormat('Y-m-d H:i:s','2017-12-01 23:05:00');
$end = DateTime::createFromFormat('Y-m-d H:i:s','2017-12-03 00:05:00');
$year = '2017';
// start date
if ($start->format('Y')<$year)
{
$newstart = new DateTime('first day of January '. $year);
}
if ($start->format('Y')==$year)
{
$newstart = $start;
}
if ($start->format('Y')>$year)
{
$result = 0;
$diff = False;
}
// end date
if ($end->format('Y')>$year)
{
$newend = new DateTime('last day of December '. $year);
}
if ($end->format('Y')==$year)
{
$newend = $end;
}
if ($end->format('Y')<$year)
{
$result = 0;
$diff = False;
}
// count if diff is applicable
if ($diff)
{
$result = $newend->diff($newstart)->format("%a");
}
echo $result;
?>
But are there any elegant php functions which can help me with this ?
Read about DateTime::diff(). It returns a DateInterval object that contains the number of days (in $days) and by inspecting the values of $h, $i and $s you can tell if you have to increment it to round the result. You can also use min() and max() to crop the time interval to the desired year.
function getDays(DateTimeInterface $start, DateTimeInterface $end, $year)
{
// Extend the start date and end date to include the entire day
$s = clone $start; // Don't modify $start and $end, use duplicates
$s->setTime(0, 0, 0);
$e = clone $end;
$e->setTime(0, 0, 0)->add(new DateInterval('P1D')); // start of the next day
// Crop the input interval to the desired year
$s = min($s, new DateTime("$year-01-01 00:00:00"));
$year ++;
$e = max(new DateTime("$year-01-01 00:00:00"), $end); // start of the next year
if ($e <= $s) {
// The input interval does not span across the desired year
return 0;
}
// Compute the difference and return the number of days
$diff = $e->diff($s);
return $diff->days;
}
$d1 = strtotime('2017-05-15');
$d2 = strtotime('2017-05-31');
$div = 24 * 3600;
echo abs(($d2 - $d1) / $div); // 16 days
Just make sure and ONLY have the date part and you shouldn't have to deal with rounding.

Skip a value in do-while loop

I've got a script that sets up a array of dates which form a dropdown. The array is set up by some variables about the current date and an fixed offset for the last date (two weeks). Stripped down this is what it looks like:
public function getDatesOptionArray()
{
$datesArray = array();
$displayDate = Mage::getModel('core/locale')->storeDate();
$displayDate->add($this->_startDaysOffset, Zend_Date::DAY);
$dayOffset = $this->_startDaysOffset;
do
{
$dayofweek = date('w', strtotime($displayDate->toString(Varien_Date::DATE_INTERNAL_FORMAT)));
$datesArray[$displayDate->toString(Varien_Date::DATE_INTERNAL_FORMAT)] = Mage::helper('core')->formatDate($displayDate, Mage_Core_Model_Locale::FORMAT_TYPE_FULL);
$displayDate->add('1', Zend_Date::DAY);
$dayOffset++;
} while ($dayOffset <= $this->_endDaysOffset);
return $datesArray;
}
The thing is I want to leave out all the 'Sunday' options, and I've got the $dayofweek variable for each, where sunday is 0. I've tried to wrap the whole thing inside the do function in an if-statement (if $dayofweek !== 0), set an if ($dayofweek == 0) { continue;} and every other trick I could think of, but I get only either of these results
1: Only the first date is shown
2: All dates until the first sunday are shown, none after that
3: All dates are shown
I think I might be missing the point on the do-while loop; how do I exclude if $dayofweek == 0?
For me is something like this, i use while because Do...While(...) the first time it will not check your condition you'll enter your loop at last 1 time, and when you use while(...){} every time your program will check your condition
public function getDatesOptionArray()
{
$datesArray = array();
$displayDate = Mage::getModel('core/locale')->storeDate();
$displayDate->add($this->_startDaysOffset, Zend_Date::DAY);
$dayOffset = $this->_startDaysOffset;
while ($dayOffset <= $this->_endDaysOffset)
{
$dayofweek = date('w', strtotime($displayDate->toString(Varien_Date::DATE_INTERNAL_FORMAT)));
if ($dayofweek != 0) {
$datesArray[$displayDate->toString(Varien_Date::DATE_INTERNAL_FORMAT)] = Mage::helper('core')->formatDate($displayDate, Mage_Core_Model_Locale::FORMAT_TYPE_FULL);
}
$displayDate->add('1', Zend_Date::DAY);
$dayOffset++;
}
return $datesArray;
}
Let say the array is
$date = array(
2017-10-12,
2017-10-13,
2017-10-14,
2017-10-15,
2017-10-16,
2017-10-17,
2017-10-18,
2017-10-19,
2017-10-20,
2017-10-21,
2017-10-22,
2017-10-23,
2017-10-24,
2017-10-25
);
Now do this for remove sunday's dates
$i=0;
do{
if(date('w',strtotime($date[$i]))>0) $dateArray[] = $date[$i];
$i++;
} while ($i<count($date));
echo "<pre>";print_r($dateArray);
return $datesArray;
$dateArray will give you dates without sundays.

Dividing timestamps into weeks

I'm looking to divide a span between two timestamps into weeks, in PHP.
Essentially, what I want to do is:
function divide_into_weeks($start_time, $end_time) {
// Iterate back from the end time,
// creating an array of timestamps broken into calendar weeks.
$done = FALSE;
$divider = $end_time;
for ($i = 0; !$done; $i++) {
$timeslots[$i]->end = $divider;
// Set the start time to the start of the current week.
$timeslots[$i]->start = strtotime("monday this week", $divider);
if ($timeslots[$i]->start <= $start_time) {$done = TRUE;}
// If we loop again, end the previous week one second before the start of this week.
$divider = $timeslots[$i]->start - 1;
}
}
However, the function hits an infinite loop.
Why? Because...
strtotime("monday this week", $monday_of_next_week -1) == $monday_of_last week;
... is true, strangely. This is unlike other strtotime operations I've tested. In other cases, you knock a second off, repeat, and it iterate back by one of whatever unit you've asked for. But not for Monday (or Sunday) of this week.
For example:
$today = strtotime("midnight");
$yesterday = strtotime("midnight", $today -1);
...produces sensible results.
But my attempts to use the same technique with "monday of this week" or "sunday of this week" have proven fruitless, so far.
So, can someone show how to iterate timestamps back by weeks?
I think this would help you:
function divide_into_weeks($start_time, $end_time, $tz) {
$tz = new DateTimezone($tz);
$start = new DateTime("#$start_time");
$end = new DateTime("#$end_time");
$start ->setTimezone($tz)->modify($start->format('o-\WW-1 00:00:00'));
$end ->setTimezone($tz)->modify($end ->format('o-\WW-7'));
$weeks = [];
do {
$weeks[] = [
'start' => clone $start,
'end' => new DateTime($start->format('o-\WW-7 23:59:59'), $tz),
];
$start->modify('+7 day');
} while ($start < $end);
return $weeks;
}
demo
This ended up being the workaround that did the trick:
(With some of the irrelevant bits trimmed.)
function divide_timespan_into_units($start_second, $end_second, $time_unit) {
$timeslots = array();
// The time_formatter guides strtotime in what parts of a timestamp to trim off to leave timestamps split on calendar divisions.
switch ($time_unit) {
case "hour":
$time_formatter = 'Y-m-d H:00:00';
break;
case "day":
$time_formatter = 'Y-m-d 00:00:00';
break;
case "week":
$time_formatter = "monday this week";
break;
}
$done = FALSE;
$divider = $end_second;
for ($i = 0; !$done; $i++) {
$timeslots[$i] = (object) array('end' => $divider);
if ($time_unit == "week") {
// Dividing timestamps up into calendar weeks requires special handling.
//
// Note on the strange constants "86399" & "86400" in the two lines following:
// This is a workaround for a fluke in how strtotime treats weeks:
//
// strtotime can't decide if Sunday or Monday is the start of the week.
// You have to force it by rolling back a full day plus one second from the start of Monday, to get into the previous week.
//
// Simply rolling back a second from Sunday by 1 second doesn't get you into the previous week,
// because if you ask for "this Sunday" on any day other than Sunday, it will return the coming Sunday, not the past Sunday.
// However, if you back up 1 second from the stroke of midnight Monday, and ask what week you're in,
// you'll be told that you're still in the same week.
//
// This nudge of an extra 86400 seconds (a full extra day) back and forth forces the week to click over.
//
// This will likely be settled in a later version of PHP, but as of 5.3.5 (when this is being coded) it's an issue.
$timeslots[$i]->start = strtotime("monday this week", $divider-86400);
$divider = $timeslots[$i]->start+86399;
} else {
$timeslots[$i]->start = strtotime(date($time_formatter, $divider));
}
if ($timeslots[$i]->start <= $start_second) {$done = TRUE;}
$divider = $timeslots[$i]->start - 1;
}
}

Categories