Jump to content

Matching day of week and time of day


NotionCommotion

Recommended Posts

 

I wish to find the closest two DateTimes which are within $fillStart and $fillEnd and have the same week and 24 hour times as $gapStart and $gapEnd.

For instance, the following results in $fillStartModified and $fillEndModified which meets that criteria.

fillStart           2019-07-23 00:15:00 Tue
fillEnd             2019-09-23 13:00:00 Mon
gapStart            2019-05-23 00:15:00 Thu
gapEnd              2019-06-23 13:00:00 Sun
fillStartModified   2019-07-25 00:15:00 Thu
fillEndModified     2019-08-25 13:00:00 Sun

I came up with the following which seems to work, but it is kind of complicated and I am not positive it will meet all edge conditions.  Any recommendations?  Thanks

function getFill(\DateTimeImmutable $fillStart, \DateTimeImmutable $fillEnd, \DateTimeInterface $gapStart, \DateTimeInterface $gapEnd)
{
    //$gapInterval = $gapStart->diff($gapEnd);  // Doesn't work 
    $gapInterval = new \DateInterval('PT'.($gapEnd->getTimestamp() - $gapStart->getTimestamp()).'S');
    if($gapStart > $fillStart) {
        //The fill is older than the gap so make the fill's endTime match the gap's endTime
        $fillEndModified = $fillEnd
        ->modify('previous '.$gapEnd->format('D'))
        ->setTime(...explode('.', $gapEnd->format('H.i.s.u')));
        if($fillEndModified->diff($fillEnd)->format('%a') >= 7) {
            $fillEndModified = $fillEndModified->add(new \DateInterval('P7D'));
        }
        $fillStartModified = $fillEndModified->sub($gapInterval);
        if($fillStartModified < $fillStart) {
            $fillStartModified=null;
            $fillEndModified=null;
        }
    }
    else {
        //The fill is newer than the gap so make the fill's startTime match the gap's startTime
        $fillStartModified = $fillStart
        ->modify('next '.$gapStart->format('D'))
        ->setTime(...explode('.', $gapStart->format('H.i.s.u')));
        if($fillStart->diff($fillStartModified)->format('%a') >= 7) {
            $fillStartModified = $fillStartModified->sub(new \DateInterval('P7D'));
        }
        $fillEndModified = $fillStartModified->add($gapInterval);
        if($fillEndModified > $fillEnd) {
            $fillStartModified=null;
            $fillEndModified=null;
        }
    }
    return [$fillStartModified, $fillEndModified];
}

 

Link to comment
Share on other sites

Hold on.

3 hours ago, NotionCommotion said:

I wish to find the closest two DateTimes which are within $fillStart and $fillEnd and have the same week and 24 hour times as $gapStart and $gapEnd.

For instance, the following results in $fillStartModified and $fillEndModified which meets that criteria.


fillStart           2019-07-23 00:15:00 Tue
fillEnd             2019-09-23 13:00:00 Mon
gapStart            2019-05-23 00:15:00 Thu
gapEnd              2019-06-23 13:00:00 Sun
fillStartModified   2019-07-25 00:15:00 Thu
fillEndModified     2019-08-25 13:00:00 Sun

 

How did you arrive at those two dates?

Link to comment
Share on other sites

10 hours ago, NotionCommotion said:

Just kidding, nothing unethical or illegal.

I don't think anyone thought there was?  The question is just why these dates:

On 9/2/2020 at 10:20 AM, NotionCommotion said:

fillStartModified   2019-07-25 00:15:00 Thu

fillEndModified     2019-08-25 13:00:00 Sun

Instead of something else.  Based on your description, $fillStartModified makes sense, it's the first thursday after $fillStart.  Your $fillEndModified however does not.  According to your description you want the closest date within the fill range that matches the same weekday.  I interpret that as meaning that for $fillEndModified want either 2019-07-28 (first sunday after $fillStartModified/$fillStart) or 2019-09-22 (last sunday before $fillEnd).

I haven't bothered to analyze your code to determine what it's doing.  That's too much like work and it seems you're not sure it's even correct.  A better English definition of your exact requirements/process is needed.

Link to comment
Share on other sites

3 hours ago, kicken said:

Instead of something else.  Based on your description, $fillStartModified makes sense, it's the first thursday after $fillStart.  Your $fillEndModified however does not.  According to your description you want the closest date within the fill range that matches the same weekday.  I interpret that as meaning that for $fillEndModified want either 2019-07-28 (first sunday after $fillStartModified/$fillStart) or 2019-09-22 (last sunday before $fillEnd).

I haven't bothered to analyze your code to determine what it's doing.  That's too much like work and it seems you're not sure it's even correct.  A better English definition of your exact requirements/process is needed.

Say I have a fill from January 1st to January 31st and from April 1st to April 30th.  Don't have a calendar in front of me to confirm the week days, but if I had a gap from February 10th to the 12th, the closest would be latest time in January which ends on the same time/week as the February 12th gap end, and if I had a gap from March 20th to 22nd, the closest would be the earliest time in April which starts on the same time/week as the February 10th gap start.

Link to comment
Share on other sites

I don't have a calendar in front of me either, but I'm really sure that there is no time in January which ends on the same week as February 12th.

Try explaining it again but with different words. Let's say that you can't use the words "gap", "earliest", or "closest".

Link to comment
Share on other sites

8 hours ago, requinix said:

I don't have a calendar in front of me either, but I'm really sure that there is no time in January which ends on the same week as February 12th.

Try explaining it again but with different words. Let's say that you can't use the words "gap", "earliest", or "closest".

You mean you can't read my mind to gain an understanding of how I define those words?  Sorry requinix, my bad.

Typically with PHP, we consider time the combination of the day/month/year plus the seconds past midnight, but for this application I am defining "WeekDayTime" as the day of the week (D) plus the seconds past midnight.  For instance, Examples #1 to #4 are not the same WeekDayTime as Thu 2019-01-10 13:34:25 because the day of the week is different, #8 is not the same because the seconds past midnight are different, but #5 to #7 are the same because they both have the same day of the week and seconds past midnight.

I am also defining "gap" as two DateTimes where the later is greater than the first, and "fill" as two other DateTimes again where the later is greater than the first.

Applying this definition of WeekDayTime, fill, and gap, I am trying to determine the start and end DateTimes (not WeekDayTimes) which:

  • fall within two other (fill) DateTimes.
  • have the same number of seconds between them as two other (gap) DateTimes.
  • have the same WeekDayTime as two other (gap) DateTimes.
  • have the least number of seconds between the start time and the gap's start time (or the end time and the gap's end time which is the same)

Sorry, still used "gap" and "fill", but hope it makes more sense now.

Target      Thu 2019-01-10 13:34:25

Example #1: Fri 2019-01-11 13:34:25 (P1D)
Example #2: Wed 2019-01-16 13:34:25 (P6D)
Example #3: Sun 2019-02-10 13:34:25 (P1M)
Example #4: Fri 2020-01-10 13:34:25 (P1Y)
Example #5: Thu 2019-01-17 13:34:25 (P7D)
Example #6: Thu 2019-01-24 13:34:25 (P14D)
Example #7: Thu 2019-05-02 13:34:25 (P112D)
Example #8: Thu 2019-05-02 14:34:25 (P112DT1H)
$dtTarget = new \DateTimeImmutable('@'.(time()-rand(600*24*60*60, 700*24*60*60)));
echo(sprintf("Target      %s\n\n", $dtTarget->format('D Y-m-d H:i:s')));
foreach(['P1D', 'P6D', 'P1M', 'P1Y', 'P7D', 'P14D', 'P112D', 'P112DT1H'] as $index=>$interval) {
    echo(sprintf("Example #%d: %s (%s)\n", $index+1, $dtTarget->add(new \DateInterval($interval))->format('D Y-m-d H:i:s'), $interval));
}

 

Link to comment
Share on other sites

Almost thought I understood:

fillStart           2019-07-23 00:15:00 Tue
fillEnd             2019-09-23 13:00:00 Mon
gapStart            2019-05-23 00:15:00 Thu
gapEnd              2019-06-23 13:00:00 Sun
fillStartModified   2019-07-25 00:15:00 Thu
fillEndModified     2019-08-25 13:00:00 Sun

"fill" is the date range you're working with, and "gap" is a start and end whose weekday numbers and times (aka "WeekDayTimes") should be copied. You need to move the fill's start date forwards the least amount of time (ie, "to the next") such that it has the same weekday number and time as the "gap" start; same for the fill's end date except it goes backwards.

That would explain how 07-23 00:15 becomes 07-25 00:15 (moved forwards "to the next" Thu 00:15) but not how 09-23 13:00 moved back almost a month to 08-25 13:00.

PS: https://3v4l.org/0lBR6

Edited by requinix
Link to comment
Share on other sites

8 hours ago, requinix said:

Almost thought I understood:

Oops!  Looks like I failed to say that new fill must also match the gap's duration.  Sorry bout that.

What had been originally messing me up were these two lines (but my original posted showed the DateInterval as being commented out as it didn't work).  I create a DateInterval for my desired duration, determine either the appropriate modified fill start or end date, and then get the other associated modified fill date by applying the gap DateInterval, and it mostly works but not always.

$fillStartModified = $fillEndModified->sub($gapStart->diff($gapEnd));

$fillEndModified = $fillStartModified->add($gapStart->diff($gapEnd));

Which is basically what you and kicken indicated on an earlier post.  DateIntervals do not represent an absolute duration but are based on what they are applied to.  Makes me more appreciate  all PHP's date functions are doing.

 

I think my original solution is good enough, but just for the heck of it have provided another narrative.

 

Given gap:  The gap starts on a Thursday at 00:15:00 and ends on a Sunday at 13:00:00, and there are 31 days, 12 hours, and 45 minutes between the two dates.

gapStart            2019-05-23 00:15:00 Thu
gapEnd              2019-06-23 13:00:00 Sun

 

Given constraint: The modified fill start and end must fall withing the original fill start and end time, and should be selected so it is as close to the gap as possible.

fillStart           2019-07-23 00:15:00 Tue
fillEnd             2019-09-23 13:00:00 Mon

 

Results:  The modified fill also starts on a Thursday at 00:15:00 and ends on a Sunday at 13:00:00, and there are also 31 days, 12 hours, and 45 minutes between the two dates.  It also falls within the given fill start and end time, and is positioned toward the start of this time span in order to be as close to the gap as possible.

fillStartModified   2019-07-25 00:15:00 Thu
fillEndModified     2019-08-25 13:00:00 Sun

 

Link to comment
Share on other sites

You saw the link I posted, right? That's most of the way there.

1. Calculate the correct new starting date and time.
2. Get the diff between the gap, in days, then subtract 1 from it. Measured in days because having exact weekdays is important, and a little short so that it's definitely going to be the day before the desired time.
3. Add that many days to the new start, then do the next weekday/time thing. PHP will handle the rest.

https://3v4l.org/7JDgp

The two things to keep in mind: working with periods defined in terms of month units or larger sucks so don't try, and DST really needs to go away once and for all.

Link to comment
Share on other sites

Hi requnix,

Yes, saw it.  I like yours more than my original version as it is more concise, but I think it doesn't select the right dates if the fill range is earlier than the gap range.  Any slicker ways to do it, or just put some if fill > gap, do what you show, else do something similar as necessary?  By the way, how do you set https://3v4l.org/0RJfq up so it does not try every version of PHP?

Of topic, but your solution provides different results if given DateTimeImmutable instead of DateTime.  If this wasn't just a quick example, would you typically write script which would work for both.

Thanks

Link to comment
Share on other sites

5 hours ago, NotionCommotion said:

but I think it doesn't select the right dates if the fill range is earlier than the gap range.

Looks right to me. https://3v4l.org/UJJfa

 

5 hours ago, NotionCommotion said:

By the way, how do you set https://3v4l.org/0RJfq up so it does not try every version of PHP?

You don't. 3v4l is meant for testing code across multiple versions.

 

5 hours ago, NotionCommotion said:

Of topic, but your solution provides different results if given DateTimeImmutable instead of DateTime.  If this wasn't just a quick example, would you typically write script which would work for both.

My code uses mutable DateTime objects. If you have to use immutable objects then the code needs to care about return values.

Link to comment
Share on other sites

52 minutes ago, requinix said:

Looks right to me. https://3v4l.org/UJJfa

Try these dates

Fill  Wednesday 1978-09-27 12:34:56 ->    Tuesday 2020-09-08 12:34:56
Gap   Wednesday 2020-09-09 01:02:03 ->     Friday 2020-09-11 04:05:06
New   Wednesday 1978-10-04 01:02:03 ->     Friday 1978-10-06 04:05:06

 

1 hour ago, requinix said:

You don't. 3v4l is meant for testing code across multiple versions.

When I view your link, I see one output, but with mine, I see dozens.

Your output:

image.thumb.png.7252bf9ce93e2972f5c572298b09f266.png

My Output

image.thumb.png.646f0f015464563c97eda84979608f29.png

 

Link to comment
Share on other sites

19 minutes ago, NotionCommotion said:

Try these dates


Fill  Wednesday 1978-09-27 12:34:56 ->    Tuesday 2020-09-08 12:34:56
Gap   Wednesday 2020-09-09 01:02:03 ->     Friday 2020-09-11 04:05:06
New   Wednesday 1978-10-04 01:02:03 ->     Friday 1978-10-06 04:05:06

 

Still looks right to me:
- Start date = fill's start date (Wednesday 12pm) moved foward to the gap's start (Wednesday 1am) = a week later
- End date = new fill's start date (Wednesday 1am) copied forward to the gap's end (Friday 4am) = 2 days later

What was the result supposed to be, and why?

 

19 minutes ago, NotionCommotion said:

When I view your link, I see one output, but with mine, I see dozens.

3v4l groups by output. Your code uses random numbers and current timestamps so the output from each run will be different. Protip: don't do that.

Link to comment
Share on other sites

Okay, so then you need to check two ranges:

1. Existing logic, except you base the new start date as coming after the gap end instead of after the fill start.
2. Mostly the same logic, except (a) you determine the new end based on the gap start, then (b) adjust the new start according to the new end.

Then decide which of those two new ranges you want to use.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.