Asked  7 Months ago    Answers:  5   Viewed   30 times

I failed to find a proper solution to this issue. As you see in Example #3 in the PHP documentation, they state that one must beware when adding months using the DateInterval in DateTime::add.

There's not really any explanation for why the method's behavior is as such and what I can do to avoid this, which I find to be an error at first sight.

Anyone have some insight into this?

 Answers

77

The issue is that each month can have a different number of days in them. The question is what you're doing when you want to increment a date by 1 month. Per the PHP documentation if you're on January 31st (or 30th) and you add 1 month, what is the expected behavior?

February only has 29 days in it. Do you want to be set to the last day of the month? You're generally safer incrementing by a set number of days if that's what you're looking for, or a static date based on the current date. Without knowing what you're trying to accomplish when you increment your month, it's tough to say how to watch for an error.

EDIT:
As someone mentions in the similar post commented by Mike B above, you probably want to do something where you (in pseudocode):

 1) Use cal_days_in_month() for the next month and save that number to a variable x
 2) If x >= current billing DOB, increment and be done
 3) DateTime::modify('last day')  (havent used this before but something along these lines) to set the date to the last date of the next month (set it to the 1st of the next month, then last day?)

Worth noting is that if you use the variable here as the new billing value, you'll wipe out your original value. I would save an extra DB value that's "first billing date" or just "billing_day_of_month" or something, and use that to figure out the day of month that you should be looking at

Wednesday, March 31, 2021
 
van_folmert
answered 7 Months ago
76

The problem is that the end date/time itself is never included in the period -- that is, the DatePeriod object represents date/times in the [$startDate, $endDate) range¹.

You have to add one second at least to the end date to always get the expected results. But let's not be stingy and add a whole minute:

$days = new DatePeriod(
    new DateTime("first $day of $m $y"),
    DateInterval::createFromDateString('next ' . $day),
    new DateTime("last day of $m $y 00:01")
);

¹ unless you use the DatePeriod::EXCLUDE_START_DATE option, in which case the start date itself is excluded as well

Wednesday, March 31, 2021
 
Gregosaurus
answered 7 Months ago
17

A note from DateInterval::format:

The DateInterval::format() method does not recalculate carry over points in time strings nor in date segments. This is expected because it is not possible to overflow values like "32 days" which could be interpreted as anything from "1 month and 4 days" to "1 month and 1 day".

So one has to recalculate carry over points. Below is the relevant code from DateInterval::format:

class DateIntervalEnhanced extends DateInterval {
    public function recalculate() {
        $from = new DateTime;
        $to = clone $from;
        $to->add($this);
        $diff = $from->diff($to);
        foreach ($diff as $k => $v) $this->$k = $v;
        return $this;
    }
}

A utility function:

function myFormatter($d1, $d2, $format) {
    $diff = strtotime($d1) - strtotime($d2);
    $df = abs($diff);
    $di = new DateIntervalEnhanced("PT${df}S");
    $di->invert = $diff < 0;
    return $di->recalculate()->format($format);
}

echo myFormatter("2017-12-31 23:59:59", "2017-12-01 00:00:00", "%m");

DEMO

Link to a post you might wanna read

Saturday, May 29, 2021
 
pocketfullofcheese
answered 5 Months ago
13

First of all, a text editor's internal representation of text has no bearing on how the text is encoded (serialized) when you save the file. So a document is not "in" an encoding; it's a sequence of abstract characters. When the document is saved to a file (or transmitted over the network) then it gets encoded.

It's up to each application to decide what it puts on the clipboard. Typically, a windows app that knows what it's doing will put a number of different representations on the clipboard. When you paste in the other app, the app will look for the representation that best suits its need.

In your case, a text editor (that knows what it's doing) will put a Unicode representation of a selected string onto the clipboard (where Unicode, in Windows, is typically moved around as UTF-16, but that's not important). When you paste in the other app, it will insert that sequence of Unicode characters into the document at the selection point.

There's an app floating around called "ClipSpy" that will help you see what I'm talking about, interactively.

Sunday, July 18, 2021
 
Baba
answered 3 Months ago
81

The most visible way is for mapping structures.

Any class which does this will have unpredictable behavior when used as the Key for a Dictionary or HashTable. The reason being is that the implementation uses both GetHashCode and Equals to properly find a value in the table. The short version of the algorithm is the following

  1. Take the modulus of the HashCode by the number of buckets and that's the bucket index
  2. Call .Equals() for the specified Key and every Key in the particular bucket.
  3. If there is a match that is the value, no match = no value.

Failing to keep GetHashCode and Equals in sync will completely break this algorithm (and numerous others).

Sunday, August 15, 2021
 
Santi
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :