NotionCommotion Posted September 2, 2020 Share Posted September 2, 2020 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]; } Quote Link to comment Share on other sites More sharing options...
requinix Posted September 2, 2020 Share Posted September 2, 2020 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? Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 3, 2020 Author Share Posted September 3, 2020 17 hours ago, requinix said: Hold on. How did you arrive at those two dates? Well, turns out the FDA will be stopping by later today to audit our 21 CFR Part 11 drug production facility and ... Just kidding, nothing unethical or illegal. Quote Link to comment Share on other sites More sharing options...
requinix Posted September 3, 2020 Share Posted September 3, 2020 ...so about those dates... Quote Link to comment Share on other sites More sharing options...
kicken Posted September 3, 2020 Share Posted September 3, 2020 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 4, 2020 Author Share Posted September 4, 2020 4 hours ago, requinix said: ...so about those dates... My birthday and anniversary? Wait, there's more dates. Been wanting a larger family and all my recent children's birthday? Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 4, 2020 Author Share Posted September 4, 2020 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. Quote Link to comment Share on other sites More sharing options...
requinix Posted September 4, 2020 Share Posted September 4, 2020 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". Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 4, 2020 Author Share Posted September 4, 2020 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)); } Quote Link to comment Share on other sites More sharing options...
requinix Posted September 4, 2020 Share Posted September 4, 2020 (edited) 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 September 4, 2020 by requinix Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 5, 2020 Author Share Posted September 5, 2020 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 Quote Link to comment Share on other sites More sharing options...
requinix Posted September 5, 2020 Share Posted September 5, 2020 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 6, 2020 Author Share Posted September 6, 2020 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 Quote Link to comment Share on other sites More sharing options...
requinix Posted September 6, 2020 Share Posted September 6, 2020 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 6, 2020 Author Share Posted September 6, 2020 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: My Output Quote Link to comment Share on other sites More sharing options...
requinix Posted September 6, 2020 Share Posted September 6, 2020 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted September 7, 2020 Author Share Posted September 7, 2020 Oops. Looked for a way to delete my 3v4l post or even ask someone to do so, but no luck. Wednesday 2020-09-02 01:02:03 to Friday 2020-09-04 04:05:06 is the closest to the gap. Quote Link to comment Share on other sites More sharing options...
requinix Posted September 7, 2020 Share Posted September 7, 2020 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.