Stop using datetime

Specially for students of the Backend Developer in PHP course , they prepared a translation of an interesting article about the side effect of a popular tool.










Working with dates and times in PHP is sometimes annoying because it leads to unexpected bugs in the code:



$startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s')); //2019-06-30 10:03:00 var_dump($finishedAt->format('Ymd H:i:s')); //2019-06-30 10:03:00
      
      





Both the $startdate



and $finishdate



are in a hurry for three minutes, because methods like add ()



, sub()



or modify()



also modify the DateTime object they are called for before returning it. The above example, of course, shows unwanted behavior.



We can fix this error by copying the object referenced before interacting with it, for example:



 $startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = clone $startedAt; $finishedAt->add(new DateInterval('PT3M'));
      
      





Every time I come across a clone in PHP code, it smells like a hack of someone's failed code architecture. In this case, we used cloning to avoid changing behavior, but at the same time, the code became ugly and acquired a lot of unnecessary noise.



Alternatively, the problem can be resolved by converting the original DateTime



instance to DateTimeImmutable



:



 $startedAt = new DateTime('2019-06-30 10:00:00'); $finishedAt = DateTimeImmutable::createFromMutable($startedAt)->add(new DateInterval('PT3M'));
      
      





Why not use DateTimeImmutable



from the very beginning?



Uncompromising Use of DateTimeImmutable



Instead of manually applying security methods to prevent unexpected changes when passing date / time objects, use DateTimeImmutable



, which encapsulates methods, making your code more reliable.



 $startedAt = new DateTimeImmutable('2019-06-30 10:00:00'); $finishedAt = $startedAt->add(new DateInterval('PT3M')); var_dump($startedAt->format('Ymd H:i:s')); //2019-06-30 10:00:00 var_dump($finishedAt->format('Ymd H:i:s')); //2019-06-30 10:03:00
      
      





In most cases, the concept of a date is considered as a value, we compare the dates by their values, and when we change the date, it becomes a different date. All this correlates perfectly with the concept of Value Object , and one of the important characteristics of value objects is that they are immutable.



Detailed coding style



Immutability forces you to explicitly reassign a DateTimeImmutable



object every time you interact with it, since it never changes its value, instead returning a copy. After many years of working with DateTime and because mutability is the default value in many imperative programming languages, itโ€™s hard to get rid of the habit of using it and conform to a new style of writing code that promotes remapping:



 $this->expiresAt = $this->expiresAt->modify('+1 week');
      
      





Statistical analysis tools such as PHPStan and one of its extensions can warn us if we omit the assignment and use DateTimeImmutable



incorrectly.



However, such a cognitive bias towards variability is suppressed when we perform arithmetic operations on the values โ€‹โ€‹of primitives, for example: $a + 3;



. In itself, this is perceived as a meaningless statement that clearly lacks a reassignment: $a = $a + 3;



or $A += 3;



. It would be cool to use something like this in the case of value objects, right?



Some programming languages โ€‹โ€‹have syntactic sugar called operator overloading, which allows you to implement operators in user-defined types and classes so that they behave just like primitive data types. I would not mind if PHP borrowed this trick from some other programming language, and we could write as follows:



 $this->expiresAt += '1 week';
      
      





One-time calculations



Some people argue that in terms of performance, it is better to use DateTime



, since the calculations are performed within the same execution area. This is acceptable, however, if you do not need to perform hundreds of operations, and you remember that links to old DateTimeImmutable



objects will be collected by the garbage collector, in most cases, in practice, memory consumption will not be a problem.



Date / time libraries



Carbon is an extremely popular library that extends the Date / Time API in PHP, adding a rich feature set. More precisely, it extends the API of the DateTime



mutable class, the use of which is contrary to the topic of this article.



Therefore, if you enjoy working with Carbon but prefer immutability, I suggest you familiarize yourself with Chronos . This is a standalone library, which was originally based on Carbon, paying particular attention to providing immutable default date / time objects, but it also includes mutable options in case of need.

Edited (07/05/2019): It turned out that Carbon has an immutable date / time option, which is a big plus. However, the reason I prefer Chronos is that, unlike Carbon, it encourages and promotes default immutability, both in code and in documentation, and these are decisive factors in the context of this article.


Last thought



DateTimeImmutable



was first introduced back in ancient PHP 5.5, but to my surprise, many developers are discovering it just now. Use DateTimeImmutable



by default whenever possible, but keep in mind some of the trade-offs that I talked about that I think are more a matter of habit and a shift in mindset.



That's all. By tradition, we are waiting for your comments, friends.



All Articles