Add missing dates to an array - php

I have the following array:
Array ( [2010-10-30] => 1 [2010-11-11] => 1 [2010-11-13] => 11 )
I am trying to fill in the array with all the missing dates between the first and last elements. I was attempting using the following but got nowhere:
foreach($users_by_date as $key => $value){
$real_next_day = date($key, time()+86400);
$array_next_day = key(next($users_by_date));
if($real_next_day != $array_next_day){
$users_by_date[$real_next_day] = $value;
}
}

The DateTime, DateInterval and DatePeriod classes can really help out here.
$begin=date_create('2010-10-30');
$end=date_create('2010-11-13');
$i = new DateInterval('P1D');
$period=new DatePeriod($begin,$i,$end);
foreach ($period as $d){
$day=$d->format('Y-m-d');
$usercount= isset($users_by_date[$day]) ? $users_by_date[$day] :0;
echo "$day $usercount";
}

I have been waiting for a chance to try out DateTime and DateInterval objects in PHP 5.3, your question was the perfect opportunity to do just that. Note that this code will not work with PHP versions earlier than 5.3
<?php
$dates = array('2010-10-30' => 1, '2010-11-01' => 1, '2010-11-13' => 1);
// get start and end out of array
reset($dates);
$start = new DateTime(key($dates));
end($dates);
$end = new DateTime(key($dates));
foreach (new DatePeriod($start, new DateInterval('P1D'), $end) as $date) {
$dateKey = $date->format('Y-m-d'); // get properly formatted date out of DateTime object
if (!isset($dates[$dateKey])) {
$dates[$dateKey] = 1;
}
}
print_r($dates);

The functions you are looking for (but not using in your example) are strtotime & diff
You would get the day range between your two dates $numdiff, and simply do something in a loop that would do:
for ($i=1; $i<=$numdiff; $i++) {
echo date("Y-m-d", strtotime("2010-10-30 +".$i." day"));
}
Result should be something like:
2010-10-31
2010-11-01
2010-11-02...
You could then pop that into your array as needed. Hope that gets you started in the right direction.

For indexes using timestamps, you can generate your array easily using the range function.
$startStamp = ...
$endStamp = ...
$oneDay = 60*60*24;
$timeIndexes = range($startStamp, $endStamp, $oneDay);
$filler = array_fill(0, count($timeIndexes), null);
$timeArray = array_combine($timeIndexes, $filler);
I am not sure what values you want in the array, but hopefully it is relatively straight-forward from here.
If you are going to be converting every timestamp to a formatted date string anyhow and would just prefer to use date strings in the first place, consider this modification.
$dateStringIndexes = array_map(
function ($t) {
return date('Y-m-d', $t);
},
$timeIndexes
);
Of course, since you are on PHP 5.2, you will likely have to compromise with a foreach loop instead of the closure.

This is the PHP 5.2 capable function I came up with that works.
Thanks guys
reset($users_by_date);
$date = key($users_by_date);
end($users_by_date);
$end = key($users_by_date);
while(strtotime($date) <= strtotime($end)){
$datearray[] = date("Y-m-d", strtotime($date));
$date = date("Y-m-d", strtotime("+1 day", strtotime($date)));
}
foreach($datearray as $key => $value){
if(!isset($users_by_date[$value])){
$users_by_date[$value] = 0;
}
}
ksort($users_by_date);

Related

My json file says ArrayArrayArray after sorting and storing a new array with php

So I need help with what should be a simple thing. I am retrieving this giant json file that has years worth of events. Sorting through them to get just last years and this years events. Storing them as a new array and trying to store them as another .json file so that I can use that with a calendar script I've written in Javascript.
My problem is that the resulting .json file just says ArrayArrayArrayArray for all the information instead of all the multidimensional arrays it should have for the information.
$string = file_get_contents('events.json');
$json = json_decode($string, true);
$new_array = array();
// variables to set the time available for calendar
$year = date("Y-m-d");
$startTime = date("Y-m-d", strtotime($year . '-1 year'));
$startTime = date('Y-m', strtotime($startTime));
$endTime = date('Y-m-d', strtotime($year . '+1 year'));
$endTime = date('Y-m', strtotime($endTime));
foreach($json as $key => $value){
$eventTime = $value['StartTime'];
$eventTime = date('Y-m', strtotime($eventTime));
if($eventTime >= $startTime && $eventTime <= $endTime){
array_push($new_array, $value);
//array_push($new_array, array($key => $value));
}
}
// Store contents of array for Calendars use.
json_encode($new_array);
file_put_contents('events1.json', $new_array);
The json_encode function doesn't modify its argument. It returns a new value. Try either
file_put_contents('events1.json', json_encode($new_array));
or
$encoded_array = json_encode($new_array);
file_put_contents('events1.json', $encoded_array);

PHP days difference calculation error

I have some PHP code to calculate the number of days between two specific dates. The difference should not count Sundays and Saturdays. Also, I have an array of dates, which includes holidays, which also need to be skipped.
I gave the starting date as 01-05-2015 and ending date as 01-06-2015. I gave the entire days in the month of may as array. Thus the difference should be 1 day. But I am getting the output as 7. What is the problem? Here is the code.
function dateRange($first, $last) {
$dates = array();
$current = strtotime($first);
$now = $current;
$last = strtotime($last);
while( $current <= $last ) {
if (date('w', $current) != 0){
$dates[] = date('d-m-Y', $current);
}
$current = strtotime('+1 day', $current);
}
unset($dates[0]);
return $dates;
}
$datea = "01-05-2015";
$date = "01-06-2015";
$hdsarray = array("1-05-2015","2-05-2015","4-05-2015","5-05-2015","7-05-2015","8-05-2015","9-05-2015","11-05-2015","12-05-2015","14-05-2015","15-05-2015","16-05-2015","18-05-2015","19-05-2015","21-05-2015","22-05-2015","23-05-2015","25-05-2015","26-05-2015","28-05-2015","29-05-2015","30-05-2015");
$datesarray = dateRange($datea, $date);
$result = array_diff($hdsarray,$datesarray);
$date_diff = sizeof($result);
echo $date_diff;
The only problem I can see is in the usage of array_diff, It actually includes the sat and sun which is excluded by dateRange function, if not found in holidays list.
Instead, you can pass your holiday dates in dateRange function, and filter over there.
function dateRange($first, $last, $excludeDates) {
$dates = array();
$current = strtotime($first);
$now = $current;
$last = strtotime($last);
while( $current <= $last ) {
if (date('w', $current) != 0 && date('w', $current) != 6 && !in_array(date('j-m-Y', $current), $excludeDates)){
$dates[] = date('d-m-Y', $current);
}
$current = strtotime('+1 day', $current);
}
return $dates;
}
$datea = "01-05-2015";
$date = "01-06-2015";
$hdsarray = array("1-05-2015","2-05-2015","4-05-2015","5-05-2015","7-05-2015","8-05-2015","9-05-2015","11-05-2015","12-05-2015","14-05-2015","15-05-2015","16-05-2015","18-05-2015","19-05-2015","21-05-2015","22-05-2015","23-05-2015","25-05-2015","26-05-2015","28-05-2015","29-05-2015","30-05-2015");
$datesarray = dateRange($datea, $date, $hdsarray);print_r($datesarray);
Result:
Array
(
[0] => 06-05-2015
[1] => 13-05-2015
[2] => 20-05-2015
[3] => 27-05-2015
[4] => 01-06-2015
)
All the 5 dates come in the result, are not sat, sun, and also not there in holidays list.
It seems that there are several problems here. First, as pointed out by others the condition:
if (date('w', $current) != 0){
only checks for Sundays, if it should also include Saturday's it should be:
if (date('w', $current) != 0 && date('w', $current) != 6){
Secondly, it seems that the $hdsarray array does not contain all of the days in May. It seems that all of the Wednesdays are missing.
The third issue is that you are using array_diff on two arrays, one containing Dates and the other ones containing Strings. From the documentation:
Two elements are considered equal if and only if (string) $elem1 ===
(string) $elem2. In words: when the string representation is the same.
In your $hdsarray you are using "1-05-2015" to denote the first day of the month, while:
echo date('d-m-Y', strtotime("1-05-2015"));
results in "01-05-2015". You will need to add an additional 0 in $hdsarray for these dates or work with dates as well.
Last but not least, the current algorithm will not work correctly if the $hdsarray contains dates for a Saturday or Sunday, the result of array_diff will still contain these dates. Since you want to filter the result of daterange the array_filter function might be more suitable.
Despite an answer has already been provided, here is a little snippet with a class handling everything for you:
<?php
class dateRange {
protected $start, $end, $daysToExclude, $datesToExclude;
function __construct($dateStart, $dateEnd, $daysToExclude, $datesToExclude) {
$this->start = $dateStart;
$this->end = $dateEnd;
$this->daysToExclude = $daysToExclude;
$this->datesToExclude = $this->fixFormat($datesToExclude);
}
public function getRangeLength ($callback = null) {
$tmp = array();
$now = strtotime($this->start);
$to = strtotime($this->end);
while ( $now <= $to ) {
if (!in_array(date("w", $now), $this->daysToExclude)) {
$tmp[] = date('d-m-Y', $now);
}
$now = strtotime('+1 day', $now);
}
is_callable($callback) && call_user_func($callback, array_diff($tmp,$this->datesToExclude));
return count(array_diff($tmp,$this->datesToExclude));
}
private function fixFormat($el) {
if (!is_array($el)) {
return false;
}
else {
foreach ($el as &$value) {
$value = date("d-m-Y",strtotime($value));
}
return $el;
}
}
}
?>
I decided to keep your current logic (using date_diff), but I thought that, in the future, you may have your boss telling you "You know what? I don't want to have mondays aswell there" so, with the current system, you will have to edit your function manually and, perhaps, you won't remember anymore what you did.
The class above expects four parameters:
dateStart (d-m-Y format)
dateEnd (d-m-Y format)
daysToExclude (array with IDs of the days to exclude -> example array(0,6) to exclude saturdays and sundays).
datesToExclude (array with the dates to exclude, every format supported).
The class will automatically fix the datesToExclude array format in order to allow you to use date_diff.
Here is an example to use it, following your case:
<?php
$dateStart = "01-05-2015";
$dateEnd = "01-06-2015";
$daysToExclude = array(0,6);
$exclusions = array(
"1-05-2015",
"2-05-2015",
"4-05-2015",
"5-05-2015",
"7-05-2015",
"8-05-2015",
"9-05-2015",
"11-05-2015",
"12-05-2015",
"14-05-2015",
"15-05-2015",
"16-05-2015",
"18-05-2015",
"19-05-2015",
"21-05-2015",
"22-05-2015",
"23-05-2015",
"25-05-2015",
"26-05-2015",
"28-05-2015",
"29-05-2015",
"30-05-2015"
);
$dateRange = new dateRange($dateStart, $dateEnd, $daysToExclude, $exclusions);
echo $dateRange->getRangeLength();
?>
The code above outputs 5.
The function getRangeLength also accepts a callback and will return the array resulting of the date_diff operation, so you can also:
$dateRange->getRangeLength(function($res) {
echo "Literal output: <br />";
print_r($res);
echo "<br />count is: " . count($res);
});
The above outputs:
Literal output:
Array ( [3] => 06-05-2015 [8] => 13-05-2015 [13] => 20-05-2015 [18] => 27-05-2015 [21] => 01-06-2015 )
count is: 5
So if you later will need to remove mondays too, you will be able to easily do that by changing daysToExclude to array(0,1,6);
Hope this will be helpful to anyone else who will need this, despite a valid answer has already been posted.
Your original problem, in any case, was pretty much related to the array_diff function, which was NOT doing its job because of the fact that the date strings were not compatible, because "1-01-2015" is different from "01-01-2015", unless you first convert BOTH of them to times and then back to dates.
The code is fine (except that $nowis not used at all). The problem is the $hdsarray is wrong:
It should $hdsarray = array("01-05-2015", "02-05-2015", "04-05-2015", "05-05-2015", "07-05-2015", "08-05-2015", "09-05-2015",...);
date('d-m-Y', $current);will always return a leading 0 for all days between 1 and 9.
That's where the difference comes from.

PHP: How to fill an array with dates (Y-m-d) as keys [duplicate]

This question already has answers here:
I have 2 dates in PHP, how can I run a foreach loop to go through all of those days?
(13 answers)
Closed 1 year ago.
I want to fill an array with values. The keys of this array should be readable dates in the format 'YEAR-MONTH-DAY'. Starting point is '2010-5-25'.
The process should abort on the current date. Obviously, all dates should be valid dates.
I thought about doing this loop. But it seems that PHP is not able to check the condition of more than one in a 'for' loop. It does not give me any warnings or errors, though.
for ($d = 25, $m = 5, $y = 2010,
$this_day = date('j'),
$this_month = date('n'),
$this_year = date('Y');
($y <= $this_year) && ($m <= $this_month) && ($d <= $this_day);
$d++)
{
$values[$y.'-'.$m.'-'.$d] = 0; //fill array
$d++;
if(!checkdate($m, $d, $y)){
$d = 1;
$m++;
if($m > 12) { $m = 1; $y++; }
}
}
Doing this with nested loops would be rather painful.
One solution would be to use integer times as keys and then convert them later in another loop into the readable dates.
Is there a more efficient way?
Here is code that does some error checking, for example, valid dates provided and start date cannot be bigger than end date:
function arrayKeyDates($start, $end='now') {
// can use DateTime::createFromFormat() instead
$startDate = new DateTime($start);
$endDate = new DateTime($end);
if ($startDate === false) {
// invalid start date.
return;
}
if ($endDate === false) {
// invalid end date.
return;
}
if ($startDate > $endDate) {
// start date cannot be greater than end date.
return;
}
$dates = array();
while($startDate <= $endDate) {
$dates[$startDate->format('Y-n-j')] = 0;
$startDate->modify('+1 day');
}
return $dates;
}
print_r(arrayKeyDate('2014-11-30'));
I get the following output:
Array
(
[2014-11-30] => 0
[2014-12-1] => 0
[2014-12-2] => 0
[2014-12-3] => 0
[2014-12-4] => 0
[2014-12-5] => 0
[2014-12-6] => 0
[2014-12-7] => 0
)
Error handling code is left to you.
UPDATE (DateTime::createFromFormat)
If you want to create the DateTime objects using a custom format you can, in my function, you can do something like this:
$startDate = DateTime::createFromFormat('Y-n-j', $start);
Where $start would have the value 2010-5-25.
For more information, see: http://php.net/manual/en/datetime.createfromformat.php
$startDate = new \DateTime('2010-05-25');
$endDate = new \DateTime();
$interval = new \DateInterval('P1D');
$period = new \DatePeriod ($startDate, $interval, $endDate);
$dates = array();
foreach ($period as $key => $date) {
$dates[$date->format('Y-m-d')] = null;
}
var_dump($dates);
Simply you can try using strtotime(). Example:
$values = array();
$oldDate = strtotime('2010-05-25');
while($oldDate <= time()){
$values[date('Y-m-d', $oldDate)] = 'Your value';
$oldDate += 86400;
//Other codes
}
I know this is an old question, but might be helpful for new viewers a shorter version
$dummyArray = array_fill(1, 7, 0);
$dates = array_flip(array_map(function($val, $idx) {
return date_create('2010-5-25')->modify('-' . $idx . ' days')->format('Y-m-d');
}, $dummyArray, array_keys($dummyArray)));
I'm basically generating a dummy array which is going to have the numbers of days I want to extract as index, and then converting those to dates with array_map, after which I just flip the array to have the dates as keys instead of values
I took the liberty to clean up your code a little to make it readable:
<?php
$this_day = date('j');
$this_month = date('n');
$this_year = date('Y');
echo sprintf("Today: d-m-y: %s-%s-%s\n", $this_day, $this_month, $this_year);
for ($d = 25, $m = 5, $y = 2010;
($y <= $this_year) && ($m <= $this_month) && ($d <= $this_day);
$d++) {
echo sprintf("Date: d-m-y: %s-%s-%s\n", $d, $m, $y);
$values[$y.'-'.$m.'-'.$d] = 0; //fill array
$d++;
if(!checkdate($m, $d, $y)){
$d = 1;
$m++;
if($m > 12) { $m = 1; $y++; }
}
}
This shows that the code works perfectly well. That is if you chose the correct condition!
Today is the 07th, but your initial values start with the 25th which falsifies the condition. To verify chose a start day of '02' and see the output...
I guess you want to re-check your condition. Most likely it is something else you want to express...
First of all; the loop doesn't execute because you are checking separately if year number is lower then current year number, etc. But today is the 7th, and you start at the 25th of may 2010:
$d = 25;
$this_day = date('j'); // today: 7
$loop = $d <= $this_day; // evaluates to false
Because the 'day-check' evaluates to false, the whole expression evaluates to false. So the loop will only start to run on december the 25th.
You can better use the DateTime object to construct the dates and perform modifications on the created object. This will also safe you a lot of sweat with stuff like leap years etc. Example:
for (
$start = new DateTime('2010-05-25'),
$today = new DateTime('now') ;
$start->diff($today)->format('%a') >= 0 ;
$start->modify('+1 day')
) {
$values[$start->format('Y-m-d')] = 0;
}
easy does it!

PHP Get recurring week from specific start date

I'm looking into trying to set up and array that will look something like this:
$dates = array(
[0] => "07/11/2013",
[1] => "14/11/2013",
[2] => "21/11/2013",
[3] => "28/11/2013",
[4] => "05/12/2013",
[5] => "12/12/2013");
I'm willing to use this, but as I want this to reoccur again next year I'd prefer to have PHP do this and enter it into an array for me. I know how to limit it to a specific amount that I want, but I don't know how to add a week onto the current date or specific date if I wanted to start 08/11/2013 for example.
I've had a quick look and I can't seem to find anything that does this.
I just need a script to add a week to the current date, at the moment this is every Thursday, and then add that to the array.
My only problem is I'm not sure how to specify a date, and then add a week every time. I assume a for loop would be best here.
Use DateTime class. DateInterval and DatePeriod classes were introduced in PHP 5.3.0, so the below solution works for only PHP >= 5.3.0:
$start = new DateTime('2013-11-07');
$end = new DateTime('2013-12-31');
$interval = new DateInterval('P1W'); // one week
$p = new DatePeriod($start, $interval, $end);
foreach ($p as $w) {
$weeks[] = $w->format('d-m-Y');
}
Demo!
As Glavic notes in the comments below, this can also be done in previous versions of PHP using the modify() method:
$start = new DateTime('2013-11-07');
$end = new DateTime('2013-12-31');
$weeks = array();
while ($start < $end) {
$weeks[] = $start->format('d-m-Y');
$start->modify('+1 week');
}
Demo.
You can use strtotime('+1 week', $unixTimestamp) for this:
<?php
$startDate = '2013-11-07';
$endDate = '2013-12-31';
$startDateUnix = strtotime($startDate);
$endDateUnix = strtotime($endDate);
$dates = array();
while ($startDateUnix < $endDateUnix) {
$dates[] = date('Y-m-d', $startDateUnix);
$startDateUnix = strtotime('+1 week', $startDateUnix);
}
print_r($dates);
?>
Outputs:
Array
(
[0] => 2013-11-07
[1] => 2013-11-14
[2] => 2013-11-21
[3] => 2013-11-28
[4] => 2013-12-05
[5] => 2013-12-12
[6] => 2013-12-19
[7] => 2013-12-26
)
DEMO
(format the date() call in any way you want to get the format you want).
strtotime does what you need
$nextWeek = strtotime('08/11/2013 + 1 week');
If you need that 8 times, loop it 8 times. You can make a function with $start and $numWeek to return an array with $numWeeks+1 values (the start added)
function createDateList($start, $numWeeks){
$dates = array($start);// add first date
// create a loop with $numWeeks illiterations:
for($i=1;$<=$numWeeks; $i++){
// Add the weeks, take the first value and add $i weeks to it
$time = strtotime($dates[0].' + '.$i.' week'); // get epoch value
$dates[] = date("d/M/Y", $time); // set to prefered date format
}
return $dates;
}
would the strtotime() function work here?
$nextweek = strtotime('thursday next week');
$date = date('d/m/Y', $nextweek);
To create a 5 element array containing today (or this thursday) and the next 4:
for ($a = 0; $a < 5; $a++)
{
$thur = date('d/m/Y', strtotime("thursday this week + $a weeks"));
$dates[] = $thur;
}

Split time by day of the week with an interval defined by two Zend_Date

I have two Zend_Date that represent an interval :
$start = new Zend_Date($punch->getStart());
$end = new Zend_Date($punch->getEnd());
$nbHours = $start->sub($end , Zend_Date::HOUR);
$nbMinutes = $start->sub($end , Zend_Date::MINUTE);
$hoursTotal = $nbHours->get(Zend_Date::HOUR);
$minutesTotal = $nbMinutes->get(Zend_Date::MINUTE);
Is there an simple way to split the interval by day of the week with Zend_Date when the interval > 24 hours?
For example, if I have an interval from Monday 8am to Tuesday 4:30pm, I would like to have an array containing monday = 16h and tuesday = 16:30.
You don't need to use Zend_Date for this, in fact it is probably better not to. You should use the date/time classes in PHP instead.
If I understand your question correctly you want an array of days and the hours worked for those days.
I first created a mock class to reflect your code example, I have assumed it is returning timestamps:-
class Punch
{
public function getStart()
{
return time();
}
public function getEnd()
{
return strtotime('+36 hours 45 minutes');
}
}
Then we set up the DateTime objects-
$Punch = new Punch();
$start = new DateTime();
$start->setTimestamp($Punch->getStart());
$end = new DateTime();
$end->setTimestamp($Punch->getEnd());
Then we use a DateInterval object to generate our iterable DatePeriod:-
$interval = new DateInterval('PT1M');
$minutes = new DatePeriod($start, $interval, $end);
Then we simply iterate over it counting the minutes worked in each day:-
$format = 'l';
foreach($minutes as $minute){
if(!isset($result[$minute->format($format)])) $result[$minute->format($format)] = 0;
$result[$minute->format($format)]++;
}
See the manual page for acceptable formats.
We now have the number of minutes worked in each day, converting them to hours is trivial:-
foreach($result as $key => $r){
$result[$key] = $r/60;
}
var_dump($result);
Output (Obviously, you will get a different result running it at a different time) :-
array
'Monday' => float 17.483333333333
'Tuesday' => float 19.266666666667
So on Monday 17.48 hours were worked and 19.27 on Tuesday.
Alternatively:-
foreach($result as $key => $r){
$result[$key] = floor($r/60) . ':' . $r % 60;
}
Would give the following output if that is closer to what you want:-
array
'Monday' => string "17:29"
'Tuesday' => string "19:16"
That's the simplest way I can think of doing it.

Categories