Search code examples
phpdatedatetimestrtotime

How can I get a specific day of the month in PHP from a number?


Basically, I have a date set to today. From that date, I want to get the next and previous 20th day of the month. Basically, starting with 07/10/2020 (date of the post in d/m/Y format), I should end up with a date/timestamp to 20/09/2020 (previous 20th) and 20/10/2020 (next 20th).

I tried a few things and ended up with:

$ld_next_str = date("d/m/Y", strtotime("20 day this month"));
$ld_next_dt = DateTime::createFromFormat("d/m/Y", $ld_next_str);

$ld_prev_str = date("d/m/Y", strtotime("20 day last month"));
$ld_prev_dt = DateTime::createFromFormat("d/m/Y", $ld_prev_str);

But turns out this : "20 day this month" just adds 20 days to the current day so right now (on the seventh) it returns the 27th.

Knowing this, I figured I could probably just do something like subtracting the current day to that date (27 - 7 = 20) but I feel like there's probably a simpler way to do it, using strtotime without any extra steps.

Thanks in advance!

PS : I know you can get some days using "first" / "second" / "third" etc... but (I think) it doesn't go any higher than "twelfth", because I tried "twentieth" and that didn't work...


Solution

  • The formula for the previous 20th is:

    mktime(0, 0, 0, $month - (1 - floor($day / 20)), 20, $year))
    

    The formula for the next 20th is:

    mktime(0, 0, 0, $month + floor($day / 20), 20, $year))
    

    As a demo:

    for ($month = 1; $month <= 12; $month++) {
        for ($day = 1; $day <= 30; $day += 5) {
            $prev = mktime(0, 0, 0, $month - (1 - floor($day / 20)), 20, 2020);
            $next = mktime(0, 0, 0, $month + floor($day / 20), 20, 2020);
            echo "$day/$month/2020: ", date('d/m/Y', $prev), ', ', date('d/m/Y', $next), PHP_EOL;
        }
    }
    

    Outputs:

    1/1/2020: 20/12/2019, 20/01/2020
    6/1/2020: 20/12/2019, 20/01/2020
    11/1/2020: 20/12/2019, 20/01/2020
    16/1/2020: 20/12/2019, 20/01/2020
    21/1/2020: 20/01/2020, 20/02/2020
    26/1/2020: 20/01/2020, 20/02/2020
    1/2/2020: 20/01/2020, 20/02/2020
    6/2/2020: 20/01/2020, 20/02/2020
    11/2/2020: 20/01/2020, 20/02/2020
    16/2/2020: 20/01/2020, 20/02/2020
    21/2/2020: 20/02/2020, 20/03/2020
    ...