Skip to content

Commit 55e9fa8

Browse files
authored
Merge pull request #49 from wiz-develop:endou-mame/issue48
LocalDateRange の rangeType を動的ではなく静的に決定するよう修正、閉区間として From, TO を取得するメソッドを追加
2 parents bbe1514 + 36dfdd9 commit 55e9fa8

File tree

7 files changed

+283
-122
lines changed

7 files changed

+283
-122
lines changed

examples/DateTime/TestLocalDateRange.php

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
use WizDevelop\PhpValueObject\DateTime\LocalDate;
88
use WizDevelop\PhpValueObject\DateTime\LocalDateRange;
9-
use WizDevelop\PhpValueObject\DateTime\RangeType;
9+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange\LocalDateRangeClosed;
10+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange\LocalDateRangeHalfOpenRight;
11+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange\LocalDateRangeOpen;
1012

1113
require_once __DIR__ . '/../../vendor/autoload.php';
1214

@@ -21,15 +23,13 @@
2123

2224
// 2. 開区間と閉区間の違い
2325
echo "=== 開区間と閉区間の違い ===\n";
24-
$closedWeek = LocalDateRange::from(
26+
$closedWeek = LocalDateRangeClosed::from(
2527
LocalDate::of(2024, 1, 1),
2628
LocalDate::of(2024, 1, 7),
27-
RangeType::CLOSED,
2829
);
29-
$openWeek = LocalDateRange::from(
30+
$openWeek = LocalDateRangeOpen::from(
3031
LocalDate::of(2024, 1, 1),
3132
LocalDate::of(2024, 1, 7),
32-
RangeType::OPEN,
3333
);
3434

3535
echo "閉区間(両端含む): {$closedWeek->toISOString()} = {$closedWeek->days()}\n";
@@ -38,10 +38,9 @@
3838
// 3. 半開区間の使用例(一般的な日付範囲の表現)
3939
echo "=== 半開区間の使用例 ===\n";
4040
// 月初から月末まで(月末を含まない一般的なパターン)
41-
$month = LocalDateRange::from(
41+
$month = LocalDateRangeHalfOpenRight::from(
4242
LocalDate::of(2024, 1, 1),
4343
LocalDate::of(2024, 2, 1),
44-
RangeType::HALF_OPEN_RIGHT,
4544
);
4645

4746
echo "1月(右半開区間): {$month->toISOString()}\n";
@@ -50,10 +49,9 @@
5049

5150
// 4. 日付の反復処理
5251
echo "=== 日付の反復処理 ===\n";
53-
$weekRange = LocalDateRange::from(
52+
$weekRange = LocalDateRangeClosed::from(
5453
LocalDate::of(2024, 1, 1),
5554
LocalDate::of(2024, 1, 7),
56-
RangeType::CLOSED,
5755
);
5856

5957
echo "1週間の日付:\n";
@@ -64,20 +62,17 @@
6462

6563
// 5. 期間の重なり判定
6664
echo "=== 期間の重なり判定 ===\n";
67-
$q1 = LocalDateRange::from(
65+
$q1 = LocalDateRangeClosed::from(
6866
LocalDate::of(2024, 1, 1),
6967
LocalDate::of(2024, 3, 31),
70-
RangeType::CLOSED,
7168
);
72-
$q2 = LocalDateRange::from(
69+
$q2 = LocalDateRangeClosed::from(
7370
LocalDate::of(2024, 4, 1),
7471
LocalDate::of(2024, 6, 30),
75-
RangeType::CLOSED,
7672
);
77-
$marchToMay = LocalDateRange::from(
73+
$marchToMay = LocalDateRangeClosed::from(
7874
LocalDate::of(2024, 3, 1),
7975
LocalDate::of(2024, 5, 31),
80-
RangeType::CLOSED,
8176
);
8277

8378
echo "第1四半期: {$q1->toISOString()}\n";
@@ -100,10 +95,9 @@
10095

10196
// 7. エラーハンドリング
10297
echo "=== エラーハンドリング ===\n";
103-
$invalidResult = LocalDateRange::tryFrom(
98+
$invalidResult = LocalDateRangeClosed::tryFrom(
10499
LocalDate::of(2024, 12, 31),
105100
LocalDate::of(2024, 1, 1),
106-
RangeType::CLOSED,
107101
);
108102

109103
if ($invalidResult->isErr()) {
@@ -119,10 +113,9 @@
119113

120114
// 9. 年間カレンダーの例
121115
echo "\n=== 年間カレンダーの例 ===\n";
122-
$year2024 = LocalDateRange::from(
116+
$year2024 = LocalDateRangeClosed::from(
123117
LocalDate::of(2024, 1, 1),
124118
LocalDate::of(2024, 12, 31),
125-
RangeType::CLOSED,
126119
);
127120

128121
echo "2024年: {$year2024->toISOString()}\n";

src/DateTime/LocalDateRange.php

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
*/
2424
readonly 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
*
@@ -32,7 +40,6 @@
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
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WizDevelop\PhpValueObject\DateTime\LocalDateRange;
6+
7+
use Override;
8+
use WizDevelop\PhpValueObject\DateTime\LocalDate;
9+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange;
10+
use WizDevelop\PhpValueObject\DateTime\RangeType;
11+
12+
/**
13+
* @extends LocalDateRange<LocalDate, LocalDate>
14+
*/
15+
final readonly class LocalDateRangeClosed extends LocalDateRange
16+
{
17+
#[Override]
18+
public static function rangeType(): RangeType
19+
{
20+
return RangeType::CLOSED;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WizDevelop\PhpValueObject\DateTime\LocalDateRange;
6+
7+
use Override;
8+
use WizDevelop\PhpValueObject\DateTime\LocalDate;
9+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange;
10+
use WizDevelop\PhpValueObject\DateTime\RangeType;
11+
12+
/**
13+
* @extends LocalDateRange<LocalDate, LocalDate>
14+
*/
15+
final readonly class LocalDateRangeHalfOpenLeft extends LocalDateRange
16+
{
17+
#[Override]
18+
public static function rangeType(): RangeType
19+
{
20+
return RangeType::HALF_OPEN_LEFT;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WizDevelop\PhpValueObject\DateTime\LocalDateRange;
6+
7+
use Override;
8+
use WizDevelop\PhpValueObject\DateTime\LocalDate;
9+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange;
10+
use WizDevelop\PhpValueObject\DateTime\RangeType;
11+
12+
/**
13+
* @extends LocalDateRange<LocalDate, LocalDate>
14+
*/
15+
final readonly class LocalDateRangeHalfOpenRight extends LocalDateRange
16+
{
17+
#[Override]
18+
public static function rangeType(): RangeType
19+
{
20+
return RangeType::HALF_OPEN_RIGHT;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WizDevelop\PhpValueObject\DateTime\LocalDateRange;
6+
7+
use Override;
8+
use WizDevelop\PhpValueObject\DateTime\LocalDate;
9+
use WizDevelop\PhpValueObject\DateTime\LocalDateRange;
10+
use WizDevelop\PhpValueObject\DateTime\RangeType;
11+
12+
/**
13+
* @extends LocalDateRange<LocalDate, LocalDate>
14+
*/
15+
final readonly class LocalDateRangeOpen extends LocalDateRange
16+
{
17+
#[Override]
18+
public static function rangeType(): RangeType
19+
{
20+
return RangeType::OPEN;
21+
}
22+
}

0 commit comments

Comments
 (0)