Skip to content

Commit 9df28bf

Browse files
committed
use sub no overflow for all non-fixed date units and also first sub and then set to start/end, which guarantees a right start/end range
1 parent 95589ad commit 9df28bf

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The default for this package is **exclusive** approach,
4040
which means when you for instance query for the last 7 days it will **not include** the current day!
4141
You can change the default if you need in the published config file.
4242

43-
### Config
43+
### Global configuration
4444

4545
You can publish the config file with:
4646

@@ -55,7 +55,7 @@ return [
5555
/**
5656
* If you want to include the current day/week/month/year etc. in the range,
5757
* you could use the inclusive range here as a default.
58-
* Note that you can also optionally specify it for quite every scope we offer
58+
* Note that you can also fluently specify the range for quite every scope we offer
5959
* directly when using the scope:
6060
* Transaction::ofLast7Days(DateRange::INCLUSIVE); (this works for all but the singular "ofLast"-scopes)
6161
* This will do an inclusive query, even though the global default range here is set to exclusive.
@@ -71,6 +71,20 @@ return [
7171

7272
If you want to change the default range to inclusive set `DATE_SCOPES_DEFAULT_RANGE=inclusive` in your `.env`.
7373

74+
### Fluent configuration
75+
76+
As already mentioned above in the `default_range` config description text,
77+
you can also fluently specify the range for quite every scope we offer
78+
directly when using the scope:
79+
80+
```php
81+
// This works for all "ofLast"-scopes, expect the singulars like "ofLastHour",
82+
// because it would not make sense for those.
83+
Transaction::ofLast7Days(DateRange::INCLUSIVE);
84+
```
85+
86+
This will do an inclusive query (today-6 days), even though the global default range here was set to exclusive.
87+
7488
## Scopes
7589

7690
- [`seconds`](#seconds)

config/date-scopes.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/**
77
* If you want to include the current day/week/month/year etc. in the range,
88
* you could use the inclusive range here as a default.
9-
* Note that you can also optionally specify it for quite every scope we offer
9+
* Note that you can also fluently specify it for quite every scope we offer
1010
* directly when using the scope:
1111
* Transaction::ofLast7Days(DateRange::INCLUSIVE); (this works for all but the singular "ofLast"-scopes)
1212
* This will do an inclusive query, even though the global default range here is set to exclusive.

src/DateScopes.php

+24-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@
66

77
trait DateScopes
88
{
9+
/**
10+
* For example, if you subtract one month from March 31, you would get February 31, which is not a valid date.
11+
* The subMonthsNoOverflow method for instance would instead return February 28 (or February 29 in a leap year),
12+
* as it adjusts the day to the last day of the month when the resulting date would be invalid.
13+
*
14+
* In Carbon, the date units that can have this "overflow" behavior are months, years, decades, etc. because their lengths can vary.
15+
* Days, hours, minutes, and seconds have fixed lengths, so they do not have this issue.
16+
*
17+
* @var array|string[]
18+
*/
19+
private array $fixedLengthDateUnits = [
20+
'second',
21+
'minute',
22+
'hour',
23+
'day'
24+
];
25+
926
/**
1027
* @param Builder $query Eloquent Builder
1128
* @param string $dateUnit A valid date unit, such as hour, day, month, year etc...
@@ -21,23 +38,25 @@ public function scopeOfLastUnit(Builder $query, string $dateUnit, int $value, Da
2138

2239
$startFunc = 'startOf'.ucfirst($dateUnit);
2340
$endFunc = 'endOf'.ucfirst($dateUnit);
24-
$subFunc = 'sub'.ucfirst($dateUnit).'s';
41+
42+
$applyNoOverflow = (in_array($dateUnit, $this->fixedLengthDateUnits)) ? 'NoOverflow' : '' ;
43+
$subFunc = 'sub'.ucfirst($dateUnit).'s'.$applyNoOverflow;
2544

2645
$sub = ($dateUnit === 'second') ? 0 : 1;
2746

2847
if ($dateRange === DateRange::EXCLUSIVE) {
2948
$range = [
30-
'from' => now()->$startFunc()->$subFunc($value),
31-
'to' => now()->$endFunc()->$subFunc($sub),
49+
'from' => now()->$subFunc($value)->$startFunc(),
50+
'to' => now()->$subFunc($sub)->$endFunc(),
3251
];
3352
} else {
3453
$range = [
35-
'from' => now()->$startFunc()->$subFunc($value - $sub),
54+
'from' => now()->$subFunc($value - $sub)->$startFunc(),
3655
'to' => now()->$endFunc(),
3756
];
3857
}
3958

40-
// dd(collect($range)->transform(fn ($item) => $item->format('Y-m-d H:i:s')));
59+
// dd(collect($range)->transform(fn ($item) => $item->format('Y-m-d H:i:s'))->toArray());
4160

4261
return $query->whereBetween(config('date-scopes.created_column'), $range);
4362
}

0 commit comments

Comments
 (0)