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

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