I'm writing a PHP function that would use a table of sorts to look up which DB shard the application should go to, based on the datestamp I have.
The shard configuration is something like this (pseudo-code): the first column is the date of the event I'm looking for and the 2nd is the shard the event resides in.
pre-2008 -> shard1
2008-2009 -> shard2
2009_01-2009_06 -> shard3
2009_07 -> shard4
2009_08 -> shard5
2009_09 and up -> shard6
As you can see, the configuration I want is pretty flexible - it can take any date range, however small or big and map to a shard.
I am looking for the quickest way to do a lookup based on a given date.
For example, if my date is 2009-05-02, the shard I am after is shard3. If the date is 2007-08-01, then it's shard1.
Bonus points for actual PHP code, as the application is in PHP.
Thank you.
I am guessing that you don't want to have holes in date ranges, so I propose that you
just need to specify an end date for each shard, and explicitly name one Default Shard
which holds everything that is too new to fit into one of the other shards.
// configure shards
$SHARDS = array(
// <end date> => <shard number>
'2007-12-31' => 'shard1', // shard1 - up to end of 2007
'2008-12-31' => 'shard2', // shard2 - up to end of 2008
'2009-06-30' => 'shard3', // shard3 - up to end of June 09
'2009-07-31' => 'shard4', // shard4 - up to end of July 2009
'2009-08-31' => 'shard5', // shard4 - up to end of August 2009
'DEFAULT' => 'shard6', // everything else in shard 6
);
This makes it easy to get the dates right, and the code for finding a shard based on date is simple:
function findShardByDate($date) {
static $default = false;
static $sorted = false;
if($sorted === false) {
// copy of global $SHARDS
$SHARDS = $GLOBALS['SHARDS'];
$default = $SHARDS['DEFAULT'];
unset($SHARDS['DEFAULT']);
// make sure $SHARDS is sorted
ksort($SHARDS);
$sorted = $SHARDS;
unset($SHARDS);
}
// find the first shard which would contain that date
foreach($sorted as $endDate => $shardName)
if($endDate >= $date)
return $shardName;
// no shard found - use the default shard
return $default;
}
Edit: Used static variables so that sorting is only done once.
<?php
function get_shard($datetime)
{
$timestamp = strtotime($datetime);
$shards = array(array('start' => null, 'end' => '2007-12-31'),
array('start' => '2008-01-01', 'end' => '2008-12-31'),
array('start' => '2009-01-01', 'end' => '2009-06-30'),
array('start' => '2009-07-01', 'end' => '2009-07-31'),
array('start' => '2009-08-01', 'end' => '2009-08-31'),
array('start' => '2009-09-01', 'end' => null),
);
foreach ($shards as $key => $range) {
$start = strtotime($range['start']);
$end = strtotime($range['end']);
if ($timestamp >= $start && $timestamp <= $end) {
return $key + 1;
}
if ($timestamp >= $start && $end === false) {
return $key + 1;
}
}
}
$datetime = '2007-08-01';
echo 'shard' . get_shard($datetime) . "\n";
$datetime = '2009-05-02';
echo 'shard' . get_shard($datetime) . "\n";
$datetime = '2010-01-01';
echo 'shard' . get_shard($datetime) . "\n";
?>
Outputs:
shard1
shard3
shard6
Since your shards can be strictly ordered, it seems like storing them in a binary tree and then simply running a binary search through that tree would give you the fastest results.
Related
I have to create a scheduling component that will plan e-mails that need to be sent out. Users can select a start time, end time, and frequency. Code should produce a random moment for every frequency, between start and end time. Outside of office hours.
Paramaters:
User can select a period between 01/01/2020 (the start) and 01/01/2021 (the end). In this case user selects a timespan of one exactly year.
User can select a frequency. In this case user selects '2 months'.
Function:
Code produces a list of datetimes. The total time (one year) is divided by frequency (2 months). We expect a list of 6 datetimes.
Every datetime is a random moment in said frequency (2 months). Within office hours.
Result:
An example result for these paramaters might as follows, with the calculated frequency bounds for clarity:
[jan/feb] 21-02-2020 11.36
[mrt/apr] 04-03-2020 16.11
[mei/jun] 13-05-2020 09.49
[jul-aug] 14-07-2020 15.25
[sep-okt] 02-09-2020 14.09
[nov-dec] 25-12-2020 13.55
--
I've been thinking about how to implement this best, but I can't figure out an elegant solution.
How could one do this using PHP?
Any insights, references, or code spikes would be greatly appreciated. I'm really stuck on this one.
I think you're just asking for suggestions on how to generate a list of repeating (2 weekly) dates with a random time between say 9am and 5pm? Is that right?
If so - something like this (untested, pseudo code) might be a starting point:
$start = new Datetime('1st January 2021');
$end = new Datetime('1st July 2021');
$day_start = 9;
$day_end = 17;
$date = $start;
$dates = [$date]; // Start date into array
while($date < $end) {
$new_date = clone($date->modify("+ 2 weeks"));
$new_date->setTime(mt_rand($day_start, $day_end), mt_rand(0, 59));
$dates[] = $new_date;
}
var_dump($dates);
Steve's anwser seems good, but you should consider 2 additional things
holiday check, in the while after first $new_date line, like:
$holiday = array('2021-01-01', '2021-01-06', '2021-12-25');
if (!in_array($new_date,$holiday))
also a check if date is a office day or a weekend in a similar way as above with working days as an array.
It's kind of crappy code but I think it will work as you wish.
function getDiffInSeconds(\DateTime $start, \DateTime $end) : int
{
$startTimestamp = $start->getTimestamp();
$endTimestamp = $end->getTimestamp();
return $endTimestamp - $startTimestamp;
}
function getShiftData(\DateTime $start, \DateTime $end) : array
{
$shiftStartHour = \DateTime::createFromFormat('H:i:s', $start->format('H:i:s'));
$shiftEndHour = \DateTime::createFromFormat('H:i:s', $end->format('H:i:s'));
$shiftInSeconds = intval($shiftEndHour->getTimestamp() - $shiftStartHour->getTimestamp());
return [
$shiftStartHour,
$shiftEndHour,
$shiftInSeconds,
];
}
function dayIsWeekendOrHoliday(\DateTime $date, array $holidays = []) : bool
{
$weekendDayIndexes = [
0 => 'Sunday',
6 => 'Saturday',
];
$dayOfWeek = $date->format('w');
if (empty($holidays)) {
$dayIsWeekendOrHoliday = isset($weekendDayIndexes[$dayOfWeek]);
} else {
$dayMonthDate = $date->format('d/m');
$dayMonthYearDate = $date->format('d/m/Y');
$dayIsWeekendOrHoliday = (isset($weekendDayIndexes[$dayOfWeek]) || isset($holidays[$dayMonthDate]) || isset($holidays[$dayMonthYearDate]));
}
return $dayIsWeekendOrHoliday;
}
function getScheduleDates(\DateTime $start, \DateTime $end, int $frequencyInSeconds) : array
{
if ($frequencyInSeconds < (24 * 60 * 60)) {
throw new \InvalidArgumentException('Frequency must be bigger than one day');
}
$diffInSeconds = getDiffInSeconds($start, $end);
// If difference between $start and $end is bigger than two days
if ($diffInSeconds > (2 * 24 * 60 * 60)) {
// If difference is bigger than 2 days we add 1 day to start and subtract 1 day from end
$start->modify('+1 day');
$end->modify('-1 day');
// Getting new $diffInSeconds after $start and $end changes
$diffInSeconds = getDiffInSeconds($start, $end);
}
if ($frequencyInSeconds > $diffInSeconds) {
throw new \InvalidArgumentException('Frequency is bigger than difference between dates');
}
$holidays = [
'01/01' => 'New Year',
'18/04/2020' => 'Easter 1st official holiday because 19/04/2020',
'20/04/2020' => 'Easter',
'21/04/2020' => 'Easter 2nd day',
'27/04' => 'Konings',
'04/05' => '4mei',
'05/05' => '4mei',
'24/12' => 'Christmas 1st day',
'25/12' => 'Christmas 2nd day',
'26/12' => 'Christmas 3nd day',
'27/12' => 'Christmas 3rd day',
'31/12' => 'Old Year'
];
[$shiftStartHour, $shiftEndHour, $shiftInSeconds] = getShiftData($start, $end);
$amountOfNotifications = floor($diffInSeconds / $frequencyInSeconds);
$periodInSeconds = intval($diffInSeconds / $amountOfNotifications);
$maxDaysBetweenNotifications = intval($periodInSeconds / (24 * 60 * 60));
// If $maxDaysBetweenNotifications is equals to 1 then we have to change $periodInSeconds to amount of seconds for one day
if ($maxDaysBetweenNotifications === 1) {
$periodInSeconds = (24 * 60 * 60);
}
$dates = [];
for ($i = 0; $i < $amountOfNotifications; $i++) {
$periodStart = clone $start;
$periodStart->setTimestamp($start->getTimestamp() + ($i * $periodInSeconds));
$seconds = mt_rand(0, $shiftInSeconds);
// If $maxDaysBetweenNotifications is equals to 1 then we have to check only one day without loop through the dates
if ($maxDaysBetweenNotifications === 1) {
$interval = new \DateInterval('P' . $maxDaysBetweenNotifications . 'DT' . $seconds . 'S');
$date = clone $periodStart;
$date->add($interval);
$dayIsWeekendOrHoliday = dayIsWeekendOrHoliday($date, $holidays);
} else {
// When $maxDaysBetweenNotifications we have to loop through the dates to pick them
$loopsCount = 0;
$maxLoops = 3; // Max loops before breaking and skipping the period
do {
$day = mt_rand(0, $maxDaysBetweenNotifications);
$periodStart->modify($shiftStartHour);
$interval = new \DateInterval('P' . $day . 'DT' . $seconds . 'S');
$date = clone $periodStart;
$date->add($interval);
$dayIsWeekendOrHoliday = dayIsWeekendOrHoliday($date, $holidays);
// If the day is weekend or holiday then we have to increment $loopsCount by 1 for each loop
if ($dayIsWeekendOrHoliday === true) {
$loopsCount++;
// If $loopsCount is equals to $maxLoops then we have to break the loop
if ($loopsCount === $maxLoops) {
break;
}
}
} while ($dayIsWeekendOrHoliday);
}
// Adds the date to $dates only if the day is not a weekend day and holiday
if ($dayIsWeekendOrHoliday === false) {
$dates[] = $date;
}
}
return $dates;
}
$start = new \DateTime('2020-12-30 08:00:00', new \DateTimeZone('Europe/Sofia'));
$end = new \DateTime('2021-01-18 17:00:00', new \DateTimeZone('Europe/Sofia'));
$frequencyInSeconds = 86400; // 1 day
$dates = getScheduleDates($start, $end, $frequencyInSeconds);
var_dump($dates);
You have to pass $start, $end and $frequencyInSeconds as I showed in example and then you will get your random dates. Notice that I $start and $end must have hours in them because they are used as start and end hours for shifts. Because the rule is to return a date within a shift time only in working days. Also you have to provide frequency in seconds - you can calculate them outside the function or you can change it to calculate them inside. I did it this way because I don't know what are your predefined periods.
This function returns an array of \DateTime() instances so you can do whatever you want with them.
UPDATE 08/01/2020:
Holidays now are part of calculation and they will be excluded from returned dates if they are passed when you are calling the function. You can pass them in d/m and d/m/Y formats because of holidays like Easter and in case when the holiday is on weekend but people will get additional dayoff during the working week.
UPDATE 13/01/2020:
I've made updated code version to fix the issue with infinite loops when $frequencyInSeconds is shorter like 1 day. The new code used few functions getDiffInSeconds, getShiftData and dayIsWeekendOrHoliday as helper methods to reduce code duplication and cleaner and more readable code
Let's just say, for the sake of simplicity, I have two arrays, in the first is a time range in which I am wanting to go to sleep. I must sleep for the entire two hour period without any interruption, so the bed must be available during that time. The function needs to check if the bed will be available when I want to sleep.
$aSleeping = array(
'start' => '6:00 AM',
'end' => '10:00 AM'
);
$aBedAvail = array(
'start' => '10:00 AM',
'end' => '12:00 PM'
);
I have tried this, it worked on one time range, but not another, any help to improve on this is greatly appreciated.
function checkRange($aSleeping,$aBedAvail){
if(
strtotime($aSleeping['start']) >= strtotime($aBedAvail['start']) &&
strtotime($aSleeping['end']) <= strtotime($aBedAvail['end'])
){
return true;
}else{
return false;
}
}
Because a user can start sleeping at 11h59pm in one day and ends in next day, you must consider using the day information in comparison. PHP DateTime helps enable other date capabilities, in case you need perform other actions with date vars:
$aSleeping = array(
'start' => new DateTime('2020-03-10 05:00:00'),
'end' => new DateTime('2020-03-10 12:00:00')
);
$aBedAvail = array(
'start' => new DateTime('2020-03-10 05:00:00'),
'end' => new DateTime('2020-03-10 12:00:00')
);
function checkRange($aSleeping,$aBedAvail){
return ($aSleeping['start'] >= $aBedAvail['start'] && $aSleeping['end'] <= $aBedAvail['end']) ? true : false;
}
var_dump(checkRange($aSleeping,$aBedAvail));
Your function looks like it would work just fine unless either of the ranges has an end time earlier than the start time (an evening to morning range). You can correct that by checking for that condition and moving the end time to the next day if it is before the start time.
function checkRange(array $outer, array $inner) {
// create DateTimes
$outer = array_map('date_create', $outer);
$inner = array_map('date_create', $inner);
// Correct earlier end times
$outer['start'] > $outer['end'] && $outer['end']->modify('+24 hours');
$inner['start'] > $inner['end'] && $inner['end']->modify('+24 hours');
// compare ranges and return result
return $outer['start'] >= $inner['start'] && $outer['end'] <= $inner['end'];
}
I have a website which uses 4 different background images for the header area which visually corresponds to the season of the ear (summer, autumn etc.) – for the summer timeframe I use one image, for the autumn – another one and so on. The problem is that I have to manually change those images once the season of the year changes.
Maybe someone could show how would it be possible to check the current time / season of the year and then print the corresponding classes to the header element (.summer, .autumn etc.)?
I assume using PHP would be the way.
As I stated in the comments, this is an interesting challenge because the dates of seasons are always changing and different depending what part of the world you live in. Your server time and the website visitor's local time are also a factor.
Since you've stated you're just interested in a simple example based on server time and you're not concerned with it being exact, this should get you rolling:
// get today's date
$today = new DateTime();
echo 'Today is: ' . $today->format('m-d-Y') . '<br />';
// get the season dates
$spring = new DateTime('March 20');
$summer = new DateTime('June 20');
$fall = new DateTime('September 22');
$winter = new DateTime('December 21');
switch(true) {
case $today >= $spring && $today < $summer:
echo 'It\'s Spring!';
break;
case $today >= $summer && $today < $fall:
echo 'It\'s Summer!';
break;
case $today >= $fall && $today < $winter:
echo 'It\'s Fall!';
break;
default:
echo 'It must be Winter!';
}
This will output:
Today is: 11-30-2016
It's Fall!
One might consider this necromancy, Yet when I was looking for ready to use method that does this I've got here.
Mister Martin answer was good assuming the year will not changes, here is my snippet, might be useful for someone in future:
private function timestampToSeason(\DateTime $dateTime): string{
$dayOfTheYear = $dateTime->format('z');
if($dayOfTheYear < 80 || $dayOfTheYear > 356){
return 'Winter';
}
if($dayOfTheYear < 173){
return 'Spring';
}
if($dayOfTheYear < 266){
return 'Summer';
}
return 'Fall';
}
cheers!
I know this question is quite old, but none of the answers on this or any of the other questions that ask this question account for timezones, hemispheres, and different calendars in the same function, and since this came up first when I Googled for this, here's what I ended up writing myself.
If you want to get fancier you could calculate the actual Astronomical dates, geolocate the user, internationalize the season name output, etc., but that's beyond the scope of this question anyway.
function get_season( string $date = '', string $timezone = 'Europe/London', string $hemisphere = 'northern', string $calendar = 'meteorological' ): string {
// Create the calendars that you want to use for each hemisphere.
$seasons = array(
'northern' => array(
'astronomical' => array(
'spring' => '03-20', // mm-dd
'summer' => '06-21',
'autumn' => '09-22',
'winter' => '12-21',
),
'meteorological' => array(
'spring' => '03-01',
'summer' => '06-01',
'autumn' => '09-01',
'winter' => '12-01',
),
),
'southern' => array(
'astronomical' => array(
'spring' => '09-22',
'summer' => '12-21',
'autumn' => '03-20',
'winter' => '06-21',
),
'meteorological' => array(
'spring' => '09-01',
'summer' => '12-01',
'autumn' => '03-01',
'winter' => '06-01',
),
),
);
// Set $date to today if no date specified.
if ( empty( $date ) ) {
$date = new \DateTimeImmutable( 'now', new \DateTimeZone( $timezone ) );
} else {
$date = new \DateTimeImmutable( $date, new \DateTimeZone( $timezone ) );
}
// Set the relevant defaults.
$seasons = $seasons[ strtolower( $hemisphere ) ][ strtolower( $calendar ) ];
$current_season = array_key_last( $seasons );
$year = $date->format( 'Y' );
// Return the season based on whether its date has passed $date.
foreach ( $seasons as $season => $start_date ) {
$start_date = new \DateTimeImmutable( $year . '-' . $start_date, new \DateTimeZone( $timezone ) );
if ( $date < $start_date ) {
return $current_season;
}
$current_season = $season;
}
return $current_season;
}
Usage
// Pass nothing to get current meteorological season in the Northern hemisphere.
echo get_season();
// Pre-PHP 8.0, though I've only tested it on 7.4.
echo get_season( 'December 18', 'Europe/London', 'northern', 'astronomical' );
// After PHP 8.0 you can use named arguments to skip the defaults.
echo get_season( 'December 18', calendar: 'astronomical' );
Outputs
winter // Current season.
autumn
autumn // If PHP 8.0, otherwise error.
I personally like that it lets me add calendars if I discover something unique, like for example, the beginning of Summer is always the first Thursday after April 18th in Iceland.
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.
My mind seems to be going blank on this one.
I have to write something to figure out which date range todays day/month combination fits into.
I have a set amount of date ranges, which are:
$dateRanges = array(
1 => "16 January to 30 April",
2 => "1 May to 30 June",
3 => "1 July to 15 August",
4 => "16 August to 15 September",
5 => "15 September to 15 October",
6 => "16 October to 15 January"
);
All I'm trying to return is the array key of the range the current date fits into.
At the moment all thats going through my head is I'll have to set up a large if statement to look at the current date('j') and date('n') and match the results up. But surely thats pretty messy and not very efficient?
Any ideas of a better way to approach this problem would be much appreciated!
$today = time();
foreach ($dateRanges as $key => $range) {
list($start, $end) = explode(' to ', $range);
$start .= ' ' . date('Y'); // add 2011 to the string
$end .= ' ' . date('Y');
if ((strtotime($start) <= $today) && (strtotime($end) >= $today)) {
break;
}
}
$key will be either the index of the matching date range, or null/false.
This is a variation on Mark B's answer, but made more efficient by turning this into pure numeric comparisons:
function get_today () {
$dateRanges = array(
0 => 116, // 16th Jan
1 => 501, // 1st May
2 => 701, // 1st July ..etc..
3 => 816,
4 => 916,
5 => 1016
);
$today = (int) date('nd');
foreach ($dateRanges as $key => $date) {
if ($today < $date) {
$result = $key;
break;
}
}
return (empty($result)) ? 6 : $result;
}
Returns an integer matching the keys in your sample array
Create DateTime instances for the values in the array und use simple comparison operators like > and <
Just use strtotime to create a UNIX epoch, then use the inbuilt < and > operators.
http://www.php.net/manual/en/function.strtotime.php
$time_min = strtotime("17 January 2011");
$time_max = strtotime("30 April 2011");
if ($time >= $time_min && $time < $time_max)
{
echo "Time is within range!";
}
You can then just expand this to use the array of ranges you specified.