23
23
*/
24
24
readonly class LocalDateRange implements IValueObject, Stringable, IteratorAggregate, Countable
25
25
{
26
+ /**
27
+ * NOTE: 範囲種別(子クラスでオーバーライド可能)
28
+ */
29
+ public static function rangeType (): RangeType
30
+ {
31
+ return RangeType::HALF_OPEN_RIGHT ; // デフォルトの範囲タイプ
32
+ }
33
+
26
34
/**
27
35
* Avoid new() operator.
28
36
*
32
40
final private function __construct (
33
41
private mixed $ from ,
34
42
private mixed $ to ,
35
- private RangeType $ rangeType
36
43
) {
37
44
// NOTE: 不変条件(invariant)
38
45
assert (static ::isValid ($ from , $ to )->isOk ());
@@ -46,7 +53,7 @@ final public function equals(IValueObject $other): bool
46
53
{
47
54
return $ this ->from ->equals ($ other ->from )
48
55
&& $ this ->to ->equals ($ other ->to )
49
- && $ this -> rangeType === $ other ->rangeType ;
56
+ && static :: rangeType () === $ other ->rangeType () ;
50
57
}
51
58
52
59
#[Override]
@@ -75,9 +82,8 @@ final public function jsonSerialize(): string
75
82
final public static function from (
76
83
mixed $ from ,
77
84
mixed $ to ,
78
- RangeType $ rangeType = RangeType::HALF_OPEN_RIGHT
79
85
): static {
80
- return new static ($ from , $ to, $ rangeType );
86
+ return new static ($ from , $ to );
81
87
}
82
88
83
89
/**
@@ -89,10 +95,9 @@ final public static function from(
89
95
final public static function tryFrom (
90
96
mixed $ from ,
91
97
mixed $ to ,
92
- RangeType $ rangeType = RangeType::HALF_OPEN_RIGHT
93
98
): Result {
94
99
return static ::isValid ($ from , $ to )
95
- ->andThen (static fn () => Result \ok (static ::from ($ from , $ to, $ rangeType )));
100
+ ->andThen (static fn () => Result \ok (static ::from ($ from , $ to )));
96
101
}
97
102
98
103
// -------------------------------------------------------------------------
@@ -126,12 +131,12 @@ protected static function isValid(mixed $from, mixed $to): Result
126
131
*/
127
132
final public function toISOString (): string
128
133
{
129
- $ leftBracket = match ($ this -> rangeType ) {
134
+ $ leftBracket = match (static :: rangeType () ) {
130
135
RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => '[ ' ,
131
136
RangeType::OPEN , RangeType::HALF_OPEN_LEFT => '( ' ,
132
137
};
133
138
134
- $ rightBracket = match ($ this -> rangeType ) {
139
+ $ rightBracket = match (static :: rangeType () ) {
135
140
RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => '] ' ,
136
141
RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => ') ' ,
137
142
};
@@ -155,9 +160,26 @@ final public function getTo(): mixed
155
160
return $ this ->to ;
156
161
}
157
162
158
- final public function getRangeType (): RangeType
163
+ /**
164
+ * @return TStart
165
+ */
166
+ final public function getFromAsClosed (): mixed
167
+ {
168
+ return match (static ::rangeType ()) {
169
+ RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => $ this ->from ,
170
+ RangeType::OPEN , RangeType::HALF_OPEN_LEFT => $ this ->from ->addDays (1 ),
171
+ };
172
+ }
173
+
174
+ /**
175
+ * @return TEnd
176
+ */
177
+ final public function getToAsClosed (): mixed
159
178
{
160
- return $ this ->rangeType ;
179
+ return match (static ::rangeType ()) {
180
+ RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => $ this ->to ,
181
+ RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => $ this ->to ->addDays (-1 ),
182
+ };
161
183
}
162
184
163
185
/**
@@ -167,7 +189,7 @@ final public function getRangeType(): RangeType
167
189
*/
168
190
final public function withFrom (mixed $ from ): static
169
191
{
170
- return static ::from ($ from , $ this ->to , $ this -> rangeType );
192
+ return static ::from ($ from , $ this ->to );
171
193
}
172
194
173
195
/**
@@ -177,7 +199,7 @@ final public function withFrom(mixed $from): static
177
199
*/
178
200
final public function withTo (mixed $ to ): static
179
201
{
180
- return static ::from ($ this ->from , $ to, $ this -> rangeType );
202
+ return static ::from ($ this ->from , $ to );
181
203
}
182
204
183
205
/**
@@ -187,7 +209,7 @@ final public function withTo(mixed $to): static
187
209
*/
188
210
final public function tryWithFrom (mixed $ from ): Result
189
211
{
190
- return static ::tryFrom ($ from , $ this ->to , $ this -> rangeType );
212
+ return static ::tryFrom ($ from , $ this ->to );
191
213
}
192
214
193
215
/**
@@ -197,20 +219,20 @@ final public function tryWithFrom(mixed $from): Result
197
219
*/
198
220
final public function tryWithTo (mixed $ to ): Result
199
221
{
200
- return static ::tryFrom ($ this ->from , $ to, $ this -> rangeType );
222
+ return static ::tryFrom ($ this ->from , $ to );
201
223
}
202
224
203
225
/**
204
226
* 指定された日付が範囲内に含まれるかを判定
205
227
*/
206
228
final public function contains (LocalDate $ date ): bool
207
229
{
208
- $ afterFrom = match ($ this -> rangeType ) {
230
+ $ afterFrom = match (static :: rangeType () ) {
209
231
RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => $ date ->isAfterOrEqualTo ($ this ->from ),
210
232
RangeType::OPEN , RangeType::HALF_OPEN_LEFT => $ date ->isAfter ($ this ->from ),
211
233
};
212
234
213
- $ beforeTo = match ($ this -> rangeType ) {
235
+ $ beforeTo = match (static :: rangeType () ) {
214
236
RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => $ date ->isBeforeOrEqualTo ($ this ->to ),
215
237
RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => $ date ->isBefore ($ this ->to ),
216
238
};
@@ -240,10 +262,10 @@ final public function strictlyBefore(self $other): bool
240
262
{
241
263
return $ this ->to ->isBefore ($ other ->from ) || (
242
264
$ this ->to ->is ($ other ->from ) && (
243
- $ this -> rangeType === RangeType::OPEN
244
- || $ this -> rangeType === RangeType::HALF_OPEN_RIGHT
245
- || $ other ->rangeType === RangeType::OPEN
246
- || $ other ->rangeType === RangeType::HALF_OPEN_LEFT
265
+ static :: rangeType () === RangeType::OPEN
266
+ || static :: rangeType () === RangeType::HALF_OPEN_RIGHT
267
+ || $ other ->rangeType () === RangeType::OPEN
268
+ || $ other ->rangeType () === RangeType::HALF_OPEN_LEFT
247
269
)
248
270
);
249
271
}
@@ -260,7 +282,7 @@ final public function days(): int
260
282
$ days = (int )(($ toTimestamp - $ fromTimestamp ) / 86400 );
261
283
262
284
// 区間タイプによる調整
263
- return match ($ this -> rangeType ) {
285
+ return match (static :: rangeType () ) {
264
286
RangeType::CLOSED => $ days + 1 , // 両端を含む
265
287
RangeType::OPEN => max (0 , $ days - 1 ), // 両端を含まない
266
288
RangeType::HALF_OPEN_LEFT , RangeType::HALF_OPEN_RIGHT => $ days , // 片方の端を含む
@@ -274,12 +296,12 @@ final public function days(): int
274
296
#[Override]
275
297
final public function getIterator (): Generator
276
298
{
277
- $ current = match ($ this -> rangeType ) {
299
+ $ current = match (static :: rangeType () ) {
278
300
RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => $ this ->from ,
279
301
RangeType::OPEN , RangeType::HALF_OPEN_LEFT => $ this ->from ->addDays (1 ),
280
302
};
281
303
282
- $ endCondition = match ($ this -> rangeType ) {
304
+ $ endCondition = match (static :: rangeType () ) {
283
305
RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => fn (LocalDate $ date ) => $ date ->isBeforeOrEqualTo ($ this ->to ),
284
306
RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => fn (LocalDate $ date ) => $ date ->isBefore ($ this ->to ),
285
307
};
0 commit comments