22 Sep 2011, 1:57 p.m.

Some Thoughts on PHP's DateTime, Object Mutability and an Alternative Implementation

I'm beginning think that while the introduction of PHP's newish DateTime object is very welcome, its implementation is one of the big missed opportunities in the language. This is because the decision was taken to make it mutable. In essence, DateTime has been implemented as an Entity rather than a Value Object.

Value Objects

This is kind of odd, because dates and times are almost the canonical example of a Value Object, and this is how most languages implement them. I'll let Martin Fowler explain it better than I can:

For value objects to work properly...it's a very good idea to make them immutable - that is, once created none of their fields change. The reason for this is to avoid aliasing bugs. An aliasing bug occurs when two objects share the same value object and one of the owners changes the values in it.

Thus, if Martin has a hire date of March 18 and we know that Cindy was hired on the same day, we may set Cindy's hire date to be the same [object] as Martin's. If Martin then changes the month in his hire date to May, Cindy's hire date changes too. Whether it's correct or not, it isn't what people expect.

Usually with small values like this people expect to change a hire date by replacing the existing date object with a new one. Making Value Objects immutable fulfills that expectation.

So by choosing to make DateTime mutable, you break people's expectations about how values work, and increase the risk of introducing obscure bugs.

You also lose the option of employing a lot of patterns that apply to Value Objects, such as Flyweight, which allows us to optimise by replacing large numbers of similar objects with references to a small number of shared Value Objects.

DateTime Arithmetic

Perhaps more practically, in PHP at least, this mutability leads to some very clunky syntax when calculating new dates based on intervals. So given a DateTime named $startdate and a DateInterval named $interval, here's how I'd like to be able to calculate an end date:

<?php

$enddate = $startdate->add($interval);

In this way, $startdate would be safely unmodified, whilst $enddate would be a new DateTime object. But in actual fact, DateTime::add() and DateTime::sub() modify the original object.

Here's a kind of fiddly workaround:

<?php

$enddate = clone($startdate);
$enddate->add($interval);

And you sort of have to hope that nobody uses $enddate in between, and that nobody tampers with $startdate elsewhere in the code. There may be better ways of doing this, so do comment if there are.

ImDateTime

For fun, I've started to put together an immutable alternative to DateTime, which implements the syntax that I'd prefer to see, and which I've named ImDateTime. It isn't exactly production-ready, or even finished, but it's there on GitHub for people to have a play with.

Posted by Simon in PHP
29 Nov 2011, 1:40 a.m.

David Allen

HI there,
We are thinking of implementing a similar object but I thought I would first google and see what I came up with.
Your object looks good.
However, you wrote it is not production ready.
What did you mean by that?
Regards
David

29 Nov 2011, 1:51 a.m.

Simon Harris

Thanks, David.

I should really update that point, as the code is ready for use now. I'd be happy for people to use it, make suggestions and even send pull requests.

9 Dec 2013, 8:33 p.m.

Ciaran McNulty

For anyone who finds this on Google, PHP 5.5 now contains a DateTimeImmutable object that operates much like Simon describes here.

http://php.net/DateTimeImmutable