How to display anything in php in between particular time duration?
Asked Answered
H

7

5

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_start_day"}; $data->{"start_time"}; $data->{"select_end_day"}; and $data->{"end_time"}; is controlled by the user.

PHP Code:

    if (file_exists('feeds/ptp-ess_landing.json')) {
    $data = json_decode(file_get_contents('feeds/ptp-ess_landing.json'));
    }

    date_default_timezone_set('America/Toronto');
    $arradate = strtolower(date('D'));
    $nowtime = (int)date('His');


    $start_day=$data->{"select_start_day"};
    $start_time=$data->{"start_time"};

    $end_day=$data->{"select_end_day"};
    $end_time=$data->{"end_time"};

For example, let us suppose the user enter $start_day as sun $start_time as 143400 $end_day as wed $end_time as 140000

The above time duration means we are in range and it should display anything we want to display until tomorrow 2pm as tomorrow is wednesday. I am in EST.

I am using the following code in order to pull the current day of the week and time:

date_default_timezone_set('America/Toronto');
$arradate = strtolower(date('D'));
$nowtime = (int)date('His');

Problem Statement:

I am wondering what if logic I need to use so that its print anything Sunday 143400 and Wednesday 140000.

if()    {
    echo "Its in range";
}

Cases:

  1. If the override is supposed to apply from Monday at 8am to Monday at 6pm and today is Wednesday then the override doesn't apply.

  2. If its Monday 6 pm and Friday 6 pm then the override will work for 6 hours on Monday, Tuesday whole day, Wednesday whole day, Thursday whole day, Friday upto 6pm

  3. If it Sunday 6pm and Monday 6pm then the override will work for 6 hours on Monday and 18 hours on Monday.

  4. If its Tuesday 6pm and Friday 6pm and today is Tuesday 9pm then the override will apply.

  5. if its Thursday 6pm and Wednesday 6pm then the override will work for 6 hours on thursday, 24 hours on Friday, 24 hours on Saturday, 24 hours on Sunday, 24 hours on Monday, 24 hours on Tuesday, 24 hours on Wednesday and 18 hours on Thursday.

Huoh answered 29/9, 2019 at 2:27 Comment(10)
That is coming from a JSON. Its a separate admin portal. I am adding more code to make it clear.Huoh
Yeah, maybe a little more sense, but why do you use braces? Why not $data->select_start_day?Runnymede
It just became a habit. I agree we can use without braces.Huoh
When does your week start? Can the range be Thursday-Wednesday?Runnymede
Yes, it can be. In EST it will end tomorrow if the range is Thursday-Wednesday.Huoh
Then it will always be in range, right? What are your boundaries?Runnymede
Yes, it will always be in range. When you say boundaries, it mean time?. Thursday 14000 and end on Wednesday 130000. Again everything is dynamicHuoh
Let us continue this discussion in chat.Runnymede
I expounded on the answer.Defensible
OH wow. Yes making a duplicate question 2 days ago after getting votes and response on this is bad form. The effort is not to learn how date() works. The effort is to complete the task.Defensible
P
2

The difficult part of this question lies in handling ranges that 'wrap' around the end of the week, i.e. your example case 5

I'd suggest setting up a reference array of days that covers two weeks

$days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'];
$days = array_merge($days, $days);

Slice it so that it starts at the day of the start point (it will be reindexed from 0)

$days = array_slice($days, array_search($startDay, $days));

You can then build a reference integer for both now and the end point

$nowRef = (int) (array_search($nowDay, $days) . $nowTime);
$endRef = (int) (array_search($endDay, $days) . $endTime);

Note that you could do the same for the start point, but as the days array starts with $startDay (index 0) this is equivalent to $startTime

Your if condition then simply becomes

if ($nowRef >= $startTime && $nowRef <= $endRef) {
   // IN RANGE
}

N.B. This assumes that your user inputs have been validated, and that if the start day and end day are the same then the end time is greater than the start time


Your naming convention is a bit inconsistent, so I have renamed some of your variables and switched to camel case for readability

$nowDay = $arradate;
$nowTime = $nowtime;

$startDay = $start_day;
$startTime = $start_time;

$endDay = $end_day;
$endTime = $end_time;
Prather answered 2/10, 2019 at 13:25 Comment(4)
I am wondering if you can modify the code on the basis of inputs I have given in the question above. This is what I have tried 3v4l.org/56SZt Let me know if its look good.Huoh
@Huoh Yep, looks good as far as I can see, I have attached a legend to the bottom of my answer that indicates which variables I have renamed :)Prather
Your answer looks very good and its working for me. Thanks for your help. I came up with one condition. Sorry the requirements are beyond my control. Let say if its Thursday 160000 and Thursday 140000 which is not technically true. In this case, when day of the week is same then $endTime should always be greater than $startTime. Is there any we can put a validation that $endTime should always be greater than $startTime when day of the week is same ?Huoh
@Huoh Happy to help, if the answer is helpful, please consider upvoting it and/or marking as accepted. Re:validation, you should have something that checks that the user's inputs make sense (are actually days and times appropriately) and also fail if ($startDay == $endDay && $startTime > $endTime). Any errors can be reported back to the user giving them a chance to choose different inputs :)Prather
R
1

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.

Runnymede answered 1/10, 2019 at 23:13 Comment(10)
Hi, it has failed for this condition. Tuesday 6pm Friday 6pm. Right now, it is 10pm EST.Huoh
I think you are doing code on the basis of date, not on the basis of the days of the week, thats why it is failing.Huoh
I debugged, Start Date : DateTime Object ( [date] => 2019-09-25 11:30:00.000000 [timezone_type] => 3 [timezone] => America/Toronto ) End Date : DateTime Object ( [date] => 2019-09-25 11:35:00.000000 [timezone_type] => 3 [timezone] => America/Toronto ) Server Time : DateTime Object ( [date] => 2019-10-02 11:37:39.000000 [timezone_type] => 3 [timezone] => America/Toronto )Huoh
I admit, my solution is not fool-proof. I will take a look at it later.Runnymede
Alright, if you check this 3v4l.org/4X480 I am in EST and today is wednesday. I put wednesday 130000 and wednesday 140000. The code is taking wednesday on 25th September, not today. In short, it should be done on the basis of days of the week not date.Huoh
Ok, again the same error. Its Wednesday here EST time 5:04pm. When I put Monday 140000 Monday 180000. it is taking previous date 30 September. Monday should be now October 7. It would be a best idea to do on the basis of days of the week.Huoh
Why should Monday be the one on October 7th?Runnymede
We are not going on back date. September 30 will be the back date. Next Monday gonna come on October 7th. Lets think in days of the week. I think you are getting caught up with dates.Huoh
Check my cases above. I have mentioned 5 cases.Huoh
Let us continue this discussion in chat.Runnymede
E
1

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

Excursus answered 4/10, 2019 at 12:47 Comment(0)
D
0

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.

Defensible answered 29/9, 2019 at 2:45 Comment(4)
There is only one way. $arradate = strtolower(date('D')); extracts the day. User entered the days here $start_day=$data->{select_start_day}; $end_day=$data->{select_end_day};Huoh
Edited with an example.Defensible
I have modified my question. Let me know if its make senseHuoh
Yes and you also opened up a duplicate. The solution remains the same.Defensible
H
0

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";  
}

Hereto answered 29/9, 2019 at 3:22 Comment(4)
I think this logic is failing in some cases.Huoh
There is no issue in time logic. I am finding some issue in day of the week logic. In this one $arradateNum >= $start_dayNum and $arradateNum >= $end_dayNumHuoh
Its failed for this logic when start day of the week is Monday time 111300, end day of the week is Saturday time 115702Huoh
I have modified my question. Let me know if its make senseHuoh
S
0

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

Sheath answered 1/10, 2019 at 6:4 Comment(9)
I have edited my question. It is just a sample example. The user can enter any day of the week and time. It is not necessary that user enter mon 111300 and sat 114500Huoh
@Huoh yes the code will work for any user input. You need to capture user input using $_POSTSheath
I am wondering if you can update the answer. I am also doing it from my side.Huoh
I can update but What is the issue that you are facing?Sheath
The first part should be like this if ( $startDay >= $arradate && $endDay <= $arradate && $startHour >= 18 and $endHour <= 18 ) { echo 'In Range'; } else { echo 'out of Range'; } The user is entering the time in hhmmss not in hours.Huoh
You are comparing with hours here $startHour >= 18 and $endHour <= 18Huoh
Please go through the code. I have explained how startHour and endHour is calculated using format G from the datetime obj. The datetime obj is created dynamically based on user inputSheath
The actual should date not matter. It should be in between days of the week. I am in EST timezone. Let us suppose if I enter Saturday 143400 (Start Day/Time) and Wednesday 140000 (End Day/Time)Huoh
I have modified my question. Let me know if its make sense.Huoh
M
0

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!

Mcnelly answered 7/10, 2019 at 11:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.