Precision Through Imprecision: Improving Time Objects
tl;dr When creating value objects representing time, I recommend choosing how finegrained the time should be with your domain experts and round it off to that precision in the value object.
When modeling important numbers, it’s considered good form to specify the precision. Whether it’s money, size or weight; you’ll typically round off to a given decimal point. Even if it’s only for user display, rounding off makes the data more predictable for manipulation and storage.
Unfortunately, we don’t often do this when handling time and it bites us in the rear. Consider the following code:
$estimatedDeliveryDate = new DateTimeImmutable('2017-06-21');
// let's assume today is ALSO 2017-06-21
$now = new DateTimeImmutable('now');
if ($now > $estimatedDeliveryDate) {
echo 'Package is late!';
} else {
echo 'Package is on the way.';
}
Since it’s June 21 in the code sample, this code should print “Package is on the way.” After all, the day isn’t over yet, it might just be coming later in the afternoon.
Except the code doesn’t do that. Because we didn’t specify the time component, PHP helpfully zero pads $estimatedDeliveryDate
to 2017-06-21 00:00:00
. On the other hand, $now
is calculated for…now. “Now” includes the current time (which probably isn’t midnight), so you’ll get 2017-06-21 15:33:34
which is indeed later than 2017-06-21 00:00:00
.
Solution 1
“Oh, this is a quick fix.” folks might say and update it to the following.
$estimatedDeliveryDate = new DateTimeImmutable('2017-06-21');
$estimatedDeliveryDate = $estimatedDeliveryDate->setTime(23, 59);
Cool, we changed the time to include up to midnight. Except the time is padded to 23:59:00
so if you look in the last 59 seconds of the day, you’ll have the same problem.
“Grrr, okay.” folks might say.
$estimatedDeliveryDate = new DateTimeImmutable('2017-06-21');
$estimatedDeliveryDate = $estimatedDeliveryDate->setTime(23, 59, 59);
Cool, now it’s fixed.
…Unless you’re on PHP 7.1 which adds microseconds to DateTime
objects. So now it only occurs on the last second of the day. I may be biased after working on too many high traffic systems but sooner or later a user or a automated process will hit that and complain. Good luck tracking down THAT bug. :-/
Okay, let’s add microseconds.
$estimatedDeliveryDate = new DateTimeImmutable('2017-06-21')
$estimatedDeliveryDate = $estimatedDeliveryDate
Truncated by Planet PHP, read more at the original (another 23822 bytes)