Handling rounding millisecond issue with `diffInSeconds()` in Time

Handling rounding millisecond issue with `diffInSeconds()` in Time

Posted on:September 5, 2023 at 02:00 PM

If you are using laravel and carbon, there are high change to have familiar with diffInSeconds() function. This function is helpful to find the time difference between two time in second format.

What you need?

  • A laravel application

Table of Content

Dig into deep

Let’s dig into deep about diffInSeconds(). Let’s investigate this code:

// Move time to midnight tomorrow
$tomorrow = now()->addDay(1)->startOfDay();
// Get time in Second format
$getUntilTomorrowInSecond = now()->diffInSeconds($tomorrow);

now()->addSeconds($getUntilTomorrowInSecond);

Theoretically it will print out tomorrow (midnight) 12:00 AM.

Open the tinker session, and try to run this code. In reality you should see end of today 11:59:59, just 1 second short of tomorrow.

now()->addSeconds(now()->diffInSeconds(now()->addDay(1)->startOfDay()))

It will result like this:

"Theoretically it will print out tomorrow (midnight) 12:00 AM."

Scenario

Imagine that you have a command that finds all the users those have birthday tomorrow. So, the command will find all of them and queue a job for tomorrow.

Manage command

To create the command, you need to run the following command:

php artisan make:command FindBirthdayCandidates

In the command, I want to list down all the users who has birthday tomorrow. And then queuing job for wishing them tomorrow.

<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Jobs\WishForBirthday;
use Illuminate\Console\Command;

class FindBirthdayCandidates extends Command
{
    protected $signature = 'birthday-candidates';

    protected $description = 'Find list of user who has birthday tomorrow';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        $users = User::where('date_of_birth', now()->addDay())->get();

        foreach ($users as $user) {
            WishForBirthday::dispatch()
                // Delaying the job for tomorrow
                ->delay($user->date_of_birth->diffInSeconds(now()));
        }

        return 0;
    }
}

ℹ️ I assume date_of_birth returns the dateTime / Carbon instance.

What’s happened?

We expect to get the difference between now and birth date of users (tomorrow) in second format. For example, if the difference is 1 hour, it will return 60 seconds as a result from here. But unfortunately it end up the end of the day, just 1 second short of tomorrow.

$user->date_of_birth->diffInSeconds(now()));

Your job actually ended up by today, even without reaching to tomorrow. There is a high change to fail your job.

How to overcome

There are three ways to solve this issue:

  1. Add 1 second with the birthday to reach to next day.
  2. Somehow the job execute exactly at 0 millisecond, which is technically not possible.
  3. Increase number of retry.

Add 1 second

now()->addSeconds(now()->diffInSeconds(now()->addDay(1)->startOfDay()->addSecond()))

The result will be:

"Add 1 second"

Run in millisecond

$now = \Carbon\CarbonImmutable::now()->setMilliseconds(0); $now->addSeconds($now->diffInSeconds($now->addDay(1)->startOfDay()->setMilliseconds(0)))

"Run in millisecond"

Increase number of retry

In the job, increase the number of retry.

class WishForBirthday implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

+    public $retry = 3;

    public function handle()
    {
        // Some logic
    }
}

Hope it will help you to solve the particular issue.

Thanks for reading 🙂.