I have a php code as shown below in which I want to display anything in between two calendar days of the week.
The values coming inside $data->{\"select_s
Here is my stab at it, using a mapping table and string concatenation. It doesn't work if the days are in reversed order. e.g. If today is Sunday and the value of select_start_day
is Fri
and the value of select_end_day
is Mon
, then it won't work.
<?php
$arr = (object) [
'select_start_day' => 'wed',
'start_time' => 143400,
'select_end_day' => 'wed',
'end_time' => 220000
];
$map_daysToNumbers = ['sun'=>1, 'mon'=>2, 'tue'=>3, 'wed'=>4, 'thu'=>5, 'fri'=>6, 'sat'=>7];
$startString = $map_daysToNumbers[$arr->select_start_day] . str_pad($arr->start_time, 6, '0', STR_PAD_LEFT);
$endString = $map_daysToNumbers[$arr->select_end_day] . str_pad($arr->end_time, 6, '0', STR_PAD_LEFT);
$tz = new \DateTimeZone('America/Toronto');
$today = new \DateTime('now', $tz);
$todayString = ($today->format('w')+1) . $today->format('His');
if($startString <= $todayString && $todayString <= $endString){
echo 'In range';
}
or date-based solution. Neither of them is guaranteed to fulfil your needs.
$arr = (object) [
'select_start_day' => 'tue',
'start_time' => 143400,
'select_end_day' => 'wed',
'end_time' => 220000
];
$tz = new \DateTimeZone('America/Toronto');
// extrapolate the start date looking 7 days back
$sDate = new \DateTime('tomorrow midnight', $tz);
$sDate->modify('last '.$arr->select_start_day);
$sDate->setTime(...str_split(str_pad($arr->start_time, 6, '0', STR_PAD_LEFT), 2));
// or bound the start date to be between last sunday and next saturday
$sDate = new \DateTime('Saturday last week', $tz);
$sDate->modify('next '.$arr->select_start_day);
$sDate->setTime(...str_split(str_pad($arr->start_time, 6, '0', STR_PAD_LEFT), 2));
// extrapolate the end date
$eDate = clone $sDate;
$eDate->modify('yesterday'); // workaround to consider the same day possibility
$eDate->modify('next '.$arr->select_end_day);
$eDate->setTime(...str_split(str_pad($arr->end_time, 6, '0', STR_PAD_LEFT), 2));
// Test against today
$today = new \DateTime('now', $tz);
var_dump($sDate);
var_dump($eDate);
var_dump($today);
if($sDate <= $today && $today <= $eDate){
echo 'In range';
}
The first way will always start in the past and depending on your range it might include today or not. The second will always be bound to the current week, which I believe is what you wanted.
As @mickmackusa and I said in the comments, the requirements given to you are vague and imprecise. You either need more rigid rules or a date based solution, i.e. you are given two precise dates (timestamps) and then you compare if a date falls between them. This is what I tried to do in my second option, but It is unknown if the date should be in the past or future.
Here is the Example of displaying anything between time duration
Add any time of Toronto in start time and end time..you will be get output between this day and time duration in every week.
date_default_timezone_set('America/Toronto');
$now = new DateTime();
$date = $now->format('Y-m-d');
$time = $now->format('h:i A');
$dayname = strtolower(date('l', strtotime($date))); //it will give you today's day name
$start_day = "monday"; //lets consider your data will be come as startday="monday" from $data->{"select_start_day"};
$start_time = '07:50 AM'; //lets consider your data will be come as starttime="07:50 AM" from $data->{"start_time"};
$end_day = "tuesday"; //lets consider your data will be come as endday="monday" from $data->{"select_end_day"};
$end_time = '08:26 AM'; //lets consider your data will be come as endtime="08:26 AM" from $data->{"end_time"};
$todays_date_time = strtotime($dayname);
$start_day_time = strtotime($start_day);
$end_day_time = strtotime($end_day);
$timeconvert = strtotime($time);
$timeconvertstart = strtotime($start_time);
$timeconvertend = strtotime($end_time);
if($todays_date_time >= $start_day_time && $todays_date_time <= $end_day_time )
{
if ($timeconvert >= $timeconvertstart && $timeconvert <= $timeconvertend) {
echo "test";
}
}
I Hope this one is help you!
I would keep all the dates in the same format. Unix time is a great way to store the time because you can easily run calculations.
In your example you are calculating using both strings and numbers. In fact you have 3 different ways of expressing the date. I suggest using just date() to save and compare all of the values and only format that Unix timestamp when displaying to the end user.
↓↓↓↓ new edit ↓↓↓
Look carefully at what your code is returning:
$arradate = strtolower(date('D')); //echos the day ex: sun
$nowtime = (int)date('His'); //echos a different format ex: 25019
And have a good read of the date function's docs, specifically the arguments you chose for nowtime which are 'His' which will return:
H 24-hour format of an hour with leading zeros 00 through 23
i Minutes with leading zeros 00 to 59
s Seconds with leading zeros 00 through 59
Now consider that date() returns a Unix timestamp and you will see the failure. You simply need to remove the formatting of the date in all the code save for elements that display to the end user. The computer gets no benefit from knowing it is Wednesday. The computer only counts up from the year 1970.
So you just need one argument to call the current date. Either will work:
$arradate = date();
$nowtime = date();
Finally after reading about Unix time you will see that the dates you have stored are backwards. You end_time is day 0 and your start time is two days after that.
Try This:
$dowMap = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
$arradate = date('D');
$arradateNum = array_search($arradate, $dowMap);
$start_dayNum = array_search($start_day, $dowMap);
$end_dayNum = array_search($end_day, $dowMap);
if(
($arradateNum >= $start_dayNum && $nowtime>=$data->{"start_time"}) &&
($arradateNum <= $end_dayNum && $nowtime <= $data->{"end_time"})
) {
echo "Show Anything";
}
You should thoroughly go through the PHP Manual and see how to create DateTime Objects and various date formats available.
First of all you need to convert the User Input to DateTimeObjects. Since the user input contains only Day (represented by D) and Time (represented by His), you could use following code to get these Objects.
date_default_timezone_set('America/Toronto'); //Setting the Timezone
//These are the User Inputs
$start_day = 'tue';
$start_time = 181300;
$end_day = 'fri';
$end_time = 134500;
//User Input Ends
//Create the Date Time Object for Start and End Date
$startDate = DateTime::createFromFormat ( 'D His', $start_day . ' '. $start_time);
$endDate = DateTime::createFromFormat ( 'D His', $end_day . ' '. $end_time);
You can check various options available for createFromFormat in the Manual
Now that you have the Date Time Objects you could get any value from them. Lets get the Day of the Week in Numeric Format as well as Hour in Numeric Format for both Start and End Date.
$startHour = $startDate->format('G'); //24-hour format of an hour without leading zeros
$startDay = $startDate->format('N'); //numeric representation of the day of the week
$endHour = $endDate->format('G'); //24-hour format of an hour without leading zeros
$endDay = $endDate->format('N'); //numeric representation of the day of the week
You can check about G
and N
format as well as other options available in Manual
We Need to check the time is between Tuesday 6PM to Friday 6PM.
First of all we will check whether the Day is between Tuesday and Friday. We have the $startDay
and $endDay
variables above to determine that.
For Tuesday, the corresponding numeric value is 2 and for Friday it is 5.
For Time Range, we have the $startHour
and $endHour
variables. 6PM in the format is represented as 18 ( 12 + 6).
So we can check this condition using the following code:
if ( $startDay >= 2 && $endDay <= 5 && $startHour >= 18 and $endHour <= 18 ) {
echo 'In Range';
} else {
echo 'out of Range';
}
This way you can check any complex condition. You can see the Online Demo at 3v4l
At first, I can recommend you use Object-oriented programming to better structuration of your code and decomposition of the task. You can create an abstraction to work with the weekday time. For example:
class WeekDayTime
{
/** @var string[] map of the name of days and their number */
const DAY_MAP = [
'Mon' => 1,
'Tue' => 2,
'Wed' => 3,
'Thu' => 4,
'Fri' => 5,
'Sat' => 6,
'Sun' => 7
];
/** @var int number of the day */
private $dayNumber;
/** @var int amount of hours */
private $hours;
/** @var int amount of minutes */
private $minutes;
/** @var int amount of seconds */
private $seconds;
/**
* Constuctor
* @param string $day number of the day
* @param int $hours amount of hours
* @param int $minutes amount of minutes
* @param int $seconds amount of seconds
*/
public function __construct(string $day, int $hours, int $minutes, int $seconds)
{
assert(array_key_exists($day, static::DAY_MAP), 'The day is incorrect');
assert($hours < 24, 'The hours must be less than 24');
assert($minutes < 60, 'The hours must be less than 60');
assert($seconds < 60, 'The hours must be less than 60');
$this->dayNumber = static::DAY_MAP[$day];
$this->hours = $hours;
$this->minutes = $minutes;
$this->seconds = $seconds;
}
/**
* Get number of the day
* @return int number of the day
*/
public function getDayNumber(): int
{
return $this->dayNumber;
}
/**
* Get amount of hours
* @return int amount of hours
*/
public function getHours(): int
{
return $this->hours;
}
/**
* Get amount of minutes
* @return int amount of minutes
*/
public function getMinutes(): int
{
return $this->minutes;
}
/**
* Get amount of seconds
* @return int amount of seconds
*/
public function getSeconds(): int
{
return $this->seconds;
}
/**
* Check if the current week day time is less the a denined week day time
* @param WeekDayTime $value value which will be compared
* @return bool status of the checking
*/
public function isLessOrEqual(WeekDayTime $value): bool
{
$isLess = $this->dayNumber < $value->dayNumber;
$isLessOrEqual = $this->dayNumber === $value->getDayNumber()
&& $this->hours <= $value->getHours()
&& $this->minutes <= $value->getMinutes()
&& $this->seconds <= $value->getSeconds();
return $isLess || $isLessOrEqual;
}
/**
* Check if the current week day time is greater the a denined week day time
* @param WeekDayTime $value value which will be compared
* @return bool status of the checking
*/
public function isGreaterOrEqual(WeekDayTime $value): bool
{
$isGreater = $this->dayNumber > $value->dayNumber;
$isGreaterOrEqual = $this->dayNumber === $value->getDayNumber()
&& $this->hours >= $value->getHours()
&& $this->minutes >= $value->getMinutes()
&& $this->seconds >= $value->getSeconds();
return $isGreater || $isGreaterOrEqual;
}
}
It will be the object-value which will have information about the day of week and time and methods to compare objects of this class. After it, you can create a class to contain a range of weekday time. For example:
class WeekDayTimeRange
{
/** WeekDayTime range start */
private $start;
/** WeekDayTime range end */
private $end;
/**
* Constuctor
* @param WeekDayTime $start range start
* @param WeekDayTime $end range end
*/
public function __construct(WeekDayTime $start, WeekDayTime $end)
{
$this->start = $start;
$this->end = $end;
}
/**
* Check if a date-time occurs into the range
* @param DateTimeInterface the date-time which will be checked
* @return bool status of the checking
*/
public function inRange(DateTimeInterface $dateTime): bool
{}
}
As can you see this class has information about range start, range end and method to check the occurrence of any date-time into the range. If you want to check the occurrence into a range which has start value less then end value (for example from Monday to Friday) you can do the following implementation of inRange
method:
public function inRange(DateTimeInterface $dateTime): bool
{
$day = $dateTime->format('D');
$hours = $dateTime->format('H');
$minutes = $dateTime->format('i');
$seconds = $dateTime->format('s');
$weekDayTime = new WeekDayTime($day, $hours, $minutes, $seconds);
return $this->start->isLessOrEqual($weekDayTime) && $this->end->isGreaterOrEqual($weekDayTime);
}
But if you want to check the occurrence into a range which has start value greater then end value (for example from Friday to Monday) you should break range to two ranges: from range start to week end and from week start to range end and to check the occurrence of the date-time into both ranges. For example:
public function inRange(DateTimeInterface $dateTime): bool
{
$day = $dateTime->format('D');
$hours = $dateTime->format('H');
$minutes = $dateTime->format('i');
$seconds = $dateTime->format('s');
$weekDayTime = new WeekDayTime($day, $hours, $minutes, $seconds);
// if the range end is less then range start we break the current range to two range
if ($this->end->isLessOrEqual($this->start)) {
$range1 = new WeekDayTimeRange($this->start, new WeekDayTime('Sun', 23,59,59));
$range2 = new WeekDayTimeRange(new WeekDayTime('Mon', 0,0,0), $this->end);
return $range1->inRange($dateTime) || $range2->inRange($dateTime);
}
return $this->start->isLessOrEqual($weekDayTime) && $this->end->isGreaterOrEqual($weekDayTime);
}
Example of using:
// Date occurs into the range from Tuesday to Friday
$start = new WeekDayTime('Tue', 10, 0,0);
$end = new WeekDayTime('Fri', 14, 0,0);
$range = new WeekDayTimeRange($start, $end);
$range->inRange(DateTime::createFromFormat('Y-m-d H:i:s', '2019-10-03 10:00:00'));
// Date doesn't occur into the range from Tuesday to Friday
$start = new WeekDayTime('Tue', 10, 0,0);
$end = new WeekDayTime('Fri', 14, 0,0);
$range = new WeekDayTimeRange($start, $end);
$range->inRange(DateTime::createFromFormat('Y-m-d H:i:s', '2019-10-05 10:00:00'));
// Date doesn't occur into the range from Friday to Tuesday
$start = new WeekDayTime('Fri', 14, 0,0);
$end = new WeekDayTime('Tue', 10, 0,0);
$range = new WeekDayTimeRange($start, $end);
$range->inRange(DateTime::createFromFormat('Y-m-d H:i:s', '2019-10-03 10:00:00'));
// Date occurs into the range from Friday to Tuesday
$start = new WeekDayTime('Fri', 14, 0,0);
$end = new WeekDayTime('Tue', 10, 0,0);
$range->inRange(DateTime::createFromFormat('Y-m-d H:i:s', '2019-10-05 10:00:00'));
You can see demo at sandbox