Handling rounding millisecond issue with `diffInSeconds()` in Time
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:
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:
- Add 1 second with the birthday to reach to next day.
- Somehow the job execute exactly at
0
millisecond, which is technically not possible. - Increase number of retry.
Add 1 second
now()->addSeconds(now()->diffInSeconds(now()->addDay(1)->startOfDay()->addSecond()))
The result will be:
Run in millisecond
$now = \Carbon\CarbonImmutable::now()->setMilliseconds(0); $now->addSeconds($now->diffInSeconds($now->addDay(1)->startOfDay()->setMilliseconds(0)))
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 🙂.