NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Filter any metric with range(). It accepts a Period case or its string value — the two are interchangeable — plus an explicit start and end for a custom window:

use RoundlyConsulting\Metrics\Enums\Period;

$metric->range('MTD');                 // month to date
$metric->range(Period::MonthToDate);   // identical
$metric->range(Period::ThisQuarter);   // the full current quarter
$metric->range(Period::Custom, '2024-01-01 00:00:00', '2024-03-01 00:00:00');
$metric->range(Period::All);           // no date filtering (the default)

Every period

KeyEnum caseLabelWindow
7, 14, 30, 60, 90, 365Days7 … Days3657 Days … 365 DaysThe last N days, up to now
TODAYTodayTodayToday, start to end of day
YESTERDAYYesterdayYesterdayThe whole of yesterday
WTDWeekToDateWeek To DateMonday of this week to now
MTDMonthToDateMonth To DateStart of the month to now
QTDQuarterToDateQuarter To DateStart of the quarter to now
YTDYearToDateYear To DateStart of the year to now
THIS_WEEKThisWeekThis WeekThe full current ISO week, Monday to Sunday
LAST_WEEKLastWeekLast WeekThe full previous ISO week
THIS_MONTHThisMonthThis MonthThe full current month
LAST_MONTHLastMonthLast MonthThe full previous month
THIS_QUARTERThisQuarterThis QuarterThe full current quarter
LAST_QUARTERLastQuarterLast QuarterThe full previous quarter
THIS_YEARThisYearThis YearThe full current year
LAST_YEARLastYearLast YearThe full previous year
CUSTOMCustomCustomExplicit start and end
ALLAllAllNo date filter (the default)

The default range is ALL — no date filtering — unless you change metrics.default_range. ranges() on any metric returns the same key → label map, and labels run through Laravel’s translator, so a range picker can be localised in your app’s translation files.

Weeks are ISO weeks — Monday to Sunday — whatever the app locale, the same weeks the WEEK trend buckets count. Quarter and year-to-date comparisons never overflow on month-end days: on 12-31, LAST_QUARTER is Q3, and on a leap day the previous YTD ends on 02-28.

Timezones

A metric has a reporting timezone — timezone() on the metric (or Dashboard::timezone()), else config('metrics.timezone'), else the app timezone. Ranges are resolved in it (TODAY is today there, and CUSTOM bounds are read as its wall clock), and trend buckets are labelled in it — handy for per-tenant reporting:

$metric->timezone('America/New_York')->range(Period::Today)->toArray();

Timestamps are taken to be stored in the app timezone (config('app.timezone')), which is how Eloquent writes them. Range bounds are converted to it before they reach the query, and a trend moves each row’s timestamp onto the reporting clock before bucketing it — daylight-saving changes of either zone included — so a row stored at 2026-09-14 23:30 UTC counts as 2026-09-15 in Europe/Bratislava:

config(['metrics.timezone' => 'Europe/Bratislava']);   // app timezone: UTC

Metrics::value()->count(User::query())->range('TODAY')->result()->value();
Metrics::trend()->count(User::query(), 'id')->hourly()->range('TODAY')->result()->trends();
// ['2026-09-15 00:00' => ..., '2026-09-15 01:00' => ..., ...] — local hours

A date column (no time of day) has no timezone to convert from; aggregate one with the reporting timezone left at the app timezone.

Enum toolkit

Period and Unit are string-backed enums built on enums-for-laravel, so they come with helpers for host selects and validation:

use RoundlyConsulting\Metrics\Enums\Period;
use RoundlyConsulting\Metrics\Enums\Unit;

Period::toOptions()->all();   // ['7' => '7 Days', ..., 'ALL' => 'All'] — value => label map
Period::options();            // Collection<EnumOption> of {value, label, name} DTOs for JS/Inertia
Period::validationRule();     // 'in:7,14,30,...,ALL'
Unit::toOptions()->all();     // ['MINUTE' => 'Minute', ..., 'YEAR' => 'Year']
Unit::validationRule();       // 'in:MINUTE,HOUR,DAY,WEEK,MONTH,YEAR'
Unit::labels();               // Collection<string> of readable labels

Validate a range coming from the request before applying it — an unknown value throws InvalidRangeException when the metric resolves:

use RoundlyConsulting\Metrics\Enums\Period;
use RoundlyConsulting\Metrics\Facades\Metrics;

$validated = $request->validate([
    'period' => ['required', Period::validationRule()],
]);

return Metrics::get('revenue')->range($validated['period']);

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.