66
77use Closure ;
88use DateTimeInterface ;
9+ use DateTimeZone ;
910use Psr \Http \Message \StreamInterface ;
1011use SimPod \ClickHouseClient \Exception \UnsupportedParamType ;
12+ use SimPod \ClickHouseClient \Exception \UnsupportedParamValue ;
1113use SimPod \ClickHouseClient \Sql \Type ;
1214
1315use function array_keys ;
1618use function implode ;
1719use function in_array ;
1820use function is_array ;
21+ use function is_float ;
22+ use function is_int ;
1923use function is_string ;
2024use function json_encode ;
2125use function sprintf ;
2428use function strtolower ;
2529use function trim ;
2630
27- /** @phpstan-type Converter = Closure(mixed, Type|string|null, bool):(StreamInterface|string) */
28- final class ParamValueConverterRegistry
31+ /** @phpstan-type Converter Closure(mixed, Type|string|null, bool):(StreamInterface|string) */
32+ final readonly class ParamValueConverterRegistry
2933{
30- /** @var list<string> */
31- private static array $ caseInsensitiveTypes = [
34+ private const CaseInsensitiveTypes = [
3235 'bool ' ,
3336 'date ' ,
3437 'date32 ' ,
@@ -47,8 +50,9 @@ final class ParamValueConverterRegistry
4750 /** @phpstan-var array<string, Converter> */
4851 private array $ registry ;
4952
50- public function __construct ()
51- {
53+ public function __construct (
54+ private DateTimeZone |null $ clickHouseTimeZone = null ,
55+ ) {
5256 $ formatPoint = static fn (array $ point ) => sprintf ('(%s) ' , implode (', ' , $ point ));
5357 // phpcs:ignore SlevomatCodingStandard.Functions.RequireArrowFunction.RequiredArrowFunction
5458 $ formatRingOrLineString = static function (array $ v ) use ($ formatPoint ) {
@@ -85,10 +89,10 @@ public function __construct()
8589
8690 'bool ' => static fn (bool $ value ) => $ value ,
8791
88- 'date ' => self ::dateConverter (),
89- 'date32 ' => self ::dateConverter (),
90- 'datetime ' => self ::dateTimeConverter (),
91- 'datetime32 ' => self ::dateTimeConverter (),
92+ 'date ' => self ::dateConverter ($ this -> clickHouseTimeZone ),
93+ 'date32 ' => self ::dateConverter ($ this -> clickHouseTimeZone ),
94+ 'datetime ' => self ::dateTimeConverter ($ this -> clickHouseTimeZone ),
95+ 'datetime32 ' => self ::dateTimeConverter ($ this -> clickHouseTimeZone ),
9296 'datetime64 ' => static fn (DateTimeInterface |string |int |float $ value ) => $ value instanceof DateTimeInterface
9397 ? $ value ->format ('Y-m-d H:i:s.u ' )
9498 : $ value ,
@@ -227,7 +231,7 @@ public function get(Type|string $type): Closure
227231
228232 $ typeName = strtolower ($ typeName );
229233 $ converter = $ this ->registry [$ typeName ] ?? null ;
230- if ($ converter !== null && in_array ($ typeName , self ::$ caseInsensitiveTypes , true )) {
234+ if ($ converter !== null && in_array ($ typeName , self ::CaseInsensitiveTypes , true )) {
231235 return $ converter ;
232236 }
233237
@@ -264,18 +268,42 @@ private static function decimalConverter(): Closure
264268 return static fn (float |int |string $ value ) => $ value ;
265269 }
266270
267- private static function dateConverter (): Closure
271+ private static function dateConverter (DateTimeZone | null $ clickHouseTimeZone ): Closure
268272 {
269- return static fn (DateTimeInterface |string |int |float $ value ) => $ value instanceof DateTimeInterface
270- ? $ value ->format ('Y-m-d ' )
271- : $ value ;
273+ return static function (mixed $ value ) use ($ clickHouseTimeZone ) {
274+ if ($ value instanceof DateTimeInterface) {
275+ if ($ clickHouseTimeZone !== null ) {
276+ $ value = $ value ->setTimezone ($ clickHouseTimeZone );
277+ }
278+
279+ return $ value ->format ('Y-m-d ' );
280+ }
281+
282+ if (is_string ($ value ) || is_float ($ value ) || is_int ($ value )) {
283+ return $ value ;
284+ }
285+
286+ throw UnsupportedParamValue::type ($ value );
287+ };
272288 }
273289
274- private static function dateTimeConverter (): Closure
290+ private static function dateTimeConverter (DateTimeZone | null $ clickHouseTimeZone ): Closure
275291 {
276- return static fn (DateTimeInterface |string |int |float $ value ) => $ value instanceof DateTimeInterface
277- ? $ value ->format ('Y-m-d H:i:s ' )
278- : $ value ;
292+ return static function (mixed $ value ) use ($ clickHouseTimeZone ) {
293+ if ($ value instanceof DateTimeInterface) {
294+ if ($ clickHouseTimeZone !== null ) {
295+ $ value = $ value ->setTimezone ($ clickHouseTimeZone );
296+ }
297+
298+ return $ value ->format ('Y-m-d H:i:s ' );
299+ }
300+
301+ if (is_string ($ value ) || is_float ($ value ) || is_int ($ value )) {
302+ return $ value ;
303+ }
304+
305+ throw UnsupportedParamValue::type ($ value );
306+ };
279307 }
280308
281309 private static function dateIntervalConverter (): Closure
0 commit comments