Definisi task baru lengkap setelah frekuensinya ditentukan: seberapa sering dan pada jam berapa task harus berjalan. Scheduler Laravel menyediakan puluhan method frekuensi yang pada akhirnya dikompilasi menjadi satu ekspresi cron lima field — jadi memahami frekuensi berarti memahami bagaimana method-method tersebut menulis cron untuk kita. Pada chapter ini kita petakan method yang tersedia, bereksperimen dengan schedule:list sebagai alat verifikasi, lalu tutup dengan constraint kondisional dan urusan yang sering bikin terpeleset: zona waktu.

Method Frekuensi Inti #

Berikut pemetaan method frekuensi yang paling sering dipakai beserta ekspresi cron yang dihasilkannya:

MethodEkspresi cronBerarti
everySecond()* * * * *setiap detik (sub-menit)
everyMinute()* * * * *setiap menit
everyFiveMinutes()*/5 * * * *tiap 5 menit
hourly()0 * * * *tiap jam tepat
hourlyAt(17)17 * * * *menit ke-17 tiap jam
daily() / dailyAt('2:30')0 0 * * * / 30 2 * * *tengah malam / jam tertentu
twiceDaily(8, 14)0 8,14 * * *dua kali sehari
weekly() / weeklyOn(1, '8:00')0 0 * * 0 / 0 8 * * 1mingguan / Senin jam 08:00
monthly() / monthlyOn(1, '0:00')0 0 1 * *tanggal 1 tiap bulan
quarterly()0 0 1 1-12/3 *awal kuartal
yearly()0 0 1 1 *sekali setahun

Method jam (at('08:00'), dailyAt(), weeklyOn()) menerima format HH:MM atau H:MM. Method menit (hourlyAt()) juga menerima array, misalnya hourlyAt([10, 30, 50]) menghasilkan 10,30,50 * * * *.

Keluarga jam yang lebih jarang dipakai mengikuti pola yang sama dan semuanya menerima offset: everyTwoHours(30) berarti menit ke-30 setiap dua jam, lalu everyThreeHours(), everyFourHours(), dan everySixHours() mengikuti. Untuk kasus yang tidak tercakup method mana pun, method cron() menerima ekspresi mentah — cron('15 10 * * 1-5') menulis field lima langsung tanpa perantara. Prinsip pemilihannya sederhana: pakai method bernama bila tersedia agar jadwal terbaca sebagai niat, simpan cron() untuk pola yang benar-benar khusus.

Eksperimen: Sebelas Jadwal Sekaligus #

Cara tercepat memahami komposisi frekuensi adalah melihat langsung hasil kompilasinya. Kita definisikan sebelas task di routes/console.php — sengaja memakai command yang sama agar fokus jatuh pada pola cronnya:

php
Schedule::call(fn () => null)->everyMinute()->name('ping-healthcheck');
Schedule::command('tasks:prune')->everyFiveMinutes();
Schedule::command('tasks:prune')->hourlyAt(17);
Schedule::command('tasks:prune')->dailyAt('02:30');
Schedule::command('tasks:prune')->twiceDaily(8, 14);
Schedule::command('tasks:prune')->weekdays()->at('08:00');
Schedule::job(new SendDailyReport)->weeklyOn(1, '03:00');
Schedule::command('report:generate')->monthlyOn(1, '00:00');
Schedule::command('report:generate')->quarterly();
Schedule::command('report:generate')->yearly();
Schedule::command('report:generate')->dailyAt('06:00')->timezone('Asia/Jakarta');

Output yang Dihasilkan (schedule:list):

text
  *   *    * *      *    ping-healthcheck ........ Next Due: 1 minute from now
  */5 *    * *      *    php artisan tasks:prune  Next Due: 5 minutes from now
  17  *    * *      *    php artisan tasks:prune  Next Due: 47 minutes from now
  30  2    * *      *    php artisan tasks:prune .. Next Due: 6 hours from now
  0   8,14 * *      *    php artisan tasks:prune . Next Due: 11 hours from now
  0   8    * *      1-5  php artisan tasks:prune . Next Due: 11 hours from now
  0   3    * *      1    App\Jobs\SendDailyReport .. Next Due: 5 days from now
  0   0    1 *      *    php artisan report:generate  Next Due: 2 weeks from now
  0   0    1 1-12/3 *    php artisan report:generate  Next Due: 2 months from now
  0   0    1 1      *    php artisan report:generate  Next Due: 2 months from now
  0   23   * *      *    php artisan report:generate  Next Due: 2 hours from now

Perhatikan dua hal dari listing di atas. Pertama, weekdays()->at('08:00') menyusun dua field sekaligus: field jam 8 dan field hari 1-5 (Senin sampai Jumat). Kedua, baris terakhir tampil sebagai 0 23 * * * padahal kodenya dailyAt('06:00') — itu efek ->timezone('Asia/Jakarta') yang akan kita bedah di subbab terakhir.

Constraint Tambahan #

Selain pola cron, scheduler menyediakan constraint yang dievaluasi saat runtime. Yang paling sering dipakai:

  • weekdays() — membatasi Senin–Jumat (mengubah field hari pada cron).
  • between('09:00', '17:00') — hanya berjalan pada rentang jam tertentu.
  • when(closure) / skip(closure) — kondisi bebas yang mengembalikan boolean.

Berikut demonstrasi nyata keduanya. Sandbox mendefinisikan dua task everyMinute — satu dengan between(), satu dengan when() — lalu schedule:run dieksekusi pada pukul 20:25:

php
Schedule::command('tasks:prune')
    ->everyMinute()
    ->between('09:00', '17:00')
    ->name('prune-jam-kerja');

Schedule::command('tasks:prune')
    ->everyMinute()
    ->when(fn () => date('N') < 6)
    ->name('prune-hari-kerja');

Output yang Dihasilkan:

text
  2026-10-13 20:25:21 Running ['artisan' tasks:prune] .......... 217.40ms DONE
  ⇂ prune-hari-kerja  

Hanya prune-hari-kerja yang tampil. Task prune-jam-kerja lolos uji cron * * * * * tetapi ditolak constraint between('09:00', '17:00') karena pukul 20:25 berada di luar jam kerja — dan 13 Oktober 2026 adalah hari Selasa (date('N') === 2), sehingga closure when() mengembalikan true. Constraint gagal berarti task dilewati tanpa error; pengecekan terjadi di filtersPass() sebelum eksekusi.

Zona Waktu: Cron Server vs Aplikasi #

Kronologi menentukan kapan schedule:run dipanggil ada di tangan cron daemon yang mengikuti jam server. Namun penentuan task mana yang due dievaluasi scheduler berdasarkan zona waktu aplikasi — nilai timezone di config/app.php, yang defaultnya UTC.

Method ->timezone() mengubah evaluasi per-task. Jadwal dailyAt('06:00')->timezone('Asia/Jakarta') pada eksperimen kita berarti pukul 06:00 WIB — scheduler mengeksekusinya saat jam UTC menunjukkan 23:00. Perintah schedule:list --timezone=Asia/Jakarta memperlihatkan seluruh jadwal dalam tampilan WIB:

text
  30  9     * *      *    php artisan tasks:prune . Next Due: 6 hours from now
  0   15,21 * *      *    php artisan tasks:prune  Next Due: 11 hours from now
  0   15    * *      1-5  php artisan tasks:prune . Next Due: 11 hours from now
  0   10    * *      1    App\Jobs\SendDailyReport . Next Due: 5 days from now
  0   6     * *      *    php artisan report:generate  Next Due: 2 hours from now

Task 30 2 * * * (02:30 UTC) kini tampil sebagai 30 9 * * * (09:30 WIB), sementara task ber-timezone Jakarta tetap tampil 0 6 * * * karena memang didefinisikan dalam WIB.

Frekuensi dan constraint mengatur kapan task berjalan; dua chapter ke depan mengatur bagaimana task berjalan. Yang pertama: mencegah eksekusi tumpang tindih ketika task berjalan lebih lama dari intervalnya di Preventing Overlapping . Rujukan method frekuensi lengkap ada di dokumentasi Task Scheduling .