1616use WizDevelop \PhpValueObject \ValueObjectMeta ;
1717
1818/**
19+ * @phpstan-type Year int<LocalDate::MIN_YEAR, LocalDate::MAX_YEAR>
20+ * @phpstan-type Month int<1, 12>
21+ * @phpstan-type Day int<1, 31>
22+ *
1923 * ローカル日付を表す値オブジェクト
2024 */
2125#[ValueObjectMeta(name: 'ローカル日付 ' )]
4347
4448 /**
4549 * Avoid new() operator.
46- * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
47- * @param int<1, 12> $month the month, from 1 to 12
48- * @param int<1, 31> $day the day, from 1 to 31
50+ * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
51+ * @param Month $month the month, from 1 to 12
52+ * @param Day $day the day, from 1 to 31
4953 */
5054 final private function __construct (
5155 private int $ year ,
@@ -85,9 +89,9 @@ final public function jsonSerialize(): string
8589 // MARK: factory methods
8690 // -------------------------------------------------------------------------
8791 /**
88- * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
89- * @param int<1, 12> $month the month, from 1 to 12
90- * @param int<1, 31> $day the day, from 1 to 31
92+ * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
93+ * @param Month $month the month, from 1 to 12
94+ * @param Day $day the day, from 1 to 31
9195 */
9296 final public static function of (int $ year , int $ month , int $ day ): static
9397 {
@@ -146,7 +150,7 @@ final public static function tryFromNullable(?DateTimeInterface $value): Result
146150 return static ::tryFrom ($ value )->map (static fn ($ result ) => Option \some ($ result ));
147151 }
148152
149- final public static function now (DateTimeZone $ timeZone ): static
153+ final public static function now (DateTimeZone $ timeZone = new DateTimeZone ( ' Asia/Tokyo ' ) ): static
150154 {
151155 $ value = new DateTimeImmutable ('now ' , $ timeZone );
152156
@@ -155,6 +159,11 @@ final public static function now(DateTimeZone $timeZone): static
155159 return static ::of ($ year , $ month , $ day );
156160 }
157161
162+ final public static function max (): static
163+ {
164+ return static ::of (self ::MAX_YEAR , 12 , 31 );
165+ }
166+
158167 /**
159168 * Obtains an instance of `LocalDate` from the epoch day count.
160169 *
@@ -186,10 +195,10 @@ final public static function ofEpochDay(int $epochDay): static
186195 // Convert march-based values back to January-based.
187196 $ marchMonth0 = intdiv ($ marchDoy0 * 5 + 2 , 153 );
188197
189- /** @var int<1, 12> $month */
198+ /** @var Month $month */
190199 $ month = ($ marchMonth0 + 2 ) % 12 + 1 ;
191200
192- /** @var int<1, 31> $dom */
201+ /** @var Day $dom */
193202 $ dom = $ marchDoy0 - intdiv ($ marchMonth0 * 306 + 5 , 10 ) + 1 ;
194203
195204 $ yearEst += intdiv ($ marchMonth0 , 10 );
@@ -260,16 +269,14 @@ className: static::class,
260269 );
261270 }
262271
263-
264-
265272 return Result \ok (true );
266273 }
267274
268275 /**
269276 * 有効な日かどうかを判定
270277 * @param int $year 年
271- * @param int<1, 12> $monthOfYear 月
272- * @param int<1, 31> $dayOfMonth 日
278+ * @param Month $monthOfYear 月
279+ * @param Day $dayOfMonth 日
273280 * @return Result<bool,ValueObjectError>
274281 */
275282 final protected static function isValidDate (int $ year , int $ monthOfYear , int $ dayOfMonth ): Result
@@ -337,7 +344,7 @@ final public function toISOString(): string
337344 }
338345
339346 /**
340- * @return int<self::MIN_YEAR, self::MAX_YEAR>
347+ * @return Year
341348 */
342349 final public function getYear (): int
343350 {
@@ -346,15 +353,15 @@ final public function getYear(): int
346353 }
347354
348355 /**
349- * @return int<1, 12>
356+ * @return Month
350357 */
351358 final public function getMonth (): int
352359 {
353360 return $ this ->month ;
354361 }
355362
356363 /**
357- * @return int<1, 31>
364+ * @return Day
358365 */
359366 final public function getDay (): int
360367 {
@@ -409,6 +416,11 @@ final public function compareTo(self $that): int
409416 return 0 ;
410417 }
411418
419+ final public function is (self $ that ): bool
420+ {
421+ return $ this ->compareTo ($ that ) === 0 ;
422+ }
423+
412424 final public function isBefore (self $ that ): bool
413425 {
414426 return $ this ->compareTo ($ that ) === -1 ;
@@ -463,7 +475,7 @@ final public function addMonths(int $months): static
463475
464476 $ yearDiff = Math::floorDiv ($ month , 12 );
465477
466- /** @var int<1, 12> $month */
478+ /** @var Month $month */
467479 $ month = Math::floorMod ($ month , 12 ) + 1 ;
468480
469481 $ year = $ this ->year + $ yearDiff ;
@@ -591,17 +603,17 @@ final public function toEpochDay(): int
591603 // MARK: private methods
592604 // -------------------------------------------------------------------------
593605 /**
594- * @return array{0:int<self::MIN_YEAR, self::MAX_YEAR>, 1:int<1, 12>, 2:int<1, 31> }
606+ * @return array{0:Year, 1:Month, 2:Day }
595607 */
596608 private static function extractDate (DateTimeInterface $ value ): array
597609 {
598- /** @var int<self::MIN_YEAR, self::MAX_YEAR> */
610+ /** @var Year */
599611 $ year = (int )$ value ->format ('Y ' );
600612
601- /** @var int<1, 12> */
613+ /** @var Month */
602614 $ month = (int )$ value ->format ('n ' );
603615
604- /** @var int<1, 31> */
616+ /** @var Day */
605617 $ day = (int )$ value ->format ('j ' );
606618
607619 return [$ year , $ month , $ day ];
@@ -610,9 +622,9 @@ private static function extractDate(DateTimeInterface $value): array
610622 /**
611623 * Resolves the date, resolving days past the end of month.
612624 *
613- * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
614- * @param int<1, 12> $month the month-of-year to represent
615- * @param int<1, 31> $day the day-of-month to represent, validated from 1 to 31
625+ * @param int $year the year to represent, validated from MIN_YEAR to MAX_YEAR
626+ * @param Month $month the month-of-year to represent
627+ * @param Day $day the day-of-month to represent, validated from 1 to 31
616628 */
617629 private static function resolvePreviousValid (int $ year , int $ month , int $ day ): static
618630 {
@@ -632,7 +644,7 @@ private static function isLeapYear(int $year): bool
632644 }
633645
634646 /**
635- * @param int<1, 12> $month
647+ * @param Month $month
636648 * @return int<28, 31>
637649 */
638650 private static function lengthOfMonth (int $ year , int $ month ): int
@@ -646,8 +658,8 @@ private static function lengthOfMonth(int $year, int $month): int
646658
647659 /**
648660 * Returns whether this date is the last day of the month.
649- * @param int<1, 12> $month
650- * @param int<1, 31> $day
661+ * @param Month $month
662+ * @param Day $day
651663 */
652664 private static function isEndOfMonth (int $ year , int $ month , int $ day ): bool
653665 {
0 commit comments