How to set just the year of a DateTime using the modify method?

According to the documentation, it should be possible to change only the year component of a DateTime using the modify() method. However, this only seems to work for years whose century is between 60-99.

(new DateTime)->modify('1960')->format('c'); // '1960-01-25T19:09:44+00:00'

If the century is between 00-59 (inclusive), it is instead interpreted as a time.

(new DateTime)->modify('1959')->format('c'); // '2024-01-25T19:59:00+00:00'

That seems fair enough; the parser cannot tell if we intended a date or time otherwise.

How can we force the parser to interpret our input as a year? According to the documentation for compound formats, a literal T should differentiate between the date and time parts of the input.

(new DateTime)->modify('1959T')->format('c'); // '2024-01-25T19:59:00+00:00'

Simply specifying T with no suffix has no effect.

(new DateTime)->modify('1959T0000')->format('c'); // '0000-01-25T19:59:00+00:00'

This assumes we don’t care about the time part of the date because we’re expecting it to be set to midnight as a side-effect of setting the year. However, that’s not what happens: it sets the year to 0000 and still takes 1959 as the time. Being as that’s the case, we can invert this and get the desired result.

// Note that `T` is interchangeable with a space here.
(new DateTime)->modify('0000 1959')->format('c'); // '1959-01-25T00:00:00+00:00'

This works (with the side-effect of setting the time) but how could we ever have the reasonable expectation that this should work (or will continue to work in future)? In absolutely none of the valid, documented compound formats does the time ever appear before the date, so why do we have to specify them in reverse order?

Any further attempts to force the interpreter to treat 0000 as a time specifier after the date results in parse failures because it thinks we’re specifying the time twice.

(new DateTime)->modify('1959T00:00')->format('c');
(new DateTime)->modify('1959T00:00:00')->format('c');
// DateTime::modify(): Failed to parse time string (1959T00:00:00) at position 4 (T): Double time specification 

I’m aware there are similar questions about setting just the year, but this question is asking whether there is any incantation of modify(), specifically, that will set just the year. The accepted answer otherwise involves explicitly setting the month and day again, which is inelegant and should not be necessary.

Although '0000 1959' appears to be a valid way to set just the year, it can be improved in one of two ways:

  • This incantation appears to be undocumented. A documented way to achieve the same would be a valid improvement.
  • The proposed incantation has the unwanted side-effect of modifying the time. An alternative that only changes the year would be a valid improvement.

Aside, it would be interesting if someone familiar with internals could comment on why '0000 1959'/'0000T1959' is parsed in the demonstrated manner (i.e. time before date), which has not changed since it was first implemented.

  • 6

    why don’t you use setDate?

    – 

  • 4

    Or just date("Y {other params}")? what’s the purpose of what you are trying to do? Functionality or curiousity?

    – 




  • 5

    Also — ” inelegant and should not be necessary” is subjective, and completely opinion based.

    – 

  • 2

    Why do you need to change the year of an exising date anyway (with an absolute value)? That’s not common.

    – 

  • 2

    “same values is objectively wasted CPU cycles and characters on screen as well as on disk” — If you have to split hairs like this to save overhead .. You need some more CPU allocation my friend. Otherwise Tom-aye-toe to-mah-toe — I say you’re having an XY Problem

    – 




Leave a Comment