diff --git a/src/Param/ParamValueConverterRegistry.php b/src/Param/ParamValueConverterRegistry.php index 331d967..74a0d6e 100644 --- a/src/Param/ParamValueConverterRegistry.php +++ b/src/Param/ParamValueConverterRegistry.php @@ -6,8 +6,10 @@ use Closure; use DateTimeInterface; +use DateTimeZone; use Psr\Http\Message\StreamInterface; use SimPod\ClickHouseClient\Exception\UnsupportedParamType; +use SimPod\ClickHouseClient\Exception\UnsupportedParamValue; use SimPod\ClickHouseClient\Sql\Type; use function array_keys; @@ -16,6 +18,8 @@ use function implode; use function in_array; use function is_array; +use function is_float; +use function is_int; use function is_string; use function json_encode; use function sprintf; @@ -24,11 +28,10 @@ use function strtolower; use function trim; -/** @phpstan-type Converter = Closure(mixed, Type|string|null, bool):(StreamInterface|string) */ -final class ParamValueConverterRegistry +/** @phpstan-type Converter Closure(mixed, Type|string|null, bool):(StreamInterface|string) */ +final readonly class ParamValueConverterRegistry { - /** @var list */ - private static array $caseInsensitiveTypes = [ + private const CaseInsensitiveTypes = [ 'bool', 'date', 'date32', @@ -47,8 +50,9 @@ final class ParamValueConverterRegistry /** @phpstan-var array */ private array $registry; - public function __construct() - { + public function __construct( + private DateTimeZone|null $clickHouseTimeZone = null, + ) { $formatPoint = static fn (array $point) => sprintf('(%s)', implode(',', $point)); // phpcs:ignore SlevomatCodingStandard.Functions.RequireArrowFunction.RequiredArrowFunction $formatRingOrLineString = static function (array $v) use ($formatPoint) { @@ -85,10 +89,10 @@ public function __construct() 'bool' => static fn (bool $value) => $value, - 'date' => self::dateConverter(), - 'date32' => self::dateConverter(), - 'datetime' => self::dateTimeConverter(), - 'datetime32' => self::dateTimeConverter(), + 'date' => self::dateConverter($this->clickHouseTimeZone), + 'date32' => self::dateConverter($this->clickHouseTimeZone), + 'datetime' => self::dateTimeConverter($this->clickHouseTimeZone), + 'datetime32' => self::dateTimeConverter($this->clickHouseTimeZone), 'datetime64' => static fn (DateTimeInterface|string|int|float $value) => $value instanceof DateTimeInterface ? $value->format('Y-m-d H:i:s.u') : $value, @@ -227,7 +231,7 @@ public function get(Type|string $type): Closure $typeName = strtolower($typeName); $converter = $this->registry[$typeName] ?? null; - if ($converter !== null && in_array($typeName, self::$caseInsensitiveTypes, true)) { + if ($converter !== null && in_array($typeName, self::CaseInsensitiveTypes, true)) { return $converter; } @@ -264,18 +268,42 @@ private static function decimalConverter(): Closure return static fn (float|int|string $value) => $value; } - private static function dateConverter(): Closure + private static function dateConverter(DateTimeZone|null $clickHouseTimeZone): Closure { - return static fn (DateTimeInterface|string|int|float $value) => $value instanceof DateTimeInterface - ? $value->format('Y-m-d') - : $value; + return static function (mixed $value) use ($clickHouseTimeZone) { + if ($value instanceof DateTimeInterface) { + if ($clickHouseTimeZone !== null) { + $value = $value->setTimezone($clickHouseTimeZone); + } + + return $value->format('Y-m-d'); + } + + if (is_string($value) || is_float($value) || is_int($value)) { + return $value; + } + + throw UnsupportedParamValue::type($value); + }; } - private static function dateTimeConverter(): Closure + private static function dateTimeConverter(DateTimeZone|null $clickHouseTimeZone): Closure { - return static fn (DateTimeInterface|string|int|float $value) => $value instanceof DateTimeInterface - ? $value->format('Y-m-d H:i:s') - : $value; + return static function (mixed $value) use ($clickHouseTimeZone) { + if ($value instanceof DateTimeInterface) { + if ($clickHouseTimeZone !== null) { + $value = $value->setTimezone($clickHouseTimeZone); + } + + return $value->format('Y-m-d H:i:s'); + } + + if (is_string($value) || is_float($value) || is_int($value)) { + return $value; + } + + throw UnsupportedParamValue::type($value); + }; } private static function dateIntervalConverter(): Closure diff --git a/src/Sql/ValueFormatter.php b/src/Sql/ValueFormatter.php index b68caaf..c038883 100644 --- a/src/Sql/ValueFormatter.php +++ b/src/Sql/ValueFormatter.php @@ -26,7 +26,7 @@ /** @internal */ final readonly class ValueFormatter { - public function __construct(private DateTimeZone|null $dateTimeZone = null) + public function __construct(private DateTimeZone|null $clickHouseTimeZone = null) { } @@ -67,8 +67,8 @@ public function format(mixed $value, string|null $paramName = null, string|null } if ($value instanceof DateTimeImmutable) { - if ($this->dateTimeZone !== null) { - $value = $value->setTimezone($this->dateTimeZone); + if ($this->clickHouseTimeZone !== null) { + $value = $value->setTimezone($this->clickHouseTimeZone); } return "'" . $value->format('Y-m-d H:i:s') . "'";