Skip to content

Commit 2103cf9

Browse files
committed
feat(datetime): support tempest datetime in validator and mapper
1 parent 05d9942 commit 2103cf9

33 files changed

+961
-192
lines changed

packages/clock/src/MockClock.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ final class MockClock implements Clock
1818
public function __construct(DateTimeImmutable|DateTimeInterface|string $now = 'now')
1919
{
2020
if ($now instanceof DateTimeImmutable) {
21-
$this->now = DateTime::fromTimestamp(
22-
Timestamp::fromParts($now->getTimestamp()),
23-
);
21+
$this->now = DateTime::fromTimestamp($now->getTimestamp());
2422
} else {
2523
$this->now = DateTime::parse($now);
2624
}

packages/datetime/src/DateTime.php

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Tempest\DateTime;
66

7+
use DateTimeInterface as NativeDateTimeInterface;
78
use IntlCalendar;
89
use Tempest\Clock\Clock;
910
use Tempest\Container\GenericContainer;
@@ -142,15 +143,15 @@ public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $
142143
}
143144

144145
/**
145-
* Creates a {@see DateTime} instance from individual date and time components.
146+
* Creates a {@see Tempest\DateTime\DateTime} instance from individual date and time components.
146147
*
147148
* This method constructs a DateTime object using the specified year, month, day, hour, minute, second,
148149
* and nanosecond components within a given timezone. It validates each component against the Gregorian calendar
149150
* to ensure the date and time are possible. For example, it checks for the correct range of months (1-12),
150151
* days in a month (considering leap years), hours (0-23), minutes (0-59), and seconds (0-59).
151152
*
152153
* Note: In cases where the specified time occurs twice (such as during the end of daylight saving time), the earlier occurrence
153-
* is returned. To obtain the later occurrence, you can adjust the returned instance using `->plusHours(1)`.
154+
* is returned. To obtain the later occurrence, you can adjust the returned instance using `->plusHour()`.
154155
*
155156
* @param Month|int<1, 12> $month
156157
* @param int<1, 31> $day
@@ -208,19 +209,22 @@ public static function fromParts(Timezone $timezone, int $year, Month|int $month
208209
}
209210

210211
/**
211-
* Creates a {@see DateTime} instance from a timestamp, representing the same point in time.
212-
*
213-
* This method converts a {@see DateTime} into a {@see DateTime} instance calculated for the specified timezone.
212+
* Creates a {@see Tempest\DateTime\DateTime} instance from a timestamp, representing the same point in time.
213+
* If the timestamp is an integer, it must be a seconds-based timestamp.
214214
*
215215
* @param null|Timezone $timezone Optional timezone. If null, uses the system's default timezone.
216216
*
217217
* @see Timezone::default()
218218
*/
219219
#[\Override]
220-
public static function fromTimestamp(Timestamp $timestamp, ?Timezone $timezone = null): static
220+
public static function fromTimestamp(int|Timestamp $timestamp, ?Timezone $timezone = null): static
221221
{
222222
$timezone ??= Timezone::default();
223223

224+
if (is_int($timestamp)) {
225+
$timestamp = Timestamp::fromParts($timestamp);
226+
}
227+
224228
/** @var IntlCalendar $calendar */
225229
$calendar = IntlCalendar::createInstance(namespace\to_intl_timezone($timezone));
226230

@@ -238,43 +242,55 @@ public static function fromTimestamp(Timestamp $timestamp, ?Timezone $timezone =
238242
}
239243

240244
/**
241-
* Parses a date and time string into an instance of {@see DateTime}. This method accepts the same parameters as the {@see \DateTime} constructor.
245+
* Parses the given value into an instance of {@see Tempest\DateTime\DateTime}. The value may be an integer, second-based timestamp, a native datetime string, a native {@see \DateTimeInterface}, or a {@see Tempest\DateTime\DateTime}.
242246
*
243-
* The {@see fromPattern} or {@see fromString} methods are recommended instead if you already know the format of the date string to parse.
247+
* The {@see fromPattern} methods is recommended instead if you already know the format of the date string to parse.
244248
*
245249
* Example usage:
246250
*
247251
* ```php
248-
* $raw_string = '2023-03-15 12:00:00';
249-
* $parsed_timestamp = DateTime\Timestamp::parse($raw_string);
252+
* $parsed = DateTime\DateTime::parse($input);
250253
* ```
251254
*
252-
* @param TemporalInterface|string $string The date and time string to parse.
255+
* @param NativeDateTimeInterface|TemporalInterface|string|int $string The date and time to parse.
253256
* @param null|Timezone $timezone Optional timezone for parsing. If null, uses the system's default timezone.
254257
*
255258
* @throws Exception\RuntimeException If the parsing process fails.
256259
*
257-
* @return static Returns an instance of {@see DateTime} representing the parsed date and time.
260+
* @return static Returns an instance of {@see Tempest\DateTime\DateTime} representing the parsed date and time.
258261
*/
259-
public static function parse(TemporalInterface|string $string, ?Timezone $timezone = null): static
262+
public static function parse(NativeDateTimeInterface|TemporalInterface|string|int $string, ?Timezone $timezone = null): static
260263
{
264+
if (is_int($string)) {
265+
return self::fromTimestamp($string);
266+
}
267+
268+
if (is_string($string) && trim($string) === 'now') {
269+
return self::now($timezone);
270+
}
271+
272+
if ($string instanceof NativeDateTimeInterface) {
273+
return self::fromTimestamp(
274+
timestamp: Timestamp::fromParts($string->getTimestamp()),
275+
timezone: $timezone ?? Timezone::tryFrom($string->getTimezone()?->getName() ?? ''),
276+
);
277+
}
278+
261279
if ($string instanceof TemporalInterface) {
262280
return self::fromTimestamp($string->getTimestamp());
263281
}
264282

265283
return self::fromTimestamp(
266-
Timestamp::fromParts(new \DateTime($string)->getTimestamp()),
267-
$timezone,
284+
timestamp: Timestamp::fromParts(new \DateTime($string)->getTimestamp()),
285+
timezone: $timezone,
268286
);
269287
}
270288

271289
/**
272-
* Parses a date and time string into an instance of {@see DateTime} using a specific format pattern, with optional customization for timezone and locale.
273-
*
274-
* This method is specifically designed for cases where a custom format pattern is used to parse the input string.
290+
* Parses a date and time string into an instance of {@see Tempest\DateTime\DateTime} using a specific format pattern, with optional customization for timezone and locale.
275291
*
292+
* This method is specifically designed for cases where a custom format pattern is used to parse the input string.
276293
* It allows for precise control over the parsing process by specifying the exact format pattern that matches the input string.
277-
*
278294
* Additionally, the method supports specifying a timezone and locale for parsing, enabling accurate interpretation of locale-specific formats.
279295
*
280296
* Example usage:
@@ -304,13 +320,10 @@ public static function fromPattern(string $string, FormatPattern|string $pattern
304320
}
305321

306322
/**
307-
* Creates an instance of {@see DateTime} from a date and time string, formatted according to specified styles for date and time,
308-
* with optional customization for timezone and locale.
309-
*
310-
* This method provides a more abstracted approach to parsing, allowing users to specify styles rather than a custom pattern.
323+
* Creates an instance of {@see Tempest\DateTime\DateTime} from a date and time string, formatted according to specified styles for date and time, with optional customization for timezone and locale.
311324
*
325+
* This method provides a more abstracted approach to parsing, allowing users to specify styles rather than a custom pattern.
312326
* This is particularly useful for parsing strings that follow common date and time formats.
313-
*
314327
* Additionally, the timezone and locale parameters enable accurate parsing of strings in locale-specific formats.
315328
*
316329
* Example usage:

packages/datetime/src/DateTimeConvenienceMethods.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,17 @@ public function isLeapYear(): bool
333333
return namespace\is_leap_year($this->getYear());
334334
}
335335

336+
/**
337+
* Adds a year to this date-time object, returning a new instance with the added year.
338+
*
339+
* @throws Exception\UnderflowException If adding the years results in an arithmetic underflow.
340+
* @throws Exception\OverflowException If adding the years results in an arithmetic overflow.
341+
*/
342+
public function plusYear(): static
343+
{
344+
return $this->plusYears(1);
345+
}
346+
336347
/**
337348
* Adds the specified years to this date-time object, returning a new instance with the added years.
338349
*
@@ -343,6 +354,17 @@ public function plusYears(int $years): static
343354
return $this->plusMonths($years * MONTHS_PER_YEAR);
344355
}
345356

357+
/**
358+
* Subtracts a year from this date-time object, returning a new instance with the subtracted year.
359+
*
360+
* @throws Exception\UnderflowException If subtracting the years results in an arithmetic underflow.
361+
* @throws Exception\OverflowException If subtracting the years results in an arithmetic overflow.
362+
*/
363+
public function minusYear(): static
364+
{
365+
return $this->minusYears(1);
366+
}
367+
346368
/**
347369
* Subtracts the specified years from this date-time object, returning a new instance with the subtracted years.
348370
*
@@ -353,6 +375,17 @@ public function minusYears(int $years): static
353375
return $this->minusMonths($years * MONTHS_PER_YEAR);
354376
}
355377

378+
/**
379+
* Adds a month to this date-time object, returning a new instance with the added month.
380+
*
381+
* @throws Exception\UnderflowException If adding the months results in an arithmetic underflow.
382+
* @throws Exception\OverflowException If adding the months results in an arithmetic overflow.
383+
*/
384+
public function plusMonth(): static
385+
{
386+
return $this->plusMonths(1);
387+
}
388+
356389
/**
357390
* Adds the specified months to this date-time object, returning a new instance with the added months.
358391
*
@@ -386,6 +419,17 @@ public function plusMonths(int $months): static
386419
);
387420
}
388421

422+
/**
423+
* Subtracts a month from this date-time object, returning a new instance with the subtracted month.
424+
*
425+
* @throws Exception\UnderflowException If subtracting the months results in an arithmetic underflow.
426+
* @throws Exception\OverflowException If subtracting the months results in an arithmetic overflow.
427+
*/
428+
public function minusMonth(): static
429+
{
430+
return $this->minusMonths(1);
431+
}
432+
389433
/**
390434
* Subtracts the specified months from this date-time object, returning a new instance with the subtracted months.
391435
*
@@ -419,6 +463,17 @@ public function minusMonths(int $months): static
419463
);
420464
}
421465

466+
/**
467+
* Adds a day to this date-time object, returning a new instance with the added day.
468+
*
469+
* @throws Exception\UnderflowException If adding the days results in an arithmetic underflow.
470+
* @throws Exception\OverflowException If adding the days results in an arithmetic overflow.
471+
*/
472+
public function plusDay(): static
473+
{
474+
return $this->plusDays(1);
475+
}
476+
422477
/**
423478
* Adds the specified days to this date-time object, returning a new instance with the added days.
424479
*
@@ -430,6 +485,17 @@ public function plusDays(int $days): static
430485
return $this->plus(Duration::days($days));
431486
}
432487

488+
/**
489+
* Subtracts a day from this date-time object, returning a new instance with the subtracted day.
490+
*
491+
* @throws Exception\UnderflowException If subtracting the days results in an arithmetic underflow.
492+
* @throws Exception\OverflowException If subtracting the days results in an arithmetic overflow.
493+
*/
494+
public function minusDay(): static
495+
{
496+
return $this->minusDays(1);
497+
}
498+
433499
/**
434500
* Subtracts the specified days from this date-time object, returning a new instance with the subtracted days.
435501
*

packages/datetime/src/DateTimeInterface.php

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface DateTimeInterface extends TemporalInterface
1717
*
1818
* @see Timezone::default()
1919
*/
20-
public static function fromTimestamp(Timestamp $timestamp, ?Timezone $timezone = null): static;
20+
public static function fromTimestamp(int|Timestamp $timestamp, ?Timezone $timezone = null): static;
2121

2222
/**
2323
* Checks if this {@see DateTimeInterface} instance is equal to the given {@see DateTimeInterface} instance including the timezone.
@@ -305,6 +305,14 @@ public function getWeekday(): Weekday;
305305
*/
306306
public function isLeapYear(): bool;
307307

308+
/**
309+
* Adds a year to this date-time object, returning a new instance with the added year.
310+
*
311+
* @throws Exception\UnderflowException If adding the years results in an arithmetic underflow.
312+
* @throws Exception\OverflowException If adding the years results in an arithmetic overflow.
313+
*/
314+
public function plusYear(): static;
315+
308316
/**
309317
* Adds the specified years to this date-time object, returning a new instance with the added years.
310318
*
@@ -313,6 +321,14 @@ public function isLeapYear(): bool;
313321
*/
314322
public function plusYears(int $years): static;
315323

324+
/**
325+
* Adds a month to this date-time object, returning a new instance with the added month.
326+
*
327+
* @throws Exception\UnderflowException If adding the months results in an arithmetic underflow.
328+
* @throws Exception\OverflowException If adding the months results in an arithmetic overflow.
329+
*/
330+
public function plusMonth(): static;
331+
316332
/**
317333
* Adds the specified months to this date-time object, returning a new instance with the added months.
318334
*
@@ -321,6 +337,14 @@ public function plusYears(int $years): static;
321337
*/
322338
public function plusMonths(int $months): static;
323339

340+
/**
341+
* Adds a day to this date-time object, returning a new instance with the added day.
342+
*
343+
* @throws Exception\UnderflowException If adding the days results in an arithmetic underflow.
344+
* @throws Exception\OverflowException If adding the days results in an arithmetic overflow.
345+
*/
346+
public function plusDay(): static;
347+
324348
/**
325349
* Adds the specified days to this date-time object, returning a new instance with the added days.
326350
*
@@ -329,6 +353,14 @@ public function plusMonths(int $months): static;
329353
*/
330354
public function plusDays(int $days): static;
331355

356+
/**
357+
* Subtracts a year from this date-time object, returning a new instance with the subtracted year.
358+
*
359+
* @throws Exception\UnderflowException If subtracting the years results in an arithmetic underflow.
360+
* @throws Exception\OverflowException If subtracting the years results in an arithmetic overflow.
361+
*/
362+
public function minusYear(): static;
363+
332364
/**
333365
* Subtracts the specified years from this date-time object, returning a new instance with the subtracted years.
334366
*
@@ -337,6 +369,14 @@ public function plusDays(int $days): static;
337369
*/
338370
public function minusYears(int $years): static;
339371

372+
/**
373+
* Subtracts a month from this date-time object, returning a new instance with the subtracted month.
374+
*
375+
* @throws Exception\UnderflowException If subtracting the months results in an arithmetic underflow.
376+
* @throws Exception\OverflowException If subtracting the months results in an arithmetic overflow.
377+
*/
378+
public function minusMonth(): static;
379+
340380
/**
341381
* Subtracts the specified months from this date-time object, returning a new instance with the subtracted months.
342382
*
@@ -345,6 +385,14 @@ public function minusYears(int $years): static;
345385
*/
346386
public function minusMonths(int $months): static;
347387

388+
/**
389+
* Subtracts a day from this date-time object, returning a new instance with the subtracted day.
390+
*
391+
* @throws Exception\UnderflowException If subtracting the days results in an arithmetic underflow.
392+
* @throws Exception\OverflowException If subtracting the days results in an arithmetic overflow.
393+
*/
394+
public function minusDay(): static;
395+
348396
/**
349397
* Subtracts the specified days from this date-time object, returning a new instance with the subtracted days.
350398
*

0 commit comments

Comments
 (0)