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

Posted in PHP on Thursday, the 22nd of September, 2011.

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:

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.

Comments

Posted by David Allen on Tuesday, the 29th of November, 2011.

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

Posted by Simon Harris on Tuesday, the 29th of November, 2011.

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.

Posted by Ciaran McNulty on Monday, the 9th of December, 2013.

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

Enter your comment: