From 08eae297fdd31526a446a80033c7cff3a067c7ca Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sat, 28 May 2022 02:50:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20DTO=20Convert=20=EC=9D=B8=EC=9E=90=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95,=20DateTimeConverter=20?= =?UTF-8?q?=ED=99=95=EC=9E=A5=20-=20DateTimeConverter=EC=97=90=20=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20Timezone=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sammo/DTO/Attr/Convert.php | 2 +- src/sammo/DTO/Converter/DateTimeConverter.php | 63 ++++++++++++++++++- tests/DTOTest.php | 36 +++++++++++ 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/sammo/DTO/Attr/Convert.php b/src/sammo/DTO/Attr/Convert.php index ff7c211f..1ce47d23 100644 --- a/src/sammo/DTO/Attr/Convert.php +++ b/src/sammo/DTO/Attr/Convert.php @@ -11,7 +11,7 @@ class Convert public function __construct( public readonly array $targetTypes, public readonly string $converterType, - array ...$args + ...$args ) { if(!is_subclass_of($converterType, \sammo\DTO\Converter\Converter::class)){ throw new \Exception("$converterType is not a subclass of \sammo\DTO\Converter\Converter"); diff --git a/src/sammo/DTO/Converter/DateTimeConverter.php b/src/sammo/DTO/Converter/DateTimeConverter.php index 187782d8..4e66dc4c 100644 --- a/src/sammo/DTO/Converter/DateTimeConverter.php +++ b/src/sammo/DTO/Converter/DateTimeConverter.php @@ -7,6 +7,7 @@ use sammo\TimeUtil; class DateTimeConverter implements Converter { protected bool $useFraction; + protected \DateTimeZone $timeZoneOffset; public function __construct(private array $types, ...$args) { if(count($args) > 0) { @@ -17,6 +18,48 @@ class DateTimeConverter implements Converter } else { $this->useFraction = false; } + + if(count($args) > 1) { + $args1 = $args[1]; + if(is_int($args1)){ + if($args1 < -12 || $args1 > 14){ + throw new \Exception('TimeZone argument must be between -12 and 14'); + } + if($args1 > 0){ + $offset = sprintf('+%02d00', $args1); + } + else{ + $offset = sprintf('-%02d00', abs($args1)); + } + $this->timeZoneOffset = new \DateTimeZone($offset); + } else if(is_float($args1)) { + if($args1 < -12 || $args1 > 14){ + throw new \Exception('TimeZone argument must be between -12 and 14'); + } + $isPositive = $args1 > 0; + $offset = abs($args1); + $hour = floor($offset); + $minute = floor(($offset - $hour) * 60); + if($isPositive){ + $offset = sprintf('+%02d%02d', $hour, $minute); + } else { + $offset = sprintf('-%02d%02d', $hour, $minute); + } + $this->timeZoneOffset = new \DateTimeZone($offset); + } + else if(is_string($args1)){ + $this->timeZoneOffset = new \DateTimeZone($args1); + } else if($args1 instanceof \DateTimeZone){ + $this->timeZoneOffset = $args1; + } else if($args1 === null){ + $this->timeZoneOffset = new \DateTimeZone(\date_default_timezone_get()); + } else { + throw new \Exception('DateTimeConverter constructor argument must be int|string|\DateTimeZone'); + } + } + else{ + $this->timeZoneOffset = new \DateTimeZone(\date_default_timezone_get()); + } } public function convertFrom(string|array|int|float|bool|null $raw): mixed @@ -27,10 +70,20 @@ class DateTimeConverter implements Converter if (!is_string($raw)){ throw new \Exception('DateTimeConverter can not convert non-string'); } - if (array_search('DateTime', $this->types, true) === false) { - return new \DateTime($raw); + if (array_search('DateTimeImmutable', $this->types, true) !== false) { + $objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset); } - return new \DateTimeImmutable($raw); + else if (array_search('DateTime', $this->types, true) !== false) { + $objDateTime = new \DateTime($raw, $this->timeZoneOffset); + } + else{ + $objDateTime = new \DateTimeImmutable($raw, $this->timeZoneOffset); + } + + if($objDateTime->getTimezone() !== $this->timeZoneOffset){ + $objDateTime = $objDateTime->setTimezone($this->timeZoneOffset); + } + return $objDateTime; } public function convertTo(mixed $data): string|array|int|float|bool|null @@ -41,6 +94,10 @@ class DateTimeConverter implements Converter if (!$data instanceof \DateTimeInterface) { throw new \Exception('DateTimeConverter can not convert non-DateTimeInterface'); } + + if($data->getTimezone() !== $this->timeZoneOffset){ + $data = \DateTimeImmutable::createFromInterface($data)->setTimezone($this->timeZoneOffset); + } return TimeUtil::format($data, $this->useFraction); } } diff --git a/tests/DTOTest.php b/tests/DTOTest.php index 3daf9d05..f8e8f100 100644 --- a/tests/DTOTest.php +++ b/tests/DTOTest.php @@ -8,6 +8,7 @@ use sammo\DTO\Attr\RawName; use sammo\DTO\Converter\ArrayConverter; use sammo\DTO\Converter\Converter; use sammo\DTO\Converter\MapConverter; +use sammo\DTO\Converter\DateTimeConverter; use sammo\Json; class TypeA extends DTO @@ -155,6 +156,21 @@ class TypeRawName extends DTO } } + +class TypeDateTime extends DTO{ + public function __construct( + #[Convert(DateTimeConverter::class)] + public \DateTimeImmutable $a, + #[Convert(DateTimeConverter::class, false, 9)] + public \DateTime $b, + #[Convert(DateTimeConverter::class, true, 8)] + public \DateTimeImmutable|\DateTime $c, + #[Convert(DateTimeConverter::class)] + public \DateTimeInterface $d, + ) { + } +} + class DTOTest extends PHPUnit\Framework\TestCase { public function testBasic() @@ -351,4 +367,24 @@ class DTOTest extends PHPUnit\Framework\TestCase $this->assertEquals($rawType, $testType); } + + public function testDateTime(){ + $rawType = [ + 'a' => '2022-01-01 10:11:22', + 'b' => '2022-02-01T12:34:56.1234+09:00', + 'c' => '2022-03-01T00:00:00.1234+09:00', + 'd' => '2022-04-01', + ]; + + $testValue = [ + 'a' => '2022-01-01 10:11:22', + 'b' => '2022-02-01 12:34:56', + 'c' => '2022-02-28 23:00:00.123400', + 'd' => '2022-04-01 00:00:00', + ]; + $obj = TypeDateTime::fromArray($rawType); + $testType = $obj->toArray(); + + $this->assertEquals($testValue, $testType); + } }