Skip to content

strtotime +- months/weeks results in wrong month and/or falls back to epoch instead of returning false #20048

@kkmuffme

Description

@kkmuffme

Description

This bug may seem trivial and unimportant at first glance, but it's quite a massive security issue where strtotime is used to limit/allow access for example.

The following code: https://3v4l.org/EnM7A#v8.4.13
(echo date('m', strtotime('-1 month', strtotime( '2025-03-31' ) ) ) . PHP_EOL; has the same bug just fyi)

<?php
echo date('m', strtotime('2025-03-31 -1 month')) . PHP_EOL;
echo date('m', strtotime('2025-03-30 -1 month')) . PHP_EOL;
echo date('m', strtotime('2025-05-31 -1 month')) . PHP_EOL;
echo date('m', strtotime('2025-05-30 -1 month')) . PHP_EOL;

Resulted in this output:

03
03
05
04

But I expected this output instead:

02
02
04
04

When using 2 months, then it works correctly for all months, except the ones that would end up with February it seems, e.g.

echo date('m', strtotime('2025-04-30 -2 months')) . PHP_EOL;
echo date('m', strtotime('2025-06-30 -2 months')) . PHP_EOL;

results in

03
04

but should be

02
04

Same issue with +, e.g.

echo date('m', strtotime('2025-01-30 +1 month')) . PHP_EOL;
echo date('m', strtotime('2025-03-31 +1 month')) . PHP_EOL;

results in

03
05

expected

02
04

This seems to be related/caused by another 2 bugs:

echo date('Y-m-d', strtotime('2025-02-30 -4 weeks')) . PHP_EOL;
echo date('Y-m-d', strtotime('2025-02-32 -4 weeks')) . PHP_EOL;

outputs:

2025-02-02
1970-01-01

But should be false in both cases, since there is no February 30th
The 2nd case however, is where this actually becomes a dangerous issue not only in the full date case example, but especially when not using Y-m-d but just 'm', you'll end up with "01" only, which is extremely bad.
If using this for any kind of validation (e.g. nonce, refund period,...) this is a security risk

It seems to work correctly for + and - when using year (e.g. leap years) though

PHP Version

all PHP versions up to, including PHP 8.5

Operating System

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions