Skip to content

Commit 571e062

Browse files
authored
Add DateTime extension (#431)
* WIP add DateTime extension * Fix DateTime concat * Add implementation for dateTimeInterval and dateTimeThisWeek * Implement DateTimeExtension * Clarity phpdoc on DateTime * Add test template * Formatting * Add tests for DateTime * Dynamic contains * Fix formatting * Test types * unitTime should be an integer * Add test with timezone validation * Update version * Add note about strtotime * Add import for DateTimeExtension
1 parent 4e651e1 commit 571e062

File tree

6 files changed

+717
-21
lines changed

6 files changed

+717
-21
lines changed

src/Faker/Container/ContainerBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Faker\Extension\BarcodeExtension;
99
use Faker\Extension\BloodExtension;
1010
use Faker\Extension\ColorExtension;
11+
use Faker\Extension\DateTimeExtension;
1112
use Faker\Extension\FileExtension;
1213
use Faker\Extension\NumberExtension;
1314
use Faker\Extension\UuidExtension;
@@ -70,6 +71,7 @@ public static function defaultExtensions(): array
7071
BarcodeExtension::class => Core\Barcode::class,
7172
BloodExtension::class => Core\Blood::class,
7273
ColorExtension::class => Core\Color::class,
74+
DateTimeExtension::class => Core\DateTime::class,
7375
FileExtension::class => Core\File::class,
7476
NumberExtension::class => Core\Number::class,
7577
VersionExtension::class => Core\Version::class,

src/Faker/Core/DateTime.php

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
<?php
2+
3+
namespace Faker\Core;
4+
5+
use Faker\Extension\DateTimeExtension;
6+
use Faker\Extension\GeneratorAwareExtension;
7+
use Faker\Extension\GeneratorAwareExtensionTrait;
8+
use Faker\Extension\Helper;
9+
10+
/**
11+
* @experimental
12+
*
13+
* @since 1.20.0
14+
*/
15+
final class DateTime implements DateTimeExtension, GeneratorAwareExtension
16+
{
17+
use GeneratorAwareExtensionTrait;
18+
19+
/**
20+
* @var string[]
21+
*/
22+
private $centuries = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI'];
23+
24+
/**
25+
* @var string
26+
*/
27+
private $defaultTimezone = null;
28+
29+
/**
30+
* Get the POSIX-timestamp of a DateTime, int or string.
31+
*
32+
* @param \DateTime|float|int|string $until
33+
*
34+
* @return false|int
35+
*/
36+
protected function getTimestamp($until = 'now')
37+
{
38+
if (is_numeric($until)) {
39+
return (int) $until;
40+
}
41+
42+
if ($until instanceof \DateTime) {
43+
return $until->getTimestamp();
44+
}
45+
46+
return strtotime(empty($until) ? 'now' : $until);
47+
}
48+
49+
/**
50+
* Get a DateTime created based on a POSIX-timestamp.
51+
*
52+
* @param int $timestamp the UNIX / POSIX-compatible timestamp
53+
*/
54+
protected function getTimestampDateTime(int $timestamp): \DateTime
55+
{
56+
return new \DateTime('@' . $timestamp);
57+
}
58+
59+
protected function setDefaultTimezone(string $timezone = null): void
60+
{
61+
$this->defaultTimezone = $timezone;
62+
}
63+
64+
protected function getDefaultTimezone(): ?string
65+
{
66+
return $this->defaultTimezone;
67+
}
68+
69+
protected function resolveTimezone(?string $timezone): string
70+
{
71+
if ($timezone !== null) {
72+
return $timezone;
73+
}
74+
75+
return null === $this->defaultTimezone ? date_default_timezone_get() : $this->defaultTimezone;
76+
}
77+
78+
/**
79+
* Internal method to set the timezone on a DateTime object.
80+
*/
81+
protected function setTimezone(\DateTime $dateTime, ?string $timezone): \DateTime
82+
{
83+
$timezone = $this->resolveTimezone($timezone);
84+
85+
return $dateTime->setTimezone(new \DateTimeZone($timezone));
86+
}
87+
88+
public function dateTime($until = 'now', string $timezone = null): \DateTime
89+
{
90+
return $this->setTimezone(
91+
$this->getTimestampDateTime($this->unixTime($until)),
92+
$timezone
93+
);
94+
}
95+
96+
public function dateTimeAD($until = 'now', string $timezone = null): \DateTime
97+
{
98+
$min = (PHP_INT_SIZE > 4) ? -62135597361 : -PHP_INT_MAX;
99+
100+
return $this->setTimezone(
101+
$this->getTimestampDateTime($this->generator->numberBetween($min, $this->getTimestamp($until))),
102+
$timezone
103+
);
104+
}
105+
106+
public function dateTimeBetween($from = '-30 years', $until = 'now', string $timezone = null): \DateTime
107+
{
108+
$start = $this->getTimestamp($from);
109+
$end = $this->getTimestamp($until);
110+
111+
if ($start > $end) {
112+
throw new \InvalidArgumentException('"$from" must be anterior to "$until".');
113+
}
114+
115+
$timestamp = $this->generator->numberBetween($start, $end);
116+
117+
return $this->setTimezone(
118+
$this->getTimestampDateTime($timestamp),
119+
$timezone
120+
);
121+
}
122+
123+
public function dateTimeInInterval($from = '-30 years', string $interval = '+5 days', string $timezone = null): \DateTime
124+
{
125+
$intervalObject = \DateInterval::createFromDateString($interval);
126+
$datetime = $from instanceof \DateTime ? $from : new \DateTime($from);
127+
128+
$other = (clone $datetime)->add($intervalObject);
129+
130+
$begin = min($datetime, $other);
131+
$end = $datetime === $begin ? $other : $datetime;
132+
133+
return $this->dateTimeBetween($begin, $end, $timezone);
134+
}
135+
136+
public function dateTimeThisWeek($until = 'sunday this week', string $timezone = null): \DateTime
137+
{
138+
return $this->dateTimeBetween('monday this week', $until, $timezone);
139+
}
140+
141+
public function dateTimeThisMonth($until = 'last day of this month', string $timezone = null): \DateTime
142+
{
143+
return $this->dateTimeBetween('first day of this month', $until, $timezone);
144+
}
145+
146+
public function dateTimeThisYear($until = 'last day of december', string $timezone = null): \DateTime
147+
{
148+
return $this->dateTimeBetween('first day of january', $until, $timezone);
149+
}
150+
151+
public function dateTimeThisDecade($until = 'now', string $timezone = null): \DateTime
152+
{
153+
$year = floor(date('Y') / 10) * 10;
154+
155+
return $this->dateTimeBetween("first day of january $year", $until, $timezone);
156+
}
157+
158+
public function dateTimeThisCentury($until = 'now', string $timezone = null): \DateTime
159+
{
160+
$year = floor(date('Y') / 100) * 100;
161+
162+
return $this->dateTimeBetween("first day of january $year", $until, $timezone);
163+
}
164+
165+
public function date(string $format = 'Y-m-d', $until = 'now'): string
166+
{
167+
return $this->dateTime($until)->format($format);
168+
}
169+
170+
public function time(string $format = 'H:i:s', $until = 'now'): string
171+
{
172+
return $this->date($format, $until);
173+
}
174+
175+
public function unixTime($until = 'now'): int
176+
{
177+
return $this->generator->numberBetween(0, $this->getTimestamp($until));
178+
}
179+
180+
public function iso8601($until = 'now'): string
181+
{
182+
return $this->date(\DateTime::ISO8601, $until);
183+
}
184+
185+
public function amPm($until = 'now'): string
186+
{
187+
return $this->date('a', $until);
188+
}
189+
190+
public function dayOfMonth($until = 'now'): string
191+
{
192+
return $this->date('d', $until);
193+
}
194+
195+
public function dayOfWeek($until = 'now'): string
196+
{
197+
return $this->date('l', $until);
198+
}
199+
200+
public function month($until = 'now'): string
201+
{
202+
return $this->date('m', $until);
203+
}
204+
205+
public function monthName($until = 'now'): string
206+
{
207+
return $this->date('F', $until);
208+
}
209+
210+
public function year($until = 'now'): string
211+
{
212+
return $this->date('Y', $until);
213+
}
214+
215+
public function century(): string
216+
{
217+
return Helper::randomElement($this->centuries);
218+
}
219+
220+
public function timezone(): string
221+
{
222+
return Helper::randomElement(\DateTimeZone::listIdentifiers());
223+
}
224+
}

src/Faker/Core/Number.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ final class Number implements Extension\NumberExtension
1313
{
1414
public function numberBetween(int $min = 0, int $max = 2147483647): int
1515
{
16-
$int1 = $min < $max ? $min : $max;
17-
$int2 = $min < $max ? $max : $min;
16+
$int1 = min($min, $max);
17+
$int2 = max($min, $max);
1818

1919
return mt_rand($int1, $int2);
2020
}

0 commit comments

Comments
 (0)