-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Christian Kolb
committed
May 6, 2024
1 parent
6cc09ab
commit a28e56c
Showing
4 changed files
with
513 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DigitalCraftsman\DateTimePrecision; | ||
|
||
enum CalendarMonth: string | ||
{ | ||
case JANUARY = 'JANUARY'; | ||
case FEBRUARY = 'FEBRUARY'; | ||
case MARCH = 'MARCH'; | ||
case APRIL = 'APRIL'; | ||
case MAY = 'MAY'; | ||
case JUNE = 'JUNE'; | ||
case JULY = 'JULY'; | ||
case AUGUST = 'AUGUST'; | ||
case SEPTEMBER = 'SEPTEMBER'; | ||
case OCTOBER = 'OCTOBER'; | ||
case NOVEMBER = 'NOVEMBER'; | ||
case DECEMBER = 'DECEMBER'; | ||
|
||
// -- Construction | ||
|
||
public static function fromDateTime(\DateTimeImmutable $dateTime): self | ||
{ | ||
return self::fromMonthNumber((int) $dateTime->format('n')); | ||
} | ||
|
||
public static function fromDate(Date $date): self | ||
{ | ||
return self::fromMonthNumber((int) $date->format('n')); | ||
} | ||
|
||
public static function fromMonthNumber(int $dayOfIsoWeek): self | ||
{ | ||
return match ($dayOfIsoWeek) { | ||
1 => self::JANUARY, | ||
2 => self::FEBRUARY, | ||
3 => self::MARCH, | ||
4 => self::APRIL, | ||
5 => self::MAY, | ||
6 => self::JUNE, | ||
7 => self::JULY, | ||
8 => self::AUGUST, | ||
9 => self::SEPTEMBER, | ||
10 => self::OCTOBER, | ||
11 => self::NOVEMBER, | ||
12 => self::DECEMBER, | ||
}; | ||
} | ||
|
||
// -- Accessors | ||
|
||
public function numberOfMonth(): int | ||
{ | ||
return match ($this) { | ||
self::JANUARY => 1, | ||
self::FEBRUARY => 2, | ||
self::MARCH => 3, | ||
self::APRIL => 4, | ||
self::MAY => 5, | ||
self::JUNE => 6, | ||
self::JULY => 7, | ||
self::AUGUST => 8, | ||
self::SEPTEMBER => 9, | ||
self::OCTOBER => 10, | ||
self::NOVEMBER => 11, | ||
self::DECEMBER => 12, | ||
}; | ||
} | ||
|
||
public function isEqualTo(self $calendarMonth): bool | ||
{ | ||
return $this === $calendarMonth; | ||
} | ||
|
||
public function isNotEqualTo(self $calendarMonth): bool | ||
{ | ||
return $this !== $calendarMonth; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace DigitalCraftsman\DateTimePrecision; | ||
|
||
final readonly class CalendarWeek | ||
{ | ||
// -- Construction | ||
|
||
public function __construct( | ||
public int $week, | ||
) { | ||
if ($week < 1 | ||
|| $week > 53 | ||
) { | ||
// TODO: Better exception | ||
throw new \InvalidArgumentException(sprintf('Value "%d" is not a valid week.', $week)); | ||
} | ||
} | ||
|
||
public static function fromDateTime(\DateTimeImmutable $dateTime): self | ||
{ | ||
return new self( | ||
(int) $dateTime->format('W'), | ||
); | ||
} | ||
|
||
// -- Accessors | ||
|
||
public function isEqualTo(self $month): bool | ||
{ | ||
return $this->toDateTimeImmutable() == $month->toDateTimeImmutable(); | ||
} | ||
|
||
public function isNotEqualTo(self $month): bool | ||
{ | ||
return $this->toDateTimeImmutable() != $month->toDateTimeImmutable(); | ||
} | ||
|
||
public function isBefore(self $month): bool | ||
{ | ||
return $this->toDateTimeImmutable() < $month->toDateTimeImmutable(); | ||
} | ||
|
||
public function isNotBefore(self $month): bool | ||
{ | ||
return !($this->toDateTimeImmutable() < $month->toDateTimeImmutable()); | ||
} | ||
|
||
public function isBeforeOrEqualTo(self $month): bool | ||
{ | ||
return $this->toDateTimeImmutable() <= $month->toDateTimeImmutable(); | ||
} | ||
|
||
public function isNotBeforeOrEqualTo(self $month): bool | ||
{ | ||
return !($this->toDateTimeImmutable() <= $month->toDateTimeImmutable()); | ||
} | ||
|
||
public function isAfter(self $month): bool | ||
{ | ||
return $this->toDateTimeImmutable() > $month->toDateTimeImmutable(); | ||
} | ||
|
||
public function isNotAfter(self $month): bool | ||
{ | ||
return !($this->toDateTimeImmutable() > $month->toDateTimeImmutable()); | ||
} | ||
|
||
public function isAfterOrEqualTo(self $month): bool | ||
{ | ||
return $this->toDateTimeImmutable() >= $month->toDateTimeImmutable(); | ||
} | ||
|
||
public function isNotAfterOrEqualTo(self $month): bool | ||
{ | ||
return !($this->toDateTimeImmutable() >= $month->toDateTimeImmutable()); | ||
} | ||
|
||
public function compareTo(self $month): int | ||
{ | ||
return $this->toDateTimeImmutable() <=> $month->toDateTimeImmutable(); | ||
} | ||
|
||
/** | ||
* Returns all months until the given month. If the given month is before this month, the result will be an empty array. | ||
* | ||
* @return array<int, Month> | ||
*/ | ||
public function monthsUntil( | ||
self $month, | ||
PeriodLimit $periodLimit = PeriodLimit::INCLUDING_START_AND_END, | ||
): array { | ||
$startDateTime = $periodLimit === PeriodLimit::INCLUDING_START_AND_END | ||
|| $periodLimit === PeriodLimit::INCLUDING_START | ||
? $this | ||
->modify('- 1 month') | ||
->toDateTimeImmutable() | ||
: $this->toDateTimeImmutable(); | ||
|
||
$endDateTime = $periodLimit === PeriodLimit::INCLUDING_START_AND_END | ||
|| $periodLimit === PeriodLimit::INCLUDING_END | ||
? $month | ||
->modify('+ 1 month') | ||
->toDateTimeImmutable() | ||
: $month->toDateTimeImmutable(); | ||
|
||
$interval = new \DateInterval('P1M'); | ||
/** | ||
* The options here seem counter-intuitive, but are set in a way that this logic is only handled in one place (above) instead of | ||
* two place with part of it above and part below. | ||
*/ | ||
$period = new \DatePeriod($startDateTime, $interval, $endDateTime, \DatePeriod::EXCLUDE_START_DATE); | ||
|
||
$months = []; | ||
foreach ($period as $dateTime) { | ||
$months[] = self::fromDateTime($dateTime); | ||
} | ||
|
||
return $months; | ||
} | ||
|
||
// -- Mutations | ||
|
||
public function firstDay(): Date | ||
{ | ||
$firstDayOfMonth = new \DateTimeImmutable(sprintf( | ||
'first day of %d-%d', | ||
$this->year->year, | ||
$this->week, | ||
)); | ||
|
||
return Date::fromDateTime($firstDayOfMonth); | ||
} | ||
|
||
public function lastDay(): Date | ||
{ | ||
$lastDayOfMonth = new \DateTimeImmutable(sprintf( | ||
'last day of %d-%d', | ||
$this->year->year, | ||
$this->week, | ||
)); | ||
|
||
return Date::fromDateTime($lastDayOfMonth); | ||
} | ||
|
||
public function format(string $format): string | ||
{ | ||
return $this | ||
->toDateTimeImmutable() | ||
->format($format); | ||
} | ||
|
||
public function modify(string $modifier): self | ||
{ | ||
$modifiedDateTime = $this->toDateTimeImmutable() | ||
->modify($modifier); | ||
|
||
/** @psalm-suppress PossiblyFalseArgument */ | ||
return self::fromDateTime($modifiedDateTime); | ||
} | ||
|
||
public function toMomentInTimeZone(\DateTimeZone $timeZone): Moment | ||
{ | ||
return Moment::fromStringInTimeZone( | ||
sprintf( | ||
'%d-%d-01 00:00:00', | ||
$this->year->year, | ||
$this->week, | ||
), | ||
$timeZone, | ||
); | ||
} | ||
|
||
public function modifyInTimeZone(string $modify, \DateTimeZone $timeZone): self | ||
{ | ||
$dateTimeImmutable = new \DateTimeImmutable( | ||
sprintf( | ||
'%d-%d-01 00:00:00', | ||
$this->year->year, | ||
$this->week, | ||
), | ||
$timeZone, | ||
); | ||
|
||
/** @psalm-suppress PossiblyFalseArgument */ | ||
return self::fromDateTime($dateTimeImmutable->modify($modify)); | ||
} | ||
|
||
private function toDateTimeImmutable(): \DateTimeImmutable | ||
{ | ||
/** @psalm-suppress FalsableReturnStatement */ | ||
return \DateTimeImmutable::createFromFormat( | ||
'W', | ||
(string) $this->week, | ||
); | ||
} | ||
} |
Oops, something went wrong.