| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -978,7 +978,7 @@ def isin_op_impl(x: ibis_types.Value, op: ops.IsInOp): | |||
| 978 | 978 | ||
| 979 | 979 | @scalar_op_compiler.register_unary_op(ops.ToDatetimeOp, pass_op=True) | |
| 980 | 980 | def to_datetime_op_impl(x: ibis_types.Value, op: ops.ToDatetimeOp): | |
| 981 | - if x.type() == ibis_dtypes.str: | ||
| 981 | + if x.type() in (ibis_dtypes.str, ibis_dtypes.Timestamp("UTC")): # type: ignore | ||
| 982 | 982 | return x.try_cast(ibis_dtypes.Timestamp(None)) # type: ignore | |
| 983 | 983 | else: | |
| 984 | 984 | # Numerical inputs. | |
@@ -1001,6 +1001,9 @@ def to_timestamp_op_impl(x: ibis_types.Value, op: ops.ToTimestampOp): | |||
| 1001 | 1001 | if op.format | |
| 1002 | 1002 | else timestamp(x) | |
| 1003 | 1003 | ) | |
| 1004 | + elif x.type() == ibis_dtypes.Timestamp(None): # type: ignore | ||
| 1005 | + | ||
| 1006 | + return timestamp(x) | ||
| 1004 | 1007 | else: | |
| 1005 | 1008 | # Numerical inputs. | |
| 1006 | 1009 | if op.format: | |
@@ -2016,8 +2019,8 @@ def _ibis_num(number: float): | |||
| 2016 | 2019 | ||
| 2017 | 2020 | ||
| 2018 | 2021 | @ibis_udf.scalar.builtin | |
| 2019 | - def timestamp(a: str) -> ibis_dtypes.timestamp: # type: ignore | ||
| 2020 | - """Convert string to timestamp.""" | ||
| 2022 | + def timestamp(a) -> ibis_dtypes.timestamp: # type: ignore | ||
| 2023 | + """Convert string or a datetime to timestamp.""" | ||
| 2021 | 2024 | ||
| 2022 | 2025 | ||
| 2023 | 2026 | @ibis_udf.scalar.builtin | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -371,7 +371,7 @@ def _(expr: TypedExpr, op: ops.ToDatetimeOp) -> sge.Expression: | |||
| 371 | 371 | ) | |
| 372 | 372 | return sge.Cast(this=result, to="DATETIME") | |
| 373 | 373 | ||
| 374 | - if expr.dtype == dtypes.STRING_DTYPE: | ||
| 374 | + if expr.dtype in (dtypes.STRING_DTYPE, dtypes.TIMESTAMP_DTYPE): | ||
| 375 | 375 | return sge.TryCast(this=expr.expr, to="DATETIME") | |
| 376 | 376 | ||
| 377 | 377 | value = expr.expr | |
@@ -396,7 +396,7 @@ def _(expr: TypedExpr, op: ops.ToTimestampOp) -> sge.Expression: | |||
| 396 | 396 | "PARSE_TIMESTAMP", sge.convert(op.format), expr.expr, sge.convert("UTC") | |
| 397 | 397 | ) | |
| 398 | 398 | ||
| 399 | - if expr.dtype == dtypes.STRING_DTYPE: | ||
| 399 | + if expr.dtype in (dtypes.STRING_DTYPE, dtypes.DATETIME_DTYPE): | ||
| 400 | 400 | return sge.func("TIMESTAMP", expr.expr) | |
| 401 | 401 | ||
| 402 | 402 | value = expr.expr | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,6 +73,7 @@ def output_type(self, *input_types: dtypes.ExpressionType) -> dtypes.ExpressionT | |||
| 73 | 73 | dtypes.INT_DTYPE, | |
| 74 | 74 | dtypes.STRING_DTYPE, | |
| 75 | 75 | dtypes.DATE_DTYPE, | |
| 76 | + dtypes.TIMESTAMP_DTYPE, | ||
| 76 | 77 | ): | |
| 77 | 78 | raise TypeError("expected string or numeric input") | |
| 78 | 79 | return pd.ArrowDtype(pa.timestamp("us", tz=None)) | |
@@ -91,6 +92,7 @@ def output_type(self, *input_types: dtypes.ExpressionType) -> dtypes.ExpressionT | |||
| 91 | 92 | dtypes.INT_DTYPE, | |
| 92 | 93 | dtypes.STRING_DTYPE, | |
| 93 | 94 | dtypes.DATE_DTYPE, | |
| 95 | + dtypes.DATETIME_DTYPE, | ||
| 94 | 96 | ): | |
| 95 | 97 | raise TypeError("expected string or numeric input") | |
| 96 | 98 | return pd.ArrowDtype(pa.timestamp("us", tz="UTC")) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,7 @@ | |||
| 15 | 15 | from __future__ import annotations | |
| 16 | 16 | ||
| 17 | 17 | import datetime as dt | |
| 18 | - from typing import Optional | ||
| 18 | + from typing import Literal, Optional | ||
| 19 | 19 | ||
| 20 | 20 | import bigframes_vendored.pandas.core.arrays.datetimelike as vendored_pandas_datetimelike | |
| 21 | 21 | import bigframes_vendored.pandas.core.indexes.accessor as vendordt | |
@@ -147,6 +147,21 @@ def tz(self) -> Optional[dt.timezone]: | |||
| 147 | 147 | else: | |
| 148 | 148 | raise ValueError(f"Unexpected timezone {tz_string}") | |
| 149 | 149 | ||
| 150 | + def tz_localize(self, tz: Literal["UTC"] | None) -> series.Series: | ||
| 151 | + if tz == "UTC": | ||
| 152 | + if self._data.dtype == dtypes.TIMESTAMP_DTYPE: | ||
| 153 | + raise ValueError("Already tz-aware.") | ||
| 154 | + | ||
| 155 | + return self._data._apply_unary_op(ops.ToTimestampOp()) | ||
| 156 | + | ||
| 157 | + if tz is None: | ||
| 158 | + if self._data.dtype == dtypes.DATETIME_DTYPE: | ||
| 159 | + return self._data # no-op | ||
| 160 | + | ||
| 161 | + return self._data._apply_unary_op(ops.ToDatetimeOp()) | ||
| 162 | + | ||
| 163 | + raise ValueError(f"Unsupported timezone {tz}") | ||
| 164 | + | ||
| 150 | 165 | @property | |
| 151 | 166 | def unit(self) -> str: | |
| 152 | 167 | # Assumption: pyarrow dtype | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -324,6 +324,42 @@ def test_dt_tz(scalars_dfs, col_name): | |||
| 324 | 324 | assert bf_result == pd_result | |
| 325 | 325 | ||
| 326 | 326 | ||
| 327 | + @pytest.mark.parametrize( | ||
| 328 | + ("col_name", "tz"), | ||
| 329 | + [ | ||
| 330 | + ("datetime_col", None), | ||
| 331 | + ("timestamp_col", None), | ||
| 332 | + ("datetime_col", "UTC"), | ||
| 333 | + ], | ||
| 334 | + ) | ||
| 335 | + def test_dt_tz_localize(scalars_dfs, col_name, tz): | ||
| 336 | + pytest.importorskip("pandas", minversion="2.0.0") | ||
| 337 | + scalars_df, scalars_pandas_df = scalars_dfs | ||
| 338 | + bf_series = scalars_df[col_name] | ||
| 339 | + | ||
| 340 | + bf_result = bf_series.dt.tz_localize(tz) | ||
| 341 | + pd_result = scalars_pandas_df[col_name].dt.tz_localize(tz) | ||
| 342 | + | ||
| 343 | + testing.assert_series_equal( | ||
| 344 | + bf_result.to_pandas(), pd_result, check_index_type=False | ||
| 345 | + ) | ||
| 346 | + | ||
| 347 | + | ||
| 348 | + @pytest.mark.parametrize( | ||
| 349 | + ("col_name", "tz"), | ||
| 350 | + [ | ||
| 351 | + ("timestamp_col", "UTC"), | ||
| 352 | + ("datetime_col", "US/Eastern"), | ||
| 353 | + ], | ||
| 354 | + ) | ||
| 355 | + def test_dt_tz_localize_invalid_inputs(scalars_dfs, col_name, tz): | ||
| 356 | + pytest.importorskip("pandas", minversion="2.0.0") | ||
| 357 | + scalars_df, _ = scalars_dfs | ||
| 358 | + | ||
| 359 | + with pytest.raises(ValueError): | ||
| 360 | + scalars_df[col_name].dt.tz_localize(tz) | ||
| 361 | + | ||
| 362 | + | ||
| 327 | 363 | @pytest.mark.parametrize( | |
| 328 | 364 | ("col_name",), | |
| 329 | 365 | DATETIME_COL_NAMES, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,6 @@ | |||
| 1 | 1 | SELECT | |
| 2 | 2 | CAST(TIMESTAMP_MICROS(CAST(TRUNC(`int64_col` * 0.001) AS INT64)) AS DATETIME) AS `int64_col`, | |
| 3 | 3 | SAFE_CAST(`string_col` AS DATETIME), | |
| 4 | - CAST(TIMESTAMP_MICROS(CAST(TRUNC(`float64_col` * 0.001) AS INT64)) AS DATETIME) AS `float64_col` | ||
| 4 | + CAST(TIMESTAMP_MICROS(CAST(TRUNC(`float64_col` * 0.001) AS INT64)) AS DATETIME) AS `float64_col`, | ||
| 5 | + SAFE_CAST(`timestamp_col` AS DATETIME) | ||
| 5 | 6 | FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,5 +4,6 @@ SELECT | |||
| 4 | 4 | CAST(TIMESTAMP_MICROS(CAST(TRUNC(`int64_col` * 1000000) AS INT64)) AS TIMESTAMP) AS `int64_col_s`, | |
| 5 | 5 | CAST(TIMESTAMP_MICROS(CAST(TRUNC(`int64_col` * 1000) AS INT64)) AS TIMESTAMP) AS `int64_col_ms`, | |
| 6 | 6 | CAST(TIMESTAMP_MICROS(CAST(TRUNC(`int64_col`) AS INT64)) AS TIMESTAMP) AS `int64_col_us`, | |
| 7 | - CAST(TIMESTAMP_MICROS(CAST(TRUNC(`int64_col` * 0.001) AS INT64)) AS TIMESTAMP) AS `int64_col_ns` | ||
| 7 | + CAST(TIMESTAMP_MICROS(CAST(TRUNC(`int64_col` * 0.001) AS INT64)) AS TIMESTAMP) AS `int64_col_ns`, | ||
| 8 | + TIMESTAMP(`datetime_col`) AS `datetime_col` | ||
| 8 | 9 | FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -180,7 +180,7 @@ def test_time(scalar_types_df: bpd.DataFrame, snapshot): | |||
| 180 | 180 | ||
| 181 | 181 | ||
| 182 | 182 | def test_to_datetime(scalar_types_df: bpd.DataFrame, snapshot): | |
| 183 | - col_names = ["int64_col", "string_col", "float64_col"] | ||
| 183 | + col_names = ["int64_col", "string_col", "float64_col", "timestamp_col"] | ||
| 184 | 184 | bf_df = scalar_types_df[col_names] | |
| 185 | 185 | ops_map = {col_name: ops.ToDatetimeOp().as_expr(col_name) for col_name in col_names} | |
| 186 | 186 | ||
@@ -189,14 +189,15 @@ def test_to_datetime(scalar_types_df: bpd.DataFrame, snapshot): | |||
| 189 | 189 | ||
| 190 | 190 | ||
| 191 | 191 | def test_to_timestamp(scalar_types_df: bpd.DataFrame, snapshot): | |
| 192 | - bf_df = scalar_types_df[["int64_col", "string_col", "float64_col"]] | ||
| 192 | + bf_df = scalar_types_df[["int64_col", "string_col", "float64_col", "datetime_col"]] | ||
| 193 | 193 | ops_map = { | |
| 194 | 194 | "int64_col": ops.ToTimestampOp().as_expr("int64_col"), | |
| 195 | 195 | "float64_col": ops.ToTimestampOp().as_expr("float64_col"), | |
| 196 | 196 | "int64_col_s": ops.ToTimestampOp(unit="s").as_expr("int64_col"), | |
| 197 | 197 | "int64_col_ms": ops.ToTimestampOp(unit="ms").as_expr("int64_col"), | |
| 198 | 198 | "int64_col_us": ops.ToTimestampOp(unit="us").as_expr("int64_col"), | |
| 199 | 199 | "int64_col_ns": ops.ToTimestampOp(unit="ns").as_expr("int64_col"), | |
| 200 | + "datetime_col": ops.ToTimestampOp().as_expr("datetime_col"), | ||
| 200 | 201 | } | |
| 201 | 202 | ||
| 202 | 203 | sql = utils._apply_ops_to_sql(bf_df, list(ops_map.values()), list(ops_map.keys())) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,5 @@ | |||
| 1 | + from typing import Literal | ||
| 2 | + | ||
| 1 | 3 | from bigframes import constants | |
| 2 | 4 | ||
| 3 | 5 | ||
@@ -499,6 +501,34 @@ def tz(self): | |||
| 499 | 501 | ||
| 500 | 502 | raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) | |
| 501 | 503 | ||
| 504 | + @property | ||
| 505 | + def tz_localize(self, tz: Literal["UTC"] | None): | ||
| 506 | + """Localize tz-naive Datetime Array/Index to tz-aware Datetime Array/Index. | ||
| 507 | + | ||
| 508 | + This method takes a time zone (tz) naive Datetime Array/Index object and makes | ||
| 509 | + this time zone aware. It does not move the time to another time zone. Only "UTC" | ||
| 510 | + timezone is supported. | ||
| 511 | + | ||
| 512 | + This method can also be used to do the inverse - to create a time zone unaware | ||
| 513 | + object from an aware object. To that end, pass tz=None. | ||
| 514 | + | ||
| 515 | + **Examples:** | ||
| 516 | + | ||
| 517 | + >>> import bigframes.pandas as bpd | ||
| 518 | + >>> s = bpd.Series([pd.Timestamp(year = 2026, month=1, day=1)]) | ||
| 519 | + >>> s | ||
| 520 | + 0 2026-01-01 00:00:00 | ||
| 521 | + dtype: timestamp[us][pyarrow] | ||
| 522 | + >>> s.dt.tz_localize('UTC') | ||
| 523 | + 0 2026-01-01 00:00:00+00:00 | ||
| 524 | + dtype: timestamp[us, tz=UTC][pyarrow] | ||
| 525 | + | ||
| 526 | + Returns: | ||
| 527 | + A BigFrames series with the updated timezone. | ||
| 528 | + """ | ||
| 529 | + | ||
| 530 | + raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) | ||
| 531 | + | ||
| 502 | 532 | @property | |
| 503 | 533 | def unit(self) -> str: | |
| 504 | 534 | """Returns the unit of time precision. | |
| Back | FazBrowse Home | New Git URL |
0 commit comments