2323 */
2424readonly class LocalDateRange implements IValueObject, Stringable, IteratorAggregate, Countable
2525{
26+ /**
27+ * NOTE: 範囲種別(子クラスでオーバーライド可能)
28+ */
29+ public static function rangeType (): RangeType
30+ {
31+ return RangeType::HALF_OPEN_RIGHT ; // デフォルトの範囲タイプ
32+ }
33+
2634 /**
2735 * Avoid new() operator.
2836 *
3240 final private function __construct (
3341 private mixed $ from ,
3442 private mixed $ to ,
35- private RangeType $ rangeType
3643 ) {
3744 // NOTE: 不変条件(invariant)
3845 assert (static ::isValid ($ from , $ to )->isOk ());
@@ -46,7 +53,7 @@ final public function equals(IValueObject $other): bool
4653 {
4754 return $ this ->from ->equals ($ other ->from )
4855 && $ this ->to ->equals ($ other ->to )
49- && $ this -> rangeType === $ other ->rangeType ;
56+ && static :: rangeType () === $ other ->rangeType () ;
5057 }
5158
5259 #[Override]
@@ -75,9 +82,8 @@ final public function jsonSerialize(): string
7582 final public static function from (
7683 mixed $ from ,
7784 mixed $ to ,
78- RangeType $ rangeType = RangeType::HALF_OPEN_RIGHT
7985 ): static {
80- return new static ($ from , $ to, $ rangeType );
86+ return new static ($ from , $ to );
8187 }
8288
8389 /**
@@ -89,10 +95,9 @@ final public static function from(
8995 final public static function tryFrom (
9096 mixed $ from ,
9197 mixed $ to ,
92- RangeType $ rangeType = RangeType::HALF_OPEN_RIGHT
9398 ): Result {
9499 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 )));
96101 }
97102
98103 // -------------------------------------------------------------------------
@@ -126,12 +131,12 @@ protected static function isValid(mixed $from, mixed $to): Result
126131 */
127132 final public function toISOString (): string
128133 {
129- $ leftBracket = match ($ this -> rangeType ) {
134+ $ leftBracket = match (static :: rangeType () ) {
130135 RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => '[ ' ,
131136 RangeType::OPEN , RangeType::HALF_OPEN_LEFT => '( ' ,
132137 };
133138
134- $ rightBracket = match ($ this -> rangeType ) {
139+ $ rightBracket = match (static :: rangeType () ) {
135140 RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => '] ' ,
136141 RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => ') ' ,
137142 };
@@ -155,9 +160,26 @@ final public function getTo(): mixed
155160 return $ this ->to ;
156161 }
157162
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
159178 {
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+ };
161183 }
162184
163185 /**
@@ -167,7 +189,7 @@ final public function getRangeType(): RangeType
167189 */
168190 final public function withFrom (mixed $ from ): static
169191 {
170- return static ::from ($ from , $ this ->to , $ this -> rangeType );
192+ return static ::from ($ from , $ this ->to );
171193 }
172194
173195 /**
@@ -177,7 +199,7 @@ final public function withFrom(mixed $from): static
177199 */
178200 final public function withTo (mixed $ to ): static
179201 {
180- return static ::from ($ this ->from , $ to, $ this -> rangeType );
202+ return static ::from ($ this ->from , $ to );
181203 }
182204
183205 /**
@@ -187,7 +209,7 @@ final public function withTo(mixed $to): static
187209 */
188210 final public function tryWithFrom (mixed $ from ): Result
189211 {
190- return static ::tryFrom ($ from , $ this ->to , $ this -> rangeType );
212+ return static ::tryFrom ($ from , $ this ->to );
191213 }
192214
193215 /**
@@ -197,20 +219,20 @@ final public function tryWithFrom(mixed $from): Result
197219 */
198220 final public function tryWithTo (mixed $ to ): Result
199221 {
200- return static ::tryFrom ($ this ->from , $ to, $ this -> rangeType );
222+ return static ::tryFrom ($ this ->from , $ to );
201223 }
202224
203225 /**
204226 * 指定された日付が範囲内に含まれるかを判定
205227 */
206228 final public function contains (LocalDate $ date ): bool
207229 {
208- $ afterFrom = match ($ this -> rangeType ) {
230+ $ afterFrom = match (static :: rangeType () ) {
209231 RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => $ date ->isAfterOrEqualTo ($ this ->from ),
210232 RangeType::OPEN , RangeType::HALF_OPEN_LEFT => $ date ->isAfter ($ this ->from ),
211233 };
212234
213- $ beforeTo = match ($ this -> rangeType ) {
235+ $ beforeTo = match (static :: rangeType () ) {
214236 RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => $ date ->isBeforeOrEqualTo ($ this ->to ),
215237 RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => $ date ->isBefore ($ this ->to ),
216238 };
@@ -240,10 +262,10 @@ final public function strictlyBefore(self $other): bool
240262 {
241263 return $ this ->to ->isBefore ($ other ->from ) || (
242264 $ 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
247269 )
248270 );
249271 }
@@ -260,7 +282,7 @@ final public function days(): int
260282 $ days = (int )(($ toTimestamp - $ fromTimestamp ) / 86400 );
261283
262284 // 区間タイプによる調整
263- return match ($ this -> rangeType ) {
285+ return match (static :: rangeType () ) {
264286 RangeType::CLOSED => $ days + 1 , // 両端を含む
265287 RangeType::OPEN => max (0 , $ days - 1 ), // 両端を含まない
266288 RangeType::HALF_OPEN_LEFT , RangeType::HALF_OPEN_RIGHT => $ days , // 片方の端を含む
@@ -274,12 +296,12 @@ final public function days(): int
274296 #[Override]
275297 final public function getIterator (): Generator
276298 {
277- $ current = match ($ this -> rangeType ) {
299+ $ current = match (static :: rangeType () ) {
278300 RangeType::CLOSED , RangeType::HALF_OPEN_RIGHT => $ this ->from ,
279301 RangeType::OPEN , RangeType::HALF_OPEN_LEFT => $ this ->from ->addDays (1 ),
280302 };
281303
282- $ endCondition = match ($ this -> rangeType ) {
304+ $ endCondition = match (static :: rangeType () ) {
283305 RangeType::CLOSED , RangeType::HALF_OPEN_LEFT => fn (LocalDate $ date ) => $ date ->isBeforeOrEqualTo ($ this ->to ),
284306 RangeType::OPEN , RangeType::HALF_OPEN_RIGHT => fn (LocalDate $ date ) => $ date ->isBefore ($ this ->to ),
285307 };
0 commit comments