Skip to content

Commit af04e69

Browse files
fix: pl.from_epoch was lossy for fractional values
1 parent 9908bce commit af04e69

File tree

2 files changed

+29
-11
lines changed

2 files changed

+29
-11
lines changed

py-polars/src/polars/functions/lazy.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from polars._utils.unstable import issue_unstable_warning, unstable
2424
from polars._utils.various import extend_bool, qualified_type_name
2525
from polars._utils.wrap import wrap_df, wrap_expr, wrap_s
26-
from polars.datatypes import DTYPE_TEMPORAL_UNITS, Date, Datetime, Int64
26+
from polars.datatypes import DTYPE_TEMPORAL_UNITS, Date, Datetime
2727
from polars.datatypes._parse import parse_into_datatype_expr
2828
from polars.lazyframe.opt_flags import (
2929
DEFAULT_QUERY_OPT_FLAGS,
@@ -2567,10 +2567,8 @@ def from_epoch(
25672567
Depending on the `time_unit` provided, this function will return a different dtype:
25682568
25692569
- time_unit="d" returns pl.Date
2570-
- time_unit="s" returns pl.Datetime["us"] (pl.Datetime's default)
2571-
- time_unit="ms" returns pl.Datetime["ms"]
2572-
- time_unit="us" returns pl.Datetime["us"]
25732570
- time_unit="ns" returns pl.Datetime["ns"]
2571+
- otherwise returns pl.Datetime["us"] (Polars' default unit)
25742572
25752573
Parameters
25762574
----------
@@ -2581,18 +2579,35 @@ def from_epoch(
25812579
25822580
Examples
25832581
--------
2584-
>>> df = pl.DataFrame({"timestamp": [1666683077, 1666683099]}).lazy()
2585-
>>> df.select(pl.from_epoch(pl.col("timestamp"), time_unit="s")).collect()
2582+
Convert from integer seconds:
2583+
2584+
>>> df = pl.LazyFrame({"ts": [1666683077, 1666683099]})
2585+
>>> df.select(pl.from_epoch(pl.col("ts"), time_unit="s")).collect()
25862586
shape: (2, 1)
25872587
┌─────────────────────┐
2588-
timestamp
2588+
ts
25892589
│ --- │
25902590
│ datetime[μs] │
25912591
╞═════════════════════╡
25922592
│ 2022-10-25 07:31:17 │
25932593
│ 2022-10-25 07:31:39 │
25942594
└─────────────────────┘
25952595
2596+
Convert from fractional seconds:
2597+
2598+
>>> df = pl.LazyFrame({"ts": [-609066.723456, 1066445333.8888, 3405071999.987654]})
2599+
>>> df.select(pl.from_epoch(pl.col("ts"), time_unit="s")).collect()
2600+
shape: (3, 1)
2601+
┌────────────────────────────┐
2602+
│ ts │
2603+
│ --- │
2604+
│ datetime[μs] │
2605+
╞════════════════════════════╡
2606+
│ 1969-12-24 22:48:53.276544 │
2607+
│ 2003-10-18 02:48:53.888800 │
2608+
│ 2077-11-25 13:19:59.987654 │
2609+
└────────────────────────────┘
2610+
25962611
The function can also be used in an eager context by passing a Series.
25972612
25982613
>>> s = pl.Series([12345, 12346])
@@ -2612,7 +2627,9 @@ def from_epoch(
26122627
if time_unit == "d":
26132628
return column.cast(Date)
26142629
elif time_unit == "s":
2615-
return (column.cast(Int64) * 1_000_000).cast(Datetime("us"))
2630+
return (column * 1_000_000).cast(Datetime("us"))
2631+
elif time_unit == "ms":
2632+
return (column * 1_000).cast(Datetime("us"))
26162633
elif time_unit in DTYPE_TEMPORAL_UNITS:
26172634
return column.cast(Datetime(time_unit))
26182635
else:

py-polars/tests/unit/lazyframe/test_lazyframe.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,10 +1262,11 @@ def test_from_epoch(input_dtype: PolarsDataType) -> None:
12621262
exp_dt = datetime(2006, 5, 17, 15, 34, 4)
12631263
expected = pl.DataFrame(
12641264
[
1265+
# 'd' → Date, 'ns' → Datetime('ns'), otherwise → Datetime('us')
12651266
pl.Series("timestamp_d", [date(2006, 5, 17)]),
1266-
pl.Series("timestamp_s", [exp_dt]), # s is no Polars dtype, defaults to us
1267-
pl.Series("timestamp_ms", [exp_dt]).cast(pl.Datetime("ms")),
1268-
pl.Series("timestamp_us", [exp_dt]), # us is Polars Datetime default
1267+
pl.Series("timestamp_s", [exp_dt]),
1268+
pl.Series("timestamp_ms", [exp_dt]),
1269+
pl.Series("timestamp_us", [exp_dt]),
12691270
pl.Series("timestamp_ns", [exp_dt]).cast(pl.Datetime("ns")),
12701271
]
12711272
)

0 commit comments

Comments
 (0)