Date ranges
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
| Key | Enum case | Label | Window |
|---|---|---|---|
7, 14, 30, 60, 90, 365 | Days7 … Days365 | 7 Days … 365 Days | The last N days, up to now |
TODAY | Today | Today | Today, start to end of day |
YESTERDAY | Yesterday | Yesterday | The whole of yesterday |
WTD | WeekToDate | Week To Date | Monday of this week to now |
MTD | MonthToDate | Month To Date | Start of the month to now |
QTD | QuarterToDate | Quarter To Date | Start of the quarter to now |
YTD | YearToDate | Year To Date | Start of the year to now |
THIS_WEEK | ThisWeek | This Week | The full current ISO week, Monday to Sunday |
LAST_WEEK | LastWeek | Last Week | The full previous ISO week |
THIS_MONTH | ThisMonth | This Month | The full current month |
LAST_MONTH | LastMonth | Last Month | The full previous month |
THIS_QUARTER | ThisQuarter | This Quarter | The full current quarter |
LAST_QUARTER | LastQuarter | Last Quarter | The full previous quarter |
THIS_YEAR | ThisYear | This Year | The full current year |
LAST_YEAR | LastYear | Last Year | The full previous year |
CUSTOM | Custom | Custom | Explicit start and end |
ALL | All | All | No 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 hoursA 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 labelsValidate 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 cryptoBy 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.