Skip to content

Commit 22b034c

Browse files
committed
Add more parser options like tomorrow, etc.
1 parent beb7df8 commit 22b034c

File tree

3 files changed

+407
-86
lines changed

3 files changed

+407
-86
lines changed

README.md

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# PHP Date Parser
22

33
This library parses different strings to DateTime or DateTimeImmutable classes.
4+
Can be used e.g. excellently for command line arguments and options.
45

56
## Installation
67

@@ -44,22 +45,63 @@ $dateParser = (new DateParser('today'))->formatTo('Y-m-d H:i:s');
4445
4546
## Parsing formats / overview
4647
47-
* Imagine today would be the `2023-07-07`
48-
49-
| string | description | from | to |
50-
|-----------------------------------------|-------------------------------------------------------|--------------------------------------|--------------------------------------|
51-
| <nobr>`"today"`</nobr> | Returns the date range from today. | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
52-
| <nobr>`"yesterday"`</nobr> | Returns the date range from yesterday. | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
53-
| <nobr>`"2023-07-01"`</nobr> | Exactly the given date. | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
54-
| <nobr>`"<2023-07-01"`</nobr> | Lower than the given date (excluding the given one). | <nobr>`NULL`</nobr> | <nobr>`"2023-06-30 23:59:59"`</nobr> |
55-
| <nobr>`"<+2023-07-01"`</nobr> | Lower than the given date (including the given one). | <nobr>`NULL`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
56-
| <nobr>`">2023-07-01"`</nobr> | Higher than the given date (excluding the given one). | <nobr>`"2023-07-02 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
57-
| <nobr>`">+2023-07-01"`</nobr> | Higher than the given date (including the given one). | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
58-
| <nobr>`"+2023-07-01"`</nobr> | Alias of `">+2023-07-01"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
59-
| <nobr>`"2023-07-01\|2023-07-03"`</nobr> | Date from `"2023-07-01"` to `"2023-07-03"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-03 23:59:59"`</nobr> |
60-
| <nobr>`"2023-07-01\|today"`</nobr> | Date from `"2023-07-01"` to `"today"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
61-
| <nobr>`"2023-07-01\|yesterday"`</nobr> | Date from `"2023-07-01"` to `"yesterday"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
62-
| <nobr>`"yesterday\|today"`</nobr> | Date from `"yesterday"` to `"today"` | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
48+
* Imagine today would be the: `2023-07-07`
49+
50+
| Given format | description | from | to |
51+
|-----------------------------------------|--------------------------------------------------|--------------------------------------|--------------------------------------|
52+
| <nobr>`"tomorrow"`</nobr> | Returns the date range from tomorrow. | <nobr>`"2023-07-08 00:00:00"`</nobr> | <nobr>`"2023-07-08 23:59:59"`</nobr> |
53+
| <nobr>`"=tomorrow"`</nobr> | Alias of `"tomorrow"`. | <nobr>`"2023-07-08 00:00:00"`</nobr> | <nobr>`"2023-07-08 23:59:59"`</nobr> |
54+
| <nobr>`"today"`</nobr> | Returns the date range from today. | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
55+
| <nobr>`"=today"`</nobr> | Alias of `"today"`. | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
56+
| <nobr>`"yesterday"`</nobr> | Returns the date range from yesterday. | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
57+
| <nobr>`"=yesterday"`</nobr> | Alias of `"yesterday"` | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
58+
| <nobr>`"2023-07-01"`</nobr> | Exactly the given date. | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
59+
| <nobr>`"=2023-07-01"`</nobr> | Alias of `"2023-07-01"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
60+
| - | - | - | - |
61+
| <nobr>`">tomorrow"`</nobr> | Higher than tomorrow<sup>1)</sup> | <nobr>`"2023-07-09 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
62+
| <nobr>`">=tomorrow"`</nobr> | Higher than tomorrow<sup>2)</sup> | <nobr>`"2023-07-08 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
63+
| <nobr>`">+tomorrow"`</nobr> | Alias of `">=tomorrow"` | <nobr>`"2023-07-08 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
64+
| <nobr>`"+tomorrow"`</nobr> | Alias of `">=tomorrow"` | <nobr>`"2023-07-08 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
65+
| <nobr>`">today"`</nobr> | Higher than today<sup>1)</sup> | <nobr>`"2023-07-08 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
66+
| <nobr>`">=today"`</nobr> | Higher than today<sup>2)</sup> | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
67+
| <nobr>`">+today"`</nobr> | Alias of `">=today"` | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
68+
| <nobr>`"+today"`</nobr> | Alias of `">=today"` | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
69+
| <nobr>`">yesterday"`</nobr> | Higher than yesterday<sup>1)</sup> | <nobr>`"2023-07-07 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
70+
| <nobr>`">=yesterday"`</nobr> | Higher than yesterday<sup>2)</sup> | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
71+
| <nobr>`">+yesterday"`</nobr> | Alias of `">=yesterday"` | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
72+
| <nobr>`"+yesterday"`</nobr> | Alias of `">=yesterday"` | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
73+
| <nobr>`">2023-07-01"`</nobr> | Higher than the given date<sup>1)</sup> | <nobr>`"2023-07-02 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
74+
| <nobr>`">=2023-07-01"`</nobr> | Higher than the given date<sup>2)</sup> | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
75+
| <nobr>`">+2023-07-01"`</nobr> | Alias of `">=2023-07-01"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
76+
| <nobr>`"+2023-07-01"`</nobr> | Alias of `">=2023-07-01"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`NULL`</nobr> |
77+
| - | - | - | - |
78+
| <nobr>`"<tomorrow"`</nobr> | Lower than tomorrow<sup>1)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
79+
| <nobr>`"<=tomorrow"`</nobr> | Lower than tomorrow<sup>2)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-08 23:59:59"`</nobr> |
80+
| <nobr>`"<+tomorrow"`</nobr> | Alias of `"<=tomorrow"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-08 23:59:59"`</nobr> |
81+
| <nobr>`"-tomorrow"`</nobr> | Alias of `"<=tomorrow"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-08 23:59:59"`</nobr> |
82+
| <nobr>`"<today"`</nobr> | Lower than today<sup>1)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
83+
| <nobr>`"<=today"`</nobr> | Lower than today<sup>2)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
84+
| <nobr>`"<+today"`</nobr> | Alias of `"<=today"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
85+
| <nobr>`"-today"`</nobr> | Alias of `"<=today"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
86+
| <nobr>`"<yesterday"`</nobr> | Lower than yesterday<sup>1)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-05 23:59:59"`</nobr> |
87+
| <nobr>`"<=yesterday"`</nobr> | Lower than yesterday<sup>2)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
88+
| <nobr>`"<+yesterday"`</nobr> | Alias of `"<=yesterday"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
89+
| <nobr>`"-yesterday"`</nobr> | Alias of `"<=yesterday"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
90+
| <nobr>`"<2023-07-01"`</nobr> | Lower than the given date<sup>1)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-06-30 23:59:59"`</nobr> |
91+
| <nobr>`"<=2023-07-01"`</nobr> | Lower than the given date<sup>2)</sup> | <nobr>`NULL`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
92+
| <nobr>`"<+2023-07-01"`</nobr> | Alias of `"<=2023-07-01"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
93+
| <nobr>`"-2023-07-01"`</nobr> | Alias of `"<=2023-07-01"` | <nobr>`NULL`</nobr> | <nobr>`"2023-07-01 23:59:59"`</nobr> |
94+
| - | - | - | - |
95+
| <nobr>`"2023-07-01\|2023-07-03"`</nobr> | Date range from `"2023-07-01"` to `"2023-07-03"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-03 23:59:59"`</nobr> |
96+
| <nobr>`"2023-07-01\|tomorrow"`</nobr> | Date range from `"2023-07-01"` to `"tomorrow"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-08 23:59:59"`</nobr> |
97+
| <nobr>`"2023-07-01\|today"`</nobr> | Date range from `"2023-07-01"` to `"today"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
98+
| <nobr>`"2023-07-01\|yesterday"`</nobr> | Date range from `"2023-07-01"` to `"yesterday"` | <nobr>`"2023-07-01 00:00:00"`</nobr> | <nobr>`"2023-07-06 23:59:59"`</nobr> |
99+
| <nobr>`"yesterday\|today"`</nobr> | Date range from `"yesterday"` to `"today"` | <nobr>`"2023-07-06 00:00:00"`</nobr> | <nobr>`"2023-07-07 23:59:59"`</nobr> |
100+
| - | - | - | - |
101+
| <nobr>`NULL`</nobr> | No range given (infinitive range). | <nobr>`NULL`</nobr> | <nobr>`NULL`</nobr> |
102+
103+
* <sup>1)</sup> - excluding the given one
104+
* <sup>2)</sup> - including the given one
63105
64106
## Methods
65107

src/DateParser.php

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use DateTime;
1717
use DateTimeImmutable;
1818
use Ixnode\PhpDateParser\Tests\Unit\DateParserTest;
19+
use Ixnode\PhpException\Parser\ParserException;
1920
use Ixnode\PhpException\Type\TypeInvalidException;
2021

2122
/**
@@ -44,56 +45,81 @@ class DateParser
4445

4546
final public const FORMAT_DATE = 'Y-m-d';
4647

48+
final public const VALUE_TOMORROW = 'tomorrow';
49+
4750
final public const VALUE_TODAY = 'today';
4851

4952
final public const VALUE_YESTERDAY = 'yesterday';
5053

5154
final public const SECONDS_A_DAY = 86400;
5255

56+
protected string|null $range;
57+
5358
private DateRange $dateRange;
5459

5560
/**
56-
* @param string $range
61+
* @param string|null $range
5762
* @throws TypeInvalidException
63+
* @throws ParserException
5864
*/
59-
public function __construct(protected string $range)
65+
public function __construct(string|null $range)
6066
{
67+
$this->range = !is_null($range) ? trim(strtolower($range)) : null;
68+
6169
$this->dateRange = $this->parseRange($range);
6270
}
6371

6472
/**
6573
* Parses the given date range.
6674
*
67-
* @param string $range
75+
* @param string|null $range
6876
* @return DateRange
6977
* @throws TypeInvalidException
78+
* @throws ParserException
79+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
7080
*/
71-
private function parseRange(string $range): DateRange
81+
private function parseRange(string|null $range): DateRange
7282
{
73-
$range = trim(strtolower($range));
83+
if (is_null($range)) {
84+
return new DateRange(
85+
null,
86+
null
87+
);
88+
}
89+
90+
$matches = [];
7491

7592
switch (true) {
93+
94+
/* Parses "tomorrow" date. */
95+
case $range === self::VALUE_TOMORROW:
96+
return new DateRange(
97+
(new DateTime(self::VALUE_TOMORROW))->setTime(self::HOUR_FIRST, self::MINUTE_FIRST, self::SECOND_FIRST),
98+
(new DateTime(self::VALUE_TOMORROW))->setTime(self::HOUR_LAST, self::MINUTE_LAST, self::SECOND_LAST)
99+
);
100+
76101
/* Parses "today" date. */
77102
case $range === self::VALUE_TODAY:
78103
return new DateRange(
79104
(new DateTime())->setTime(self::HOUR_FIRST, self::MINUTE_FIRST, self::SECOND_FIRST),
80105
(new DateTime())->setTime(self::HOUR_LAST, self::MINUTE_LAST, self::SECOND_LAST)
81106
);
82107

108+
83109
/* Parses "yesterday" date. */
84110
case $range === self::VALUE_YESTERDAY:
85111
return new DateRange(
86112
(new DateTime(self::VALUE_YESTERDAY))->setTime(self::HOUR_FIRST, self::MINUTE_FIRST, self::SECOND_FIRST),
87113
(new DateTime(self::VALUE_YESTERDAY))->setTime(self::HOUR_LAST, self::MINUTE_LAST, self::SECOND_LAST)
88114
);
89115

116+
90117
/* Starts with <+: parses a "∞ (infinity)" to given "from" date (including given date). */
91-
case preg_match('~^<\+~', $range) === 1:
118+
case preg_match('~^(<[+=]|-)~', $range, $matches) === 1:
92119
return new DateRange(
93120
null,
94-
$this->parseRange(substr($range, 2))->getTo()
121+
$this->parseRange(substr($range, strlen($matches[1])))->getTo()
95122
);
96-
97123
/* Starts with <: parses a "∞ (infinity)" to given "from" date (excluding given date). */
98124
case str_starts_with($range, '<'):
99125
return new DateRange(
@@ -103,36 +129,39 @@ private function parseRange(string $range): DateRange
103129
?->modify(sprintf('-%d seconds', self::SECONDS_A_DAY))
104130
);
105131

106-
/* Starts with >+: parses a given "from" (including given date) to "∞ (infinity)" date. */
107-
case preg_match('~^>\+~', $range) === 1:
108-
return new DateRange(
109-
$this->parseRange(substr($range, 2))->getFrom(),
110-
null
111-
);
112132

113-
/* Starts with +: parses a given "from" (including given date) to "∞ (infinity)" date. */
114-
case preg_match('~^\+~', $range) === 1:
133+
/* Starts with >+: parses a given "from" (including given date) to "∞ (infinity)" date. */
134+
case preg_match('~^(>[+=]|[+])~', $range, $matches) === 1:
115135
return new DateRange(
116-
$this->parseRange(substr($range, 1))->getFrom(),
136+
$this->parseRange(substr($range, strlen($matches[1])))->getFrom(),
117137
null
118138
);
119-
120139
/* Starts with >: parses a given "from" (excluding given date) to "∞ (infinity)" date. */
121140
case str_starts_with($range, '>'):
122141
return new DateRange(
123142
$this->parseRange(substr($range, 1))->getTo()?->modify('+1 second'),
124143
null
125144
);
126145

127-
/* Starts with +: parses a given "from" (including given date) to "∞ (infinity)" date. */
146+
147+
/* Starts with |: parses a given "from" (including given date) to "to" date (including given date). */
128148
case str_contains($range, '|'):
129149
$splitted = explode('|', $range);
130150
return new DateRange(
131151
$this->parseRange($splitted[0])->getFrom(),
132152
$this->parseRange($splitted[1])->getTo()
133153
);
134154

135-
/* Parses a given date (exactly). */
155+
156+
/* Starts with =: a given date exactly. */
157+
case str_starts_with($range, '='):
158+
return new DateRange(
159+
$this->parseRange(substr($range, 1))->getFrom(),
160+
$this->parseRange(substr($range, 1))->getTo(),
161+
);
162+
163+
164+
/* Parse the date */
136165
default:
137166
return new DateRange(
138167
$this->parseDate($range)->setTime(self::HOUR_FIRST, self::MINUTE_FIRST, self::SECOND_FIRST),
@@ -147,9 +176,14 @@ private function parseRange(string $range): DateRange
147176
* @param string $date
148177
* @return DateTime
149178
* @throws TypeInvalidException
179+
* @throws ParserException
150180
*/
151181
private function parseDate(string $date): DateTime
152182
{
183+
if (!preg_match('~^[0-9]{4}-[0-9]{2}-[0-9]{2}$~', $date)) {
184+
throw new ParserException($date, sprintf('String must be in the format "%s".', self::FORMAT_DATE));
185+
}
186+
153187
$date = date_create_from_format(self::FORMAT_DATE, $date);
154188

155189
if (!$date instanceof DateTime) {

0 commit comments

Comments
 (0)