Skip to content

Commit e3b8dae

Browse files
committed
feat: add more start/end convenience methods
1 parent 784066a commit e3b8dae

File tree

3 files changed

+120
-5
lines changed

3 files changed

+120
-5
lines changed

packages/datetime/src/DateTime.php

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use IntlCalendar;
99
use Tempest\Clock\Clock;
1010
use Tempest\Container\GenericContainer;
11-
use Tempest\DateTime\Exception\UnexpectedValueException;
1211
use Tempest\Support\Language\Locale;
1312

1413
/**
@@ -520,7 +519,55 @@ public function startOfDay(): static
520519
*/
521520
public function endOfDay(): static
522521
{
523-
return $this->withTime(23, 59, 59, 999999999);
522+
return $this->withTime(23, 59, 59, 999_999_999);
523+
}
524+
525+
/**
526+
* Returns a new instance set to the start of the week.
527+
*/
528+
public function startOfWeek(): static
529+
{
530+
return $this->withDay($this->day - ($this->getWeekday()->value - 1))->startOfDay();
531+
}
532+
533+
/**
534+
* Returns a new instance set to the end of the week.
535+
*/
536+
public function endOfWeek(): static
537+
{
538+
return $this->withDay($this->getDay() + (7 - $this->getWeekday()->value))->endOfDay();
539+
}
540+
541+
/**
542+
* Returns a new instance set to the start of the month.
543+
*/
544+
public function startOfMonth(): static
545+
{
546+
return $this->withDay(1)->startOfDay();
547+
}
548+
549+
/**
550+
* Returns a new instance set to the end of the month.
551+
*/
552+
public function endOfMonth(): static
553+
{
554+
return $this->withDay(Month::from($this->month)->getDaysForYear($this->year))->endOfDay();
555+
}
556+
557+
/**
558+
* Returns a new instance set to the start of the year.
559+
*/
560+
public function startOfYear(): static
561+
{
562+
return $this->withDate($this->getYear(), 1, 1)->startOfDay();
563+
}
564+
565+
/**
566+
* Returns a new instance set to the end of the year.
567+
*/
568+
public function endOfYear(): static
569+
{
570+
return $this->withDate($this->getYear(), 12, Month::DECEMBER->getDaysForYear($this->getYear()))->endOfDay();
524571
}
525572

526573
#[\Override]

packages/datetime/src/DateTimeConvenienceMethods.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,9 @@ public function getISOWeekNumber(): array
319319
*/
320320
public function getWeekday(): Weekday
321321
{
322-
return Weekday::from((int) $this->format(
322+
// Settings nanoseconds to 1 to a avoid rounding
323+
// errors with extreme values (eg. 999_999_999)
324+
return Weekday::from((int) $this->withNanoseconds(1)->format(
323325
pattern: 'e',
324326
locale: Locale::ENGLISH_UNITED_KINGDOM,
325327
));

packages/datetime/tests/DateTimeTest.php

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,8 @@ public function test_minus_methods(): void
490490
$this->assertSame(59, $datetime->minusSeconds(1)->getSeconds());
491491
$this->assertSame(59, $datetime->minusSecond()->getSeconds());
492492

493-
$this->assertSame(999999999, $datetime->minusNanoseconds(1)->getNanoseconds());
494-
$this->assertSame(999999999, $datetime->minusNanosecond()->getNanoseconds());
493+
$this->assertSame(999_999_999, $datetime->minusNanoseconds(1)->getNanoseconds());
494+
$this->assertSame(999_999_999, $datetime->minusNanosecond()->getNanoseconds());
495495
}
496496

497497
public function test_minus_months_edge_cases(): void
@@ -633,6 +633,72 @@ public function test_end_of_day(): void
633633
$this->assertSame(999999999, $new->getNanoseconds());
634634
}
635635

636+
public function test_start_of_week(): void
637+
{
638+
$date = DateTime::parse('2025-05-21 12:00');
639+
$new = $date->startOfWeek();
640+
641+
$this->assertSame(5, $new->getMonth());
642+
$this->assertSame(19, $new->getDay());
643+
$this->assertSame(0, $new->getHours());
644+
}
645+
646+
public function test_end_of_week(): void
647+
{
648+
$date = DateTime::parse('2025-05-21 12:00');
649+
$new = $date->endOfWeek();
650+
651+
$this->assertSame(5, $new->getMonth());
652+
$this->assertSame(25, $new->getDay());
653+
$this->assertSame(23, $new->getHours());
654+
$this->assertSame(59, $new->getMinutes());
655+
$this->assertSame(59, $new->getSeconds());
656+
}
657+
658+
public function test_start_of_month(): void
659+
{
660+
$date = DateTime::parse('2025-05-21 12:00');
661+
$new = $date->startOfMonth();
662+
663+
$this->assertSame(5, $new->getMonth());
664+
$this->assertSame(1, $new->getDay());
665+
$this->assertSame(Weekday::THURSDAY, $new->getWeekday());
666+
$this->assertSame(0, $new->getHours());
667+
}
668+
669+
public function test_end_of_month(): void
670+
{
671+
$date = DateTime::parse('2025-05-21 12:00', timezone: Timezone::EUROPE_PARIS);
672+
$new = $date->endOfMonth();
673+
674+
$this->assertSame(5, $new->getMonth());
675+
$this->assertSame(31, $new->getDay());
676+
$this->assertSame(Weekday::SATURDAY, $new->getWeekday());
677+
$this->assertSame(23, $new->getHours());
678+
$this->assertSame(59, $new->getMinutes());
679+
$this->assertSame(59, $new->getSeconds());
680+
}
681+
682+
public function test_end_of_month_edge_cases(): void
683+
{
684+
$date = DateTime::parse('2025-02-21 12:00');
685+
$this->assertSame(2, $date->endOfMonth()->getMonth());
686+
$this->assertSame(28, $date->endOfMonth()->getDay());
687+
$this->assertSame(Weekday::FRIDAY, $date->endOfMonth()->getWeekday());
688+
689+
$date = DateTime::parse('2024-02-21 12:00');
690+
$this->assertSame(2, $date->endOfMonth()->getMonth());
691+
$this->assertSame(29, $date->endOfMonth()->getDay());
692+
$this->assertSame(Weekday::THURSDAY, $date->endOfMonth()->getWeekday());
693+
694+
$date = DateTime::parse('2024-12-31 12:00');
695+
$this->assertSame(12, $date->endOfMonth()->getMonth());
696+
$this->assertSame(31, $date->endOfMonth()->getDay());
697+
$this->assertSame(23, $date->endOfMonth()->getHours());
698+
$this->assertSame(59, $date->endOfMonth()->getMinutes());
699+
$this->assertSame(59, $date->endOfMonth()->getSeconds());
700+
}
701+
636702
public function test_timezone_info(): void
637703
{
638704
$timeZone = Timezone::EUROPE_BRUSSELS;

0 commit comments

Comments
 (0)