I have a holiday class that contains a multidimensional array of holidays with different time periods that works basically like this:
$this->bgArray = array( 'background' => array(
array( 'name' => 'SuperBowl',
'start' => '06 Feb 2011',
'end' => '07 Feb 2011'),
array( 'name' => 'presidentsday2011',
'start' => '21 Feb 2011',
'end' => '22 Feb 2011'),
array( 'name' => 'marchmadness6',
'start' => '01 Mar 2011',
'end' => '02 Mar 2011')));
and when it is called I want to be able to select the right holiday and run subsequent code, currently I have that set up as:
foreach($holidays->bgArray['background'] AS $holiday)
{
if (($holidays->today >= strtotime($holiday['start']) && $holidays->today < strtotime($holiday['end']) || (isset($_REQUEST[$holiday['name']])) ))
{
$background = $holiday['name'];
}
}
$this->set_background($background);
this code is all working great but personally I feel like there is a better way to do this code than a foreach loop. Does anyone have any suggestions? Also, no I don't have access to a DB so I have to this all within PHP. Any help is appreciated, thanks guy.
foreach is fine for arrays. You might want to use unix timestamps directly in your array since always using strtotime isn't excactly the fastest way.
What you also could do is splitting the array up into months, having a subarray for each month. That way you don't have to iterate over all holidays but only the specific ones for the month to be displayed.
Related
I have this array data structure:
$records = [];
$records[] = ['id' => 1, 'date' => '2017-03-12', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-13', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-13', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-14', 'operation' => 'forgot_password'];
$records[] = ['id' => 1, 'date' => '2017-03-14', 'operation' => 'sent_email'];
$records[] = ['id' => 2, 'date' => '2017-03-14', 'operation' => 'sent_email'];
$records[] = ['id' => 2, 'date' => '2017-03-14', 'operation' => 'forgot_password'];
$records[] = ['id' => 1, 'date' => '2017-03-27', 'operation' => 'sent_email'];
$records[] = ['id' => 1, 'date' => '2017-03-29', 'operation' => 'sent_email'];
In this array I store operations that were done by website visitors. Obviously visitor can be identified by an id number.
What I want to do is to count how many times in a week (between Monday and Sunday inclusively) each visitor used 'sent_email' operation.
For example: first record with 'sent_email' operation shows that this operation happened in '2017-03-12' (Sunday), so that means this operation took place just once in that week for user with an id = 1.
Other next three 'sent_email' operations for user with an id = 1 happened three times in another week. (2017-03-13, 2017-03-13, 2017-03-14 those three dates belong to the same week).
I know that I probably need to loop through each record and somehow to check those dates if they belong to the same week, but I feel confused and stuck here, I don't understand logical steps needed to accomplish it. I would be really grateful if anyone could give me an explanation or pseudo code, whatever it is that would help me to tackle this problem, I really enjoy solving problems myself but because I'm stuck, I just need someone to get me up and running.
Thank you in advance for any help you can provide.
The DateTime object and the strtotime() functions both understand relative date strings, so you can do neat things like "last tuesday" and "3 days ago".
Some pseudo-code to get you started:
// figure out when your monday is
monday = ...
// figure out when your sunday is
sunday = ...
// loop over all records
foreach $record:
// skip records outside the date range you want
if date < monday or date > sunday then skip this record;
// skip records not of the type you want
if operation != email then skip this record;
// register a hit for this user
increment counter for user
$time = time();
$foo = array(
1448319600 => array(
array(
'foo' => 'bar'
),
array(
'bar' => 'foo'
)
),
1448578800 => array(
array(
'foo2' => 'bar2'
),
array(
'bar2' => 'foo2'
)
)
);
function bar($time, $foo) {
$count = 0;
do {
$count++;
$time = strtotime('+1 day', $time);
} while (isset($foo[$time]) === false);
return array(
'count' => $count,
'foo' => $foo[$time]
);
}
bar($time, $foo);
It's loading and loading and loading, because isset($foo[$time]) === false always seems to be true. I just can't find the error.
(Some more text to be able to submit. Some more text to be able to submit. Some more text to be able to submit.)
strtotime('+1day') is going to use "now" , e.g. 3:35pm as the base time, so you're doing the equivalent of
$now = time(); // 1448400979
$tomorrow = strtotime('+1 day', $now); // 1448487379
while(!isset($arr[$tomorrow])) { ... }
Since that value is highly unlikely to ever be in your array as a key, your loop never ends. You need to do a >= comparison or something, to check when your currently-being-considered date becomes larger than any key in your array.
At the start of your script you are setting time to the current time. Then in your do while loop you are incrementing the time by one day each time it loops. isset($foo[time]) === false will only return false and exit the loop when your time started at exactly 11pm sometime before Mon, 23 Nov 2015 23:00:00 +0000 or Thu, 26 Nov 2015 23:00:00 +0000.
An example being the timestamp while writing this post is 1448401349 If I add one day to it I get 1448487749.
You might want to look into rounding your timestamps to midnight to make sure you get collisions in your loop that allow it to exit. unix timestamp round to midnight
I have a multidimensional array like this:
$array = array(
0 => array(
'name' => 'first element',
'start' => '30/04/2015',
'end' => '30/06/2015'
),
1 => array(
'name' => 'second element',
'start' => '01/07/2015',
'end' => '30/09/2015'
),
2 => array(
'name' => 'fourth element',
'start' => '01/10/2015',
'end' => '15/12/2015'
)
);
I need to select one array subarray (item) based on the today date.
today date must be between start date and end date keys.
In the end I would like to have this:
$selected_subarray = array (
'name' => 'first element',
'start' => '30/04/2015',
'end' => '30/06/2015'
)
I use to check between two dates like this:
function check_if_in_range($start_date, $end_date, $today_date)
{
$start_d = strtotime($start_date);
$end_d = strtotime($end_date);
$today_d = strtotime($today_date);
return (($today_d >= $start_d) && ($today_d <= $end_d));
}
I tryed to follow suggestions from this question How to search by key=>value in a multidimensional array in PHP
but if I'm able to filter for a key = value, I'm not able to do the same using the "check_if_in_range" function.
You do know that 30/06/2015 is invalid date, and strtotime() will return false? See here. Format mm/dd/yyyy is an American month, day and year. So your format is non-standard.
Best way is to convert it to standard format, and then use strtotime()example or just use DateTime::createFromFormat()example.
After you learn how formating and converting dates works, then you can just do simple foreach loop, and break on first found result. Here is a little demo.
Try something like the following
foreach($array as $key => $value) {
if(check_in_range($value['start'], $value['end'], $today_date)) {
$selected_subarray = $value;
}
}
I am trying to sort an array in PHP by date and time which is in ISO 8601 format. I am still trying to grasp PHP and have tried many of the solutions on stack overflow and I am just not able to nail down the right function. Hopefully this is an easy answer and it will be helpful to others.
FYI, this array was generated by the Citrix API for GoToMeeting. I would like to sort the array based on startTime in the soonest time first in the list.
Here is what the array looks like using var_export with two results presented:
array (
0 => stdClass::__set_state(
array(
'createTime' => '2012-07-03T19:36:58.+0000',
'status' => 'INACTIVE',
'subject' => 'Client 1',
'startTime' => '2012-07-10T14:00:00.+0000',
'conferenceCallInfo' => 'United States: xxxxx Access Code: xxxxx',
'passwordRequired' => 'false',
'meetingType' => 'Scheduled',
'maxParticipants' => 26,
'endTime' => '2012-07-10T15:00:00.+0000',
'uniqueMeetingId' => 12345678,
'meetingid' => 123456789,
)
),
1 => stdClass::__set_state(
array(
'createTime' => '2012-07-02T21:57:48.+0000',
'status' => 'INACTIVE',
'subject' => 'Client 2',
'startTime' => '2012-07-06T19:00:00.+0000',
'conferenceCallInfo' => 'United States: xxxxx Access Code: xxxxx',
'passwordRequired' => 'false',
'meetingType' => 'Scheduled',
'maxParticipants' => 26,
'endTime' => '2012-07-06T20:00:00.+0000',
'uniqueMeetingId' => 12345678,
'meetingid' => 123456789,
)
),
)
My goal is to then output the array into html div's using a foreach loop, this code is complete and works well but my sort is off :-)
Thank you in advance for any help!
Steve
You can implement any sorting technique you can think of if you wrap it in a callback and use usort() docs here
inside your callback, you can use strtotime or similar, and do simple int comparisons.
$myDateSort = function($obj1, $obj2) {
$date1 = strtotime($obj1->startTime);
$date2 = strtotime($obj2->startTime);
return $date1 - $date2; // if date1 is earlier, this will be negative
}
usort($myArray, $myDateSort);
>> $start_dt = new DateTime()
DateTime::__set_state(array(
'date' => '2012-04-11 08:34:01',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $end_dt = new DateTime()
DateTime::__set_state(array(
'date' => '2012-04-11 08:34:06',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $start_dt->setTimestamp(strtotime('31-Jan-2012'))
DateTime::__set_state(array(
'date' => '2012-01-31 00:00:00',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $end_dt->setTimestamp(strtotime('1-Mar-2012'))
DateTime::__set_state(array(
'date' => '2012-03-01 00:00:00',
'timezone_type' => 3,
'timezone' => 'America/Los_Angeles',
))
>> $interval = $start_dt->diff($end_dt)
DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 30,
'h' => 0,
'i' => 0,
's' => 0,
'invert' => 0,
'days' => 30,
))
>> $interval->format('%mm %dd')
'0m 30d'
i.e., 31-Jan-2012 to 1-Mar-2012 yields less than a month! I'd expect the output to be 1 month, 1 day. It shouldn't matter the number of days in February; that's the point of using a time library -- it's supposed to handle these things. WolframAlpha agrees.
Should I file a bug to PHP? Is there a hack/fix/workaround to get months to work as expected?
Updated answer
This behavior of DateTime::diff is certainly unexpected, but it's not a bug. In a nutshell, diff returns years, months, days etc such that if you did
$end_ts = strtotime('+$y years +$m months +$d days' /* etc */, $start_ts);
you would get back the timestamp that corresponds to end original end date.
These additions are performed "blindly" and then date correction applies (e.g. Jan 31 + 1 month would be Feb 31, corrected to Mar 2 or Mar 3 depending on the year). In this specific example you cannot add even one month as salathe also explains.
Should I file a bug to PHP?
No.
The "month" part of the interval means that the month part of the start date can be incremented by that many months. The behaviour in PHP, taking your start date of 31-Jan-2012 and incrementing the month (literally, 31-Feb-2012) and then correcting for a valid date (PHP does this for you) would give 02-Mar-2012 which is later than the target date that you are working with.
To demonstrate this, take your start date and add n months for a few months to see the behaviour.
31-Jan-2012 (Interval)
02-Mar-2012 (P1M)
31-Mar-2012 (P2M)
01-May-2012 (P3M)
31-May-2012 (P4M)
01-Jul-2012 (P5M)
You can see that the month is being incremented, then adjusted to make a valid date.