module Polars
# Namespace for datetime related expressions.
class DateTimeExpr
# @private
attr_accessor :_rbexpr
# @private
def initialize(expr)
self._rbexpr = expr._rbexpr
end
# Offset by `n` business days.
#
# @note
# This functionality is considered **unstable**. It may be changed
# at any point without it being considered a breaking change.
#
# @param n
# Number of business days to offset by. Can be a single number of an
# expression.
# @param week_mask
# Which days of the week to count. The default is Monday to Friday.
# If you wanted to count only Monday to Thursday, you would pass
# `[true, true, true, true, false, false, false]`.
# @param roll
# What to do when the start date lands on a non-business day. Options are:
#
# - `'raise'`: raise an error
# - `'forward'`: move to the next business day
# - `'backward'`: move to the previous business day
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new({"start" => [Date.new(2020, 1, 1), Date.new(2020, 1, 2)]})
# df.with_columns(result: Polars.col("start").dt.add_business_days(5))
# # =>
# # shape: (2, 2)
# # ┌────────────┬────────────┐
# # │ start ┆ result │
# # │ --- ┆ --- │
# # │ date ┆ date │
# # ╞════════════╪════════════╡
# # │ 2020-01-01 ┆ 2020-01-08 │
# # │ 2020-01-02 ┆ 2020-01-09 │
# # └────────────┴────────────┘
def add_business_days(
n,
week_mask: [true, true, true, true, true, false, false],
roll: "raise"
)
n_rbexpr = Utils.parse_into_expression(n)
Utils.wrap_expr(
_rbexpr.dt_add_business_days(
n_rbexpr,
week_mask,
[],
roll
)
)
end
# Divide the date/datetime range into buckets.
#
# Each date/datetime is mapped to the start of its bucket.
#
# @param every [String]
# Every interval start and period length
#
# @return [Expr]
#
# @note
# The `every` argument is created with the
# the following small string formatting language:
#
# 1ns # 1 nanosecond
# 1us # 1 microsecond
# 1ms # 1 millisecond
# 1s # 1 second
# 1m # 1 minute
# 1h # 1 hour
# 1d # 1 day
# 1w # 1 week
# 1mo # 1 calendar month
# 1y # 1 calendar year
#
# eg: 3d12h4m25s # 3 days, 12 hours, 4 minutes, and 25 seconds
#
# @example
# df = (
# Polars.datetime_range(
# DateTime.new(2001, 1, 1),
# DateTime.new(2001, 1, 2),
# "225m",
# eager: true
# )
# .alias("datetime")
# .to_frame
# )
# # =>
# # shape: (7, 1)
# # ┌─────────────────────┐
# # │ datetime │
# # │ --- │
# # │ datetime[ns] │
# # ╞═════════════════════╡
# # │ 2001-01-01 00:00:00 │
# # │ 2001-01-01 03:45:00 │
# # │ 2001-01-01 07:30:00 │
# # │ 2001-01-01 11:15:00 │
# # │ 2001-01-01 15:00:00 │
# # │ 2001-01-01 18:45:00 │
# # │ 2001-01-01 22:30:00 │
# # └─────────────────────┘
#
# @example
# df.select(Polars.col("datetime").dt.truncate("1h"))
# # =>
# # shape: (7, 1)
# # ┌─────────────────────┐
# # │ datetime │
# # │ --- │
# # │ datetime[ns] │
# # ╞═════════════════════╡
# # │ 2001-01-01 00:00:00 │
# # │ 2001-01-01 03:00:00 │
# # │ 2001-01-01 07:00:00 │
# # │ 2001-01-01 11:00:00 │
# # │ 2001-01-01 15:00:00 │
# # │ 2001-01-01 18:00:00 │
# # │ 2001-01-01 22:00:00 │
# # └─────────────────────┘
#
# @example
# df = (
# Polars.datetime_range(
# DateTime.new(2001, 1, 1), DateTime.new(2001, 1, 1, 1), "10m", eager: true
# )
# .alias("datetime")
# .to_frame
# )
# df.select(["datetime", Polars.col("datetime").dt.truncate("30m").alias("truncate")])
# # =>
# # shape: (7, 2)
# # ┌─────────────────────┬─────────────────────┐
# # │ datetime ┆ truncate │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ datetime[ns] │
# # ╞═════════════════════╪═════════════════════╡
# # │ 2001-01-01 00:00:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-01 00:10:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-01 00:20:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-01 00:30:00 ┆ 2001-01-01 00:30:00 │
# # │ 2001-01-01 00:40:00 ┆ 2001-01-01 00:30:00 │
# # │ 2001-01-01 00:50:00 ┆ 2001-01-01 00:30:00 │
# # │ 2001-01-01 01:00:00 ┆ 2001-01-01 01:00:00 │
# # └─────────────────────┴─────────────────────┘
def truncate(every)
if !every.is_a?(Expr)
every = Utils.parse_as_duration_string(every)
end
every = Utils.parse_into_expression(every, str_as_lit: true)
Utils.wrap_expr(_rbexpr.dt_truncate(every))
end
# Divide the date/datetime range into buckets.
#
# Each date/datetime in the first half of the interval
# is mapped to the start of its bucket.
# Each date/datetime in the seconod half of the interval
# is mapped to the end of its bucket.
#
# @param every [String]
# Every interval start and period length
#
# @return [Expr]
#
# @note
# The `every` and `offset` argument are created with the
# the following small string formatting language:
#
# 1ns # 1 nanosecond
# 1us # 1 microsecond
# 1ms # 1 millisecond
# 1s # 1 second
# 1m # 1 minute
# 1h # 1 hour
# 1d # 1 day
# 1w # 1 week
# 1mo # 1 calendar month
# 1y # 1 calendar year
#
# eg: 3d12h4m25s # 3 days, 12 hours, 4 minutes, and 25 seconds
#
# @note
# This functionality is currently experimental and may
# change without it being considered a breaking change.
#
# @example
# df = (
# Polars.datetime_range(
# DateTime.new(2001, 1, 1),
# DateTime.new(2001, 1, 2),
# "225m",
# eager: true
# )
# .alias("datetime")
# .to_frame
# )
# df.with_columns(Polars.col("datetime").dt.round("1h").alias("round"))
# # =>
# # shape: (7, 2)
# # ┌─────────────────────┬─────────────────────┐
# # │ datetime ┆ round │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ datetime[ns] │
# # ╞═════════════════════╪═════════════════════╡
# # │ 2001-01-01 00:00:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-01 03:45:00 ┆ 2001-01-01 04:00:00 │
# # │ 2001-01-01 07:30:00 ┆ 2001-01-01 08:00:00 │
# # │ 2001-01-01 11:15:00 ┆ 2001-01-01 11:00:00 │
# # │ 2001-01-01 15:00:00 ┆ 2001-01-01 15:00:00 │
# # │ 2001-01-01 18:45:00 ┆ 2001-01-01 19:00:00 │
# # │ 2001-01-01 22:30:00 ┆ 2001-01-01 23:00:00 │
# # └─────────────────────┴─────────────────────┘
#
# @example
# df = (
# Polars.datetime_range(
# DateTime.new(2001, 1, 1), DateTime.new(2001, 1, 1, 1), "10m", eager: true
# )
# .alias("datetime")
# .to_frame
# )
# df.with_columns(Polars.col("datetime").dt.round("30m").alias("round"))
# # =>
# # shape: (7, 2)
# # ┌─────────────────────┬─────────────────────┐
# # │ datetime ┆ round │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ datetime[ns] │
# # ╞═════════════════════╪═════════════════════╡
# # │ 2001-01-01 00:00:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-01 00:10:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-01 00:20:00 ┆ 2001-01-01 00:30:00 │
# # │ 2001-01-01 00:30:00 ┆ 2001-01-01 00:30:00 │
# # │ 2001-01-01 00:40:00 ┆ 2001-01-01 00:30:00 │
# # │ 2001-01-01 00:50:00 ┆ 2001-01-01 01:00:00 │
# # │ 2001-01-01 01:00:00 ┆ 2001-01-01 01:00:00 │
# # └─────────────────────┴─────────────────────┘
def round(every)
every = Utils.parse_into_expression(every, str_as_lit: true)
Utils.wrap_expr(_rbexpr.dt_round(every))
end
# Replace time unit.
#
# @param year [Object]
# Column or literal.
# @param month [Object]
# Column or literal, ranging from 1-12.
# @param day [Object]
# Column or literal, ranging from 1-31.
# @param hour [Object]
# Column or literal, ranging from 0-23.
# @param minute [Object]
# Column or literal, ranging from 0-59.
# @param second [Object]
# Column or literal, ranging from 0-59.
# @param microsecond [Object]
# Column or literal, ranging from 0-999999.
# @param ambiguous [String]
# Determine how to deal with ambiguous datetimes:
#
# - `'raise'` (default): raise
# - `'earliest'`: use the earliest datetime
# - `'latest'`: use the latest datetime
# - `'null'`: set to null
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => [Date.new(2024, 4, 1), Date.new(2025, 3, 16)],
# "new_day" => [10, 15]
# }
# )
# df.with_columns(Polars.col("date").dt.replace(day: "new_day").alias("replaced"))
# # =>
# # shape: (2, 3)
# # ┌────────────┬─────────┬────────────┐
# # │ date ┆ new_day ┆ replaced │
# # │ --- ┆ --- ┆ --- │
# # │ date ┆ i64 ┆ date │
# # ╞════════════╪═════════╪════════════╡
# # │ 2024-04-01 ┆ 10 ┆ 2024-04-10 │
# # │ 2025-03-16 ┆ 15 ┆ 2025-03-15 │
# # └────────────┴─────────┴────────────┘
#
# @example
# df.with_columns(Polars.col("date").dt.replace(year: 1800).alias("replaced"))
# # =>
# # shape: (2, 3)
# # ┌────────────┬─────────┬────────────┐
# # │ date ┆ new_day ┆ replaced │
# # │ --- ┆ --- ┆ --- │
# # │ date ┆ i64 ┆ date │
# # ╞════════════╪═════════╪════════════╡
# # │ 2024-04-01 ┆ 10 ┆ 1800-04-01 │
# # │ 2025-03-16 ┆ 15 ┆ 1800-03-16 │
# # └────────────┴─────────┴────────────┘
def replace(
year: nil,
month: nil,
day: nil,
hour: nil,
minute: nil,
second: nil,
microsecond: nil,
ambiguous: "raise"
)
day, month, year, hour, minute, second, microsecond = (
Utils.parse_into_list_of_expressions(
day, month, year, hour, minute, second, microsecond
)
)
ambiguous_expr = Utils.parse_into_expression(ambiguous, str_as_lit: true)
Utils.wrap_expr(
_rbexpr.dt_replace(
year,
month,
day,
hour,
minute,
second,
microsecond,
ambiguous_expr
)
)
end
# Create a naive Datetime from an existing Date/Datetime expression and a Time.
#
# If the underlying expression is a Datetime then its time component is replaced,
# and if it is a Date then a new Datetime is created by combining the two values.
#
# @param time [Object]
# A Ruby time literal or Polars expression/column that resolves to a time.
# @param time_unit ["ns", "us", "ms"]
# Unit of time.
#
# @return [Expr]
def combine(time, time_unit: "us")
unless time.is_a?(::Time) || time.is_a?(Expr)
raise TypeError, "expected 'time' to be a Ruby time or Polars expression, found #{time}"
end
time = Utils.parse_into_expression(time)
Utils.wrap_expr(_rbexpr.dt_combine(time, time_unit))
end
# Convert a Date/Time/Datetime column into a String column with the given format.
#
# Similar to `cast(Polars::String)`, but this method allows you to customize the
# formatting of the resulting string.
#
# @param format [String]
# Format to use, refer to the `chrono strftime documentation
# <https://docs.rs/chrono/latest/chrono/format/strftime/index.html>`_
# for specification. Example: `"%y-%m-%d"`.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime" => [
# DateTime.new(2020, 3, 1),
# DateTime.new(2020, 4, 1),
# DateTime.new(2020, 5, 1)
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime")
# .dt.to_string("%Y/%m/%d %H:%M:%S")
# .alias("datetime_string")
# )
# # =>
# # shape: (3, 2)
# # ┌─────────────────────┬─────────────────────┐
# # │ datetime ┆ datetime_string │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ str │
# # ╞═════════════════════╪═════════════════════╡
# # │ 2020-03-01 00:00:00 ┆ 2020/03/01 00:00:00 │
# # │ 2020-04-01 00:00:00 ┆ 2020/04/01 00:00:00 │
# # │ 2020-05-01 00:00:00 ┆ 2020/05/01 00:00:00 │
# # └─────────────────────┴─────────────────────┘
def to_string(format = nil)
if format.nil?
format = "iso"
end
Utils.wrap_expr(_rbexpr.dt_to_string(format))
end
# Convert a Date/Time/Datetime column into a String column with the given format.
#
# Similar to `cast(Polars::String)`, but this method allows you to customize the
# formatting of the resulting string.
#
# @param format [String]
# Format to use, refer to the [chrono strftime documentation](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
# for specification. Example: `"%y-%m-%d"`.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime" => [
# Time.utc(2020, 3, 1),
# Time.utc(2020, 4, 1),
# Time.utc(2020, 5, 1)
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime")
# .dt.strftime("%Y/%m/%d %H:%M:%S")
# .alias("datetime_string")
# )
# # =>
# # shape: (3, 2)
# # ┌─────────────────────┬─────────────────────┐
# # │ datetime ┆ datetime_string │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ str │
# # ╞═════════════════════╪═════════════════════╡
# # │ 2020-03-01 00:00:00 ┆ 2020/03/01 00:00:00 │
# # │ 2020-04-01 00:00:00 ┆ 2020/04/01 00:00:00 │
# # │ 2020-05-01 00:00:00 ┆ 2020/05/01 00:00:00 │
# # └─────────────────────┴─────────────────────┘
#
# @example If you're interested in the day name / month name, you can use `'%A'` / `'%B'`:
# df.with_columns(
# day_name: Polars.col("datetime").dt.strftime("%A"),
# month_name: Polars.col("datetime").dt.strftime("%B")
# )
# # =>
# # shape: (3, 3)
# # ┌─────────────────────┬───────────┬────────────┐
# # │ datetime ┆ day_name ┆ month_name │
# # │ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ str ┆ str │
# # ╞═════════════════════╪═══════════╪════════════╡
# # │ 2020-03-01 00:00:00 ┆ Sunday ┆ March │
# # │ 2020-04-01 00:00:00 ┆ Wednesday ┆ April │
# # │ 2020-05-01 00:00:00 ┆ Friday ┆ May │
# # └─────────────────────┴───────────┴────────────┘
def strftime(format)
Utils.wrap_expr(_rbexpr.strftime(format))
end
# Extract the millennium from underlying representation.
#
# Applies to Date and Datetime columns.
#
# Returns the millennium number in the calendar date.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => [
# Date.new(999, 12, 31),
# Date.new(1897, 5, 7),
# Date.new(2000, 1, 1),
# Date.new(2001, 7, 5),
# Date.new(3002, 10, 20)
# ]
# }
# )
# df.with_columns(mlnm: Polars.col("date").dt.millennium)
# # =>
# # shape: (5, 2)
# # ┌────────────┬──────┐
# # │ date ┆ mlnm │
# # │ --- ┆ --- │
# # │ date ┆ i32 │
# # ╞════════════╪══════╡
# # │ 0999-12-31 ┆ 1 │
# # │ 1897-05-07 ┆ 2 │
# # │ 2000-01-01 ┆ 2 │
# # │ 2001-07-05 ┆ 3 │
# # │ 3002-10-20 ┆ 4 │
# # └────────────┴──────┘
def millennium
Utils.wrap_expr(_rbexpr.dt_millennium)
end
# Extract the century from underlying representation.
#
# Applies to Date and Datetime columns.
#
# Returns the century number in the calendar date.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => [
# Date.new(999, 12, 31),
# Date.new(1897, 5, 7),
# Date.new(2000, 1, 1),
# Date.new(2001, 7, 5),
# Date.new(3002, 10, 20)
# ]
# }
# )
# df.with_columns(cent: Polars.col("date").dt.century)
# # =>
# # shape: (5, 2)
# # ┌────────────┬──────┐
# # │ date ┆ cent │
# # │ --- ┆ --- │
# # │ date ┆ i32 │
# # ╞════════════╪══════╡
# # │ 0999-12-31 ┆ 10 │
# # │ 1897-05-07 ┆ 19 │
# # │ 2000-01-01 ┆ 20 │
# # │ 2001-07-05 ┆ 21 │
# # │ 3002-10-20 ┆ 31 │
# # └────────────┴──────┘
def century
Utils.wrap_expr(_rbexpr.dt_century)
end
# Extract year from underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the year number in the calendar date.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(1977, 1, 1), Date.new(1978, 1, 1), Date.new(1979, 1, 1)]}
# )
# df.with_columns(
# calendar_year: Polars.col("date").dt.year,
# iso_year: Polars.col("date").dt.iso_year
# )
# # =>
# # shape: (3, 3)
# # ┌────────────┬───────────────┬──────────┐
# # │ date ┆ calendar_year ┆ iso_year │
# # │ --- ┆ --- ┆ --- │
# # │ date ┆ i32 ┆ i32 │
# # ╞════════════╪═══════════════╪══════════╡
# # │ 1977-01-01 ┆ 1977 ┆ 1976 │
# # │ 1978-01-01 ┆ 1978 ┆ 1977 │
# # │ 1979-01-01 ┆ 1979 ┆ 1979 │
# # └────────────┴───────────────┴──────────┘
def year
Utils.wrap_expr(_rbexpr.dt_year)
end
# Determine whether each day lands on a business day.
#
# @note
# This functionality is considered **unstable**. It may be changed
# at any point without it being considered a breaking change.
#
# @param week_mask [Array]
# Which days of the week to count. The default is Monday to Friday.
# If you wanted to count only Monday to Thursday, you would pass
# `[true, true, true, true, false, false, false]`.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new({"start" => [Date.new(2020, 1, 3), Date.new(2020, 1, 5)]})
# df.with_columns(is_business_day: Polars.col("start").dt.is_business_day)
# # =>
# # shape: (2, 2)
# # ┌────────────┬─────────────────┐
# # │ start ┆ is_business_day │
# # │ --- ┆ --- │
# # │ date ┆ bool │
# # ╞════════════╪═════════════════╡
# # │ 2020-01-03 ┆ true │
# # │ 2020-01-05 ┆ false │
# # └────────────┴─────────────────┘
def is_business_day(
week_mask: [true, true, true, true, true, false, false]
)
Utils.wrap_expr(
_rbexpr.dt_is_business_day(
week_mask,
[]
)
)
end
# Determine whether the year of the underlying date is a leap year.
#
# Applies to Date and Datetime columns.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(2000, 1, 1), Date.new(2001, 1, 1), Date.new(2002, 1, 1)]}
# )
# df.with_columns(
# leap_year: Polars.col("date").dt.is_leap_year
# )
# # =>
# # shape: (3, 2)
# # ┌────────────┬───────────┐
# # │ date ┆ leap_year │
# # │ --- ┆ --- │
# # │ date ┆ bool │
# # ╞════════════╪═══════════╡
# # │ 2000-01-01 ┆ true │
# # │ 2001-01-01 ┆ false │
# # │ 2002-01-01 ┆ false │
# # └────────────┴───────────┘
def is_leap_year
Utils.wrap_expr(_rbexpr.dt_is_leap_year)
end
# Extract ISO year from underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the year number in the ISO standard.
# This may not correspond with the calendar year.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(1977, 1, 1), Date.new(1978, 1, 1), Date.new(1979, 1, 1)]}
# )
# df.select(
# "date",
# Polars.col("date").dt.year.alias("calendar_year"),
# Polars.col("date").dt.iso_year.alias("iso_year")
# )
# # =>
# # shape: (3, 3)
# # ┌────────────┬───────────────┬──────────┐
# # │ date ┆ calendar_year ┆ iso_year │
# # │ --- ┆ --- ┆ --- │
# # │ date ┆ i32 ┆ i32 │
# # ╞════════════╪═══════════════╪══════════╡
# # │ 1977-01-01 ┆ 1977 ┆ 1976 │
# # │ 1978-01-01 ┆ 1978 ┆ 1977 │
# # │ 1979-01-01 ┆ 1979 ┆ 1979 │
# # └────────────┴───────────────┴──────────┘
def iso_year
Utils.wrap_expr(_rbexpr.dt_iso_year)
end
# Extract quarter from underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the quarter ranging from 1 to 4.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(2001, 1, 1), Date.new(2001, 6, 30), Date.new(2001, 12, 27)]}
# )
# df.with_columns(Polars.col("date").dt.quarter.alias("quarter"))
# # =>
# # shape: (3, 2)
# # ┌────────────┬─────────┐
# # │ date ┆ quarter │
# # │ --- ┆ --- │
# # │ date ┆ i8 │
# # ╞════════════╪═════════╡
# # │ 2001-01-01 ┆ 1 │
# # │ 2001-06-30 ┆ 2 │
# # │ 2001-12-27 ┆ 4 │
# # └────────────┴─────────┘
def quarter
Utils.wrap_expr(_rbexpr.dt_quarter)
end
# Extract month from underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the month number starting from 1.
# The return value ranges from 1 to 12.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(2001, 1, 1), Date.new(2001, 6, 30), Date.new(2001, 12, 27)]}
# )
# df.with_columns(Polars.col("date").dt.month.alias("month"))
# # =>
# # shape: (3, 2)
# # ┌────────────┬───────┐
# # │ date ┆ month │
# # │ --- ┆ --- │
# # │ date ┆ i8 │
# # ╞════════════╪═══════╡
# # │ 2001-01-01 ┆ 1 │
# # │ 2001-06-30 ┆ 6 │
# # │ 2001-12-27 ┆ 12 │
# # └────────────┴───────┘
def month
Utils.wrap_expr(_rbexpr.dt_month)
end
# Extract the number of days in the month from the underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the number of days in the month.
# The return value ranges from 28 to 31.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(2001, 1, 1), Date.new(2001, 2, 1), Date.new(2000, 2, 1)]}
# )
# df.with_columns(Polars.col("date").dt.days_in_month.alias("days_in_month"))
# # =>
# # shape: (3, 2)
# # ┌────────────┬───────────────┐
# # │ date ┆ days_in_month │
# # │ --- ┆ --- │
# # │ date ┆ i8 │
# # ╞════════════╪═══════════════╡
# # │ 2001-01-01 ┆ 31 │
# # │ 2001-02-01 ┆ 28 │
# # │ 2000-02-01 ┆ 29 │
# # └────────────┴───────────────┘
def days_in_month
Utils.wrap_expr(_rbexpr.dt_days_in_month)
end
# Extract the week from the underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the ISO week number starting from 1.
# The return value ranges from 1 to 53. (The last week of year differs by years.)
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {"date" => [Date.new(2001, 1, 1), Date.new(2001, 6, 30), Date.new(2001, 12, 27)]}
# )
# df.with_columns(Polars.col("date").dt.week.alias("week"))
# # =>
# # shape: (3, 2)
# # ┌────────────┬──────┐
# # │ date ┆ week │
# # │ --- ┆ --- │
# # │ date ┆ i8 │
# # ╞════════════╪══════╡
# # │ 2001-01-01 ┆ 1 │
# # │ 2001-06-30 ┆ 26 │
# # │ 2001-12-27 ┆ 52 │
# # └────────────┴──────┘
def week
Utils.wrap_expr(_rbexpr.dt_week)
end
# Extract the week day from the underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the ISO weekday number where monday = 1 and sunday = 7
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.date_range(
# Date.new(2001, 12, 22), Date.new(2001, 12, 25), eager: true
# )
# }
# )
# df.with_columns(
# Polars.col("date").dt.weekday.alias("weekday"),
# Polars.col("date").dt.day.alias("day_of_month"),
# Polars.col("date").dt.ordinal_day.alias("day_of_year")
# )
# # =>
# # shape: (4, 4)
# # ┌────────────┬─────────┬──────────────┬─────────────┐
# # │ date ┆ weekday ┆ day_of_month ┆ day_of_year │
# # │ --- ┆ --- ┆ --- ┆ --- │
# # │ date ┆ i8 ┆ i8 ┆ i16 │
# # ╞════════════╪═════════╪══════════════╪═════════════╡
# # │ 2001-12-22 ┆ 6 ┆ 22 ┆ 356 │
# # │ 2001-12-23 ┆ 7 ┆ 23 ┆ 357 │
# # │ 2001-12-24 ┆ 1 ┆ 24 ┆ 358 │
# # │ 2001-12-25 ┆ 2 ┆ 25 ┆ 359 │
# # └────────────┴─────────┴──────────────┴─────────────┘
def weekday
Utils.wrap_expr(_rbexpr.dt_weekday)
end
# Extract day from underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the day of month starting from 1.
# The return value ranges from 1 to 31. (The last day of month differs by months.)
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.date_range(
# Date.new(2001, 12, 22), Date.new(2001, 12, 25), eager: true
# )
# }
# )
# df.with_columns(
# Polars.col("date").dt.weekday.alias("weekday"),
# Polars.col("date").dt.day.alias("day_of_month"),
# Polars.col("date").dt.ordinal_day.alias("day_of_year")
# )
# # =>
# # shape: (4, 4)
# # ┌────────────┬─────────┬──────────────┬─────────────┐
# # │ date ┆ weekday ┆ day_of_month ┆ day_of_year │
# # │ --- ┆ --- ┆ --- ┆ --- │
# # │ date ┆ i8 ┆ i8 ┆ i16 │
# # ╞════════════╪═════════╪══════════════╪═════════════╡
# # │ 2001-12-22 ┆ 6 ┆ 22 ┆ 356 │
# # │ 2001-12-23 ┆ 7 ┆ 23 ┆ 357 │
# # │ 2001-12-24 ┆ 1 ┆ 24 ┆ 358 │
# # │ 2001-12-25 ┆ 2 ┆ 25 ┆ 359 │
# # └────────────┴─────────┴──────────────┴─────────────┘
def day
Utils.wrap_expr(_rbexpr.dt_day)
end
# Extract ordinal day from underlying Date representation.
#
# Applies to Date and Datetime columns.
#
# Returns the day of month starting from 1.
# The return value ranges from 1 to 31. (The last day of month differs by months.)
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.date_range(
# Date.new(2001, 12, 22), Date.new(2001, 12, 25), eager: true
# )
# }
# )
# df.with_columns(
# Polars.col("date").dt.weekday.alias("weekday"),
# Polars.col("date").dt.day.alias("day_of_month"),
# Polars.col("date").dt.ordinal_day.alias("day_of_year")
# )
# # =>
# # shape: (4, 4)
# # ┌────────────┬─────────┬──────────────┬─────────────┐
# # │ date ┆ weekday ┆ day_of_month ┆ day_of_year │
# # │ --- ┆ --- ┆ --- ┆ --- │
# # │ date ┆ i8 ┆ i8 ┆ i16 │
# # ╞════════════╪═════════╪══════════════╪═════════════╡
# # │ 2001-12-22 ┆ 6 ┆ 22 ┆ 356 │
# # │ 2001-12-23 ┆ 7 ┆ 23 ┆ 357 │
# # │ 2001-12-24 ┆ 1 ┆ 24 ┆ 358 │
# # │ 2001-12-25 ┆ 2 ┆ 25 ┆ 359 │
# # └────────────┴─────────┴──────────────┴─────────────┘
def ordinal_day
Utils.wrap_expr(_rbexpr.dt_ordinal_day)
end
# Time
#
# @return [Expr]
def time
Utils.wrap_expr(_rbexpr.dt_time)
end
# Date
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime" => [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000)
# ]
# }
# )
# df.with_columns(Polars.col("datetime").dt.date.alias("date"))
# # =>
# # shape: (3, 2)
# # ┌─────────────────────────┬────────────┐
# # │ datetime ┆ date │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ date │
# # ╞═════════════════════════╪════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1978-01-01 │
# # │ 2024-10-13 05:30:14.500 ┆ 2024-10-13 │
# # │ 2065-01-01 10:20:30.060 ┆ 2065-01-01 │
# # └─────────────────────────┴────────────┘
def date
Utils.wrap_expr(_rbexpr.dt_date)
end
# Extract hour from underlying DateTime representation.
#
# Applies to Datetime columns.
#
# Returns the hour number from 0 to 23.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime" => [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000)
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second.alias("second"),
# Polars.col("datetime").dt.millisecond.alias("millisecond")
# )
# # =>
# # shape: (3, 5)
# # ┌─────────────────────────┬──────┬────────┬────────┬─────────────┐
# # │ datetime ┆ hour ┆ minute ┆ second ┆ millisecond │
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ i8 ┆ i32 │
# # ╞═════════════════════════╪══════╪════════╪════════╪═════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1 ┆ 0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14 ┆ 500 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30 ┆ 60 │
# # └─────────────────────────┴──────┴────────┴────────┴─────────────┘
def hour
Utils.wrap_expr(_rbexpr.dt_hour)
end
# Extract minutes from underlying DateTime representation.
#
# Applies to Datetime columns.
#
# Returns the minute number from 0 to 59.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime" => [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000)
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second.alias("second"),
# Polars.col("datetime").dt.millisecond.alias("millisecond")
# )
# # =>
# # shape: (3, 5)
# # ┌─────────────────────────┬──────┬────────┬────────┬─────────────┐
# # │ datetime ┆ hour ┆ minute ┆ second ┆ millisecond │
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ i8 ┆ i32 │
# # ╞═════════════════════════╪══════╪════════╪════════╪═════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1 ┆ 0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14 ┆ 500 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30 ┆ 60 │
# # └─────────────────────────┴──────┴────────┴────────┴─────────────┘
def minute
Utils.wrap_expr(_rbexpr.dt_minute)
end
# Extract seconds from underlying DateTime representation.
#
# Applies to Datetime columns.
#
# Returns the integer second number from 0 to 59, or a floating
# point number from 0 < 60 if `fractional: true` that includes
# any milli/micro/nanosecond component.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime" => [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000)
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second.alias("second"),
# Polars.col("datetime").dt.millisecond.alias("millisecond")
# )
# # =>
# # shape: (3, 5)
# # ┌─────────────────────────┬──────┬────────┬────────┬─────────────┐
# # │ datetime ┆ hour ┆ minute ┆ second ┆ millisecond │
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ i8 ┆ i32 │
# # ╞═════════════════════════╪══════╪════════╪════════╪═════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1 ┆ 0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14 ┆ 500 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30 ┆ 60 │
# # └─────────────────────────┴──────┴────────┴────────┴─────────────┘
#
# @example
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second(fractional: true).alias("second")
# )
# # =>
# # shape: (3, 4)
# # ┌─────────────────────────┬──────┬────────┬────────┐
# # │ datetime ┆ hour ┆ minute ┆ second │
# # │ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ f64 │
# # ╞═════════════════════════╪══════╪════════╪════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1.0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14.5 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30.06 │
# # └─────────────────────────┴──────┴────────┴────────┘
def second(fractional: false)
sec = Utils.wrap_expr(_rbexpr.dt_second)
if fractional
sec + (Utils.wrap_expr(_rbexpr.dt_nanosecond) / F.lit(1_000_000_000.0))
else
sec
end
end
# Extract milliseconds from underlying DateTime representation.
#
# Applies to Datetime columns.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime": [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000),
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second.alias("second"),
# Polars.col("datetime").dt.millisecond.alias("millisecond")
# )
# # =>
# # shape: (3, 5)
# # ┌─────────────────────────┬──────┬────────┬────────┬─────────────┐
# # │ datetime ┆ hour ┆ minute ┆ second ┆ millisecond │
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ i8 ┆ i32 │
# # ╞═════════════════════════╪══════╪════════╪════════╪═════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1 ┆ 0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14 ┆ 500 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30 ┆ 60 │
# # └─────────────────────────┴──────┴────────┴────────┴─────────────┘
def millisecond
Utils.wrap_expr(_rbexpr.dt_millisecond)
end
# Extract microseconds from underlying DateTime representation.
#
# Applies to Datetime columns.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime": [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000),
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second.alias("second"),
# Polars.col("datetime").dt.microsecond.alias("microsecond")
# )
# # =>
# # shape: (3, 5)
# # ┌─────────────────────────┬──────┬────────┬────────┬─────────────┐
# # │ datetime ┆ hour ┆ minute ┆ second ┆ microsecond │
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ i8 ┆ i32 │
# # ╞═════════════════════════╪══════╪════════╪════════╪═════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1 ┆ 0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14 ┆ 500000 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30 ┆ 60000 │
# # └─────────────────────────┴──────┴────────┴────────┴─────────────┘
def microsecond
Utils.wrap_expr(_rbexpr.dt_microsecond)
end
# Extract nanoseconds from underlying DateTime representation.
#
# Applies to Datetime columns.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "datetime": [
# Time.utc(1978, 1, 1, 1, 1, 1, 0),
# Time.utc(2024, 10, 13, 5, 30, 14, 500_000),
# Time.utc(2065, 1, 1, 10, 20, 30, 60_000),
# ]
# }
# )
# df.with_columns(
# Polars.col("datetime").dt.hour.alias("hour"),
# Polars.col("datetime").dt.minute.alias("minute"),
# Polars.col("datetime").dt.second.alias("second"),
# Polars.col("datetime").dt.nanosecond.alias("nanosecond")
# )
# # =>
# # shape: (3, 5)
# # ┌─────────────────────────┬──────┬────────┬────────┬────────────┐
# # │ datetime ┆ hour ┆ minute ┆ second ┆ nanosecond │
# # │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ i8 ┆ i8 ┆ i8 ┆ i32 │
# # ╞═════════════════════════╪══════╪════════╪════════╪════════════╡
# # │ 1978-01-01 01:01:01 ┆ 1 ┆ 1 ┆ 1 ┆ 0 │
# # │ 2024-10-13 05:30:14.500 ┆ 5 ┆ 30 ┆ 14 ┆ 500000000 │
# # │ 2065-01-01 10:20:30.060 ┆ 10 ┆ 20 ┆ 30 ┆ 60000000 │
# # └─────────────────────────┴──────┴────────┴────────┴────────────┘
def nanosecond
Utils.wrap_expr(_rbexpr.dt_nanosecond)
end
# Get the time passed since the Unix EPOCH in the give time unit.
#
# @param time_unit ["us", "ns", "ms", "s", "d"]
# Time unit.
#
# @return [Expr]
#
# @example
# df = (
# Polars.date_range(Date.new(2001, 1, 1), Date.new(2001, 1, 3), eager: true)
# .alias("date")
# .to_frame
# )
# df.with_columns(
# Polars.col("date").dt.epoch.alias("epoch_ns"),
# Polars.col("date").dt.epoch("s").alias("epoch_s")
# )
# # =>
# # shape: (3, 3)
# # ┌────────────┬─────────────────┬───────────┐
# # │ date ┆ epoch_ns ┆ epoch_s │
# # │ --- ┆ --- ┆ --- │
# # │ date ┆ i64 ┆ i64 │
# # ╞════════════╪═════════════════╪═══════════╡
# # │ 2001-01-01 ┆ 978307200000000 ┆ 978307200 │
# # │ 2001-01-02 ┆ 978393600000000 ┆ 978393600 │
# # │ 2001-01-03 ┆ 978480000000000 ┆ 978480000 │
# # └────────────┴─────────────────┴───────────┘
def epoch(time_unit = "us")
if Utils::DTYPE_TEMPORAL_UNITS.include?(time_unit)
timestamp(time_unit)
elsif time_unit == "s"
timestamp("ms").floordiv(F.lit(1000, dtype: Int64))
elsif time_unit == "d"
Utils.wrap_expr(_rbexpr).cast(Date).cast(Int32)
else
raise ArgumentError, "time_unit must be one of {'ns', 'us', 'ms', 's', 'd'}, got #{time_unit.inspect}"
end
end
# Return a timestamp in the given time unit.
#
# @param time_unit ["us", "ns", "ms"]
# Time unit.
#
# @return [Expr]
#
# @example
# df = (
# Polars.date_range(Date.new(2001, 1, 1), Date.new(2001, 1, 3), eager: true)
# .alias("date")
# .to_frame
# )
# df.with_columns(
# Polars.col("date").dt.timestamp.alias("timestamp_us"),
# Polars.col("date").dt.timestamp("ms").alias("timestamp_ms")
# )
# # =>
# # shape: (3, 3)
# # ┌────────────┬─────────────────┬──────────────┐
# # │ date ┆ timestamp_us ┆ timestamp_ms │
# # │ --- ┆ --- ┆ --- │
# # │ date ┆ i64 ┆ i64 │
# # ╞════════════╪═════════════════╪══════════════╡
# # │ 2001-01-01 ┆ 978307200000000 ┆ 978307200000 │
# # │ 2001-01-02 ┆ 978393600000000 ┆ 978393600000 │
# # │ 2001-01-03 ┆ 978480000000000 ┆ 978480000000 │
# # └────────────┴─────────────────┴──────────────┘
def timestamp(time_unit = "us")
Utils.wrap_expr(_rbexpr.dt_timestamp(time_unit))
end
# Cast the underlying data to another time unit. This may lose precision.
#
# @param time_unit ["ns", "us", "ms"]
# Time unit for the `Datetime` Series.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2001, 1, 1), DateTime.new(2001, 1, 3), "1d", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").dt.cast_time_unit("ms").alias("tu_ms"),
# Polars.col("date").dt.cast_time_unit("ns").alias("tu_ns")
# ]
# )
# # =>
# # shape: (3, 3)
# # ┌─────────────────────┬─────────────────────┬─────────────────────┐
# # │ date ┆ tu_ms ┆ tu_ns │
# # │ --- ┆ --- ┆ --- │
# # │ datetime[ns] ┆ datetime[ms] ┆ datetime[ns] │
# # ╞═════════════════════╪═════════════════════╪═════════════════════╡
# # │ 2001-01-01 00:00:00 ┆ 2001-01-01 00:00:00 ┆ 2001-01-01 00:00:00 │
# # │ 2001-01-02 00:00:00 ┆ 2001-01-02 00:00:00 ┆ 2001-01-02 00:00:00 │
# # │ 2001-01-03 00:00:00 ┆ 2001-01-03 00:00:00 ┆ 2001-01-03 00:00:00 │
# # └─────────────────────┴─────────────────────┴─────────────────────┘
def cast_time_unit(time_unit)
Utils.wrap_expr(_rbexpr.dt_cast_time_unit(time_unit))
end
# Set time zone for a Series of type Datetime.
#
# @param time_zone [String]
# Time zone for the `Datetime` Series.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 3, 1),
# DateTime.new(2020, 5, 1),
# "1mo",
# time_zone: "UTC",
# eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date")
# .dt.convert_time_zone("Europe/London")
# .alias("London")
# ]
# )
# # =>
# # shape: (3, 2)
# # ┌─────────────────────────┬─────────────────────────────┐
# # │ date ┆ London │
# # │ --- ┆ --- │
# # │ datetime[ns, UTC] ┆ datetime[ns, Europe/London] │
# # ╞═════════════════════════╪═════════════════════════════╡
# # │ 2020-03-01 00:00:00 UTC ┆ 2020-03-01 00:00:00 GMT │
# # │ 2020-04-01 00:00:00 UTC ┆ 2020-04-01 01:00:00 BST │
# # │ 2020-05-01 00:00:00 UTC ┆ 2020-05-01 01:00:00 BST │
# # └─────────────────────────┴─────────────────────────────┘
def convert_time_zone(time_zone)
Utils.wrap_expr(_rbexpr.dt_convert_time_zone(time_zone))
end
# Cast time zone for a Series of type Datetime.
#
# Different from `convert_time_zone`, this will also modify
# the underlying timestamp,
#
# @param time_zone [String]
# Time zone for the `Datetime` Series. Pass `nil` to unset time zone.
# @param ambiguous [String]
# Determine how to deal with ambiguous datetimes.
# @param non_existent [String]
# Determine how to deal with non-existent datetimes.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "london_timezone": Polars.datetime_range(
# Time.utc(2020, 3, 1),
# Time.utc(2020, 7, 1),
# "1mo",
# time_zone: "UTC",
# eager: true,
# ).dt.convert_time_zone("Europe/London")
# }
# )
# df.select(
# [
# Polars.col("london_timezone"),
# Polars.col("london_timezone")
# .dt.replace_time_zone("Europe/Amsterdam")
# .alias("London_to_Amsterdam")
# ]
# )
# # =>
# # shape: (5, 2)
# # ┌─────────────────────────────┬────────────────────────────────┐
# # │ london_timezone ┆ London_to_Amsterdam │
# # │ --- ┆ --- │
# # │ datetime[ns, Europe/London] ┆ datetime[ns, Europe/Amsterdam] │
# # ╞═════════════════════════════╪════════════════════════════════╡
# # │ 2020-03-01 00:00:00 GMT ┆ 2020-03-01 00:00:00 CET │
# # │ 2020-04-01 01:00:00 BST ┆ 2020-04-01 01:00:00 CEST │
# # │ 2020-05-01 01:00:00 BST ┆ 2020-05-01 01:00:00 CEST │
# # │ 2020-06-01 01:00:00 BST ┆ 2020-06-01 01:00:00 CEST │
# # │ 2020-07-01 01:00:00 BST ┆ 2020-07-01 01:00:00 CEST │
# # └─────────────────────────────┴────────────────────────────────┘
#
# @example You can use `ambiguous` to deal with ambiguous datetimes:
# dates = [
# "2018-10-28 01:30",
# "2018-10-28 02:00",
# "2018-10-28 02:30",
# "2018-10-28 02:00"
# ]
# df = Polars::DataFrame.new(
# {
# "ts" => Polars::Series.new(dates).str.strptime(Polars::Datetime),
# "ambiguous" => ["earliest", "earliest", "latest", "latest"]
# }
# )
# df.with_columns(
# ts_localized: Polars.col("ts").dt.replace_time_zone(
# "Europe/Brussels", ambiguous: Polars.col("ambiguous")
# )
# )
# # =>
# # shape: (4, 3)
# # ┌─────────────────────┬───────────┬───────────────────────────────┐
# # │ ts ┆ ambiguous ┆ ts_localized │
# # │ --- ┆ --- ┆ --- │
# # │ datetime[μs] ┆ str ┆ datetime[μs, Europe/Brussels] │
# # ╞═════════════════════╪═══════════╪═══════════════════════════════╡
# # │ 2018-10-28 01:30:00 ┆ earliest ┆ 2018-10-28 01:30:00 CEST │
# # │ 2018-10-28 02:00:00 ┆ earliest ┆ 2018-10-28 02:00:00 CEST │
# # │ 2018-10-28 02:30:00 ┆ latest ┆ 2018-10-28 02:30:00 CET │
# # │ 2018-10-28 02:00:00 ┆ latest ┆ 2018-10-28 02:00:00 CET │
# # └─────────────────────┴───────────┴───────────────────────────────┘
def replace_time_zone(time_zone, ambiguous: "raise", non_existent: "raise")
unless ambiguous.is_a?(Expr)
ambiguous = Polars.lit(ambiguous)
end
Utils.wrap_expr(_rbexpr.dt_replace_time_zone(time_zone, ambiguous._rbexpr, non_existent))
end
# Extract the days from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 3, 1), DateTime.new(2020, 5, 1), "1mo", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_days.alias("days_diff")
# ]
# )
# # =>
# # shape: (3, 2)
# # ┌─────────────────────┬───────────┐
# # │ date ┆ days_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════╪═══════════╡
# # │ 2020-03-01 00:00:00 ┆ null │
# # │ 2020-04-01 00:00:00 ┆ 31 │
# # │ 2020-05-01 00:00:00 ┆ 30 │
# # └─────────────────────┴───────────┘
def total_days(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_days(fractional))
end
# Extract the hours from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 1, 1), DateTime.new(2020, 1, 4), "1d", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_hours.alias("hours_diff")
# ]
# )
# # =>
# # shape: (4, 2)
# # ┌─────────────────────┬────────────┐
# # │ date ┆ hours_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════╪════════════╡
# # │ 2020-01-01 00:00:00 ┆ null │
# # │ 2020-01-02 00:00:00 ┆ 24 │
# # │ 2020-01-03 00:00:00 ┆ 24 │
# # │ 2020-01-04 00:00:00 ┆ 24 │
# # └─────────────────────┴────────────┘
def total_hours(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_hours(fractional))
end
# Extract the minutes from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 1, 1), DateTime.new(2020, 1, 4), "1d", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_minutes.alias("minutes_diff")
# ]
# )
# # =>
# # shape: (4, 2)
# # ┌─────────────────────┬──────────────┐
# # │ date ┆ minutes_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════╪══════════════╡
# # │ 2020-01-01 00:00:00 ┆ null │
# # │ 2020-01-02 00:00:00 ┆ 1440 │
# # │ 2020-01-03 00:00:00 ┆ 1440 │
# # │ 2020-01-04 00:00:00 ┆ 1440 │
# # └─────────────────────┴──────────────┘
def total_minutes(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_minutes(fractional))
end
# Extract the seconds from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 1, 1), DateTime.new(2020, 1, 1, 0, 4, 0), "1m", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_seconds.alias("seconds_diff")
# ]
# )
# # =>
# # shape: (5, 2)
# # ┌─────────────────────┬──────────────┐
# # │ date ┆ seconds_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════╪══════════════╡
# # │ 2020-01-01 00:00:00 ┆ null │
# # │ 2020-01-01 00:01:00 ┆ 60 │
# # │ 2020-01-01 00:02:00 ┆ 60 │
# # │ 2020-01-01 00:03:00 ┆ 60 │
# # │ 2020-01-01 00:04:00 ┆ 60 │
# # └─────────────────────┴──────────────┘
def total_seconds(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_seconds(fractional))
end
# Extract the milliseconds from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 1, 1), DateTime.new(2020, 1, 1, 0, 0, 1), "1ms", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_milliseconds.alias("milliseconds_diff")
# ]
# )
# # =>
# # shape: (1_001, 2)
# # ┌─────────────────────────┬───────────────────┐
# # │ date ┆ milliseconds_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════════╪═══════════════════╡
# # │ 2020-01-01 00:00:00 ┆ null │
# # │ 2020-01-01 00:00:00.001 ┆ 1 │
# # │ 2020-01-01 00:00:00.002 ┆ 1 │
# # │ 2020-01-01 00:00:00.003 ┆ 1 │
# # │ 2020-01-01 00:00:00.004 ┆ 1 │
# # │ … ┆ … │
# # │ 2020-01-01 00:00:00.996 ┆ 1 │
# # │ 2020-01-01 00:00:00.997 ┆ 1 │
# # │ 2020-01-01 00:00:00.998 ┆ 1 │
# # │ 2020-01-01 00:00:00.999 ┆ 1 │
# # │ 2020-01-01 00:00:01 ┆ 1 │
# # └─────────────────────────┴───────────────────┘
def total_milliseconds(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_milliseconds(fractional))
end
# Extract the microseconds from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 1, 1), DateTime.new(2020, 1, 1, 0, 0, 1), "1ms", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_microseconds.alias("microseconds_diff")
# ]
# )
# # =>
# # shape: (1_001, 2)
# # ┌─────────────────────────┬───────────────────┐
# # │ date ┆ microseconds_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════════╪═══════════════════╡
# # │ 2020-01-01 00:00:00 ┆ null │
# # │ 2020-01-01 00:00:00.001 ┆ 1000 │
# # │ 2020-01-01 00:00:00.002 ┆ 1000 │
# # │ 2020-01-01 00:00:00.003 ┆ 1000 │
# # │ 2020-01-01 00:00:00.004 ┆ 1000 │
# # │ … ┆ … │
# # │ 2020-01-01 00:00:00.996 ┆ 1000 │
# # │ 2020-01-01 00:00:00.997 ┆ 1000 │
# # │ 2020-01-01 00:00:00.998 ┆ 1000 │
# # │ 2020-01-01 00:00:00.999 ┆ 1000 │
# # │ 2020-01-01 00:00:01 ┆ 1000 │
# # └─────────────────────────┴───────────────────┘
def total_microseconds(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_microseconds(fractional))
end
# Extract the nanoseconds from a Duration type.
#
# @param fractional [Boolean]
# Whether to include the fractional component of the second.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "date" => Polars.datetime_range(
# DateTime.new(2020, 1, 1), DateTime.new(2020, 1, 1, 0, 0, 1), "1ms", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("date"),
# Polars.col("date").diff.dt.total_nanoseconds.alias("nanoseconds_diff")
# ]
# )
# # =>
# # shape: (1_001, 2)
# # ┌─────────────────────────┬──────────────────┐
# # │ date ┆ nanoseconds_diff │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ i64 │
# # ╞═════════════════════════╪══════════════════╡
# # │ 2020-01-01 00:00:00 ┆ null │
# # │ 2020-01-01 00:00:00.001 ┆ 1000000 │
# # │ 2020-01-01 00:00:00.002 ┆ 1000000 │
# # │ 2020-01-01 00:00:00.003 ┆ 1000000 │
# # │ 2020-01-01 00:00:00.004 ┆ 1000000 │
# # │ … ┆ … │
# # │ 2020-01-01 00:00:00.996 ┆ 1000000 │
# # │ 2020-01-01 00:00:00.997 ┆ 1000000 │
# # │ 2020-01-01 00:00:00.998 ┆ 1000000 │
# # │ 2020-01-01 00:00:00.999 ┆ 1000000 │
# # │ 2020-01-01 00:00:01 ┆ 1000000 │
# # └─────────────────────────┴──────────────────┘
def total_nanoseconds(fractional: false)
Utils.wrap_expr(_rbexpr.dt_total_nanoseconds(fractional))
end
# Offset this date by a relative time offset.
#
# This differs from `Polars.col("foo") + timedelta` in that it can
# take months and leap years into account. Note that only a single minus
# sign is allowed in the `by` string, as the first character.
#
# @param by [String]
# The offset is dictated by the following string language:
#
# - 1ns (1 nanosecond)
# - 1us (1 microsecond)
# - 1ms (1 millisecond)
# - 1s (1 second)
# - 1m (1 minute)
# - 1h (1 hour)
# - 1d (1 day)
# - 1w (1 week)
# - 1mo (1 calendar month)
# - 1y (1 calendar year)
# - 1i (1 index count)
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "dates" => Polars.datetime_range(
# DateTime.new(2000, 1, 1), DateTime.new(2005, 1, 1), "1y", eager: true
# )
# }
# )
# df.select(
# [
# Polars.col("dates").dt.offset_by("1y").alias("date_plus_1y"),
# Polars.col("dates").dt.offset_by("-1y2mo").alias("date_min")
# ]
# )
# # =>
# # shape: (6, 2)
# # ┌─────────────────────┬─────────────────────┐
# # │ date_plus_1y ┆ date_min │
# # │ --- ┆ --- │
# # │ datetime[ns] ┆ datetime[ns] │
# # ╞═════════════════════╪═════════════════════╡
# # │ 2001-01-01 00:00:00 ┆ 1998-11-01 00:00:00 │
# # │ 2002-01-01 00:00:00 ┆ 1999-11-01 00:00:00 │
# # │ 2003-01-01 00:00:00 ┆ 2000-11-01 00:00:00 │
# # │ 2004-01-01 00:00:00 ┆ 2001-11-01 00:00:00 │
# # │ 2005-01-01 00:00:00 ┆ 2002-11-01 00:00:00 │
# # │ 2006-01-01 00:00:00 ┆ 2003-11-01 00:00:00 │
# # └─────────────────────┴─────────────────────┘
def offset_by(by)
by = Utils.parse_into_expression(by, str_as_lit: true)
Utils.wrap_expr(_rbexpr.dt_offset_by(by))
end
# Roll backward to the first day of the month.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "dates" => Polars.datetime_range(
# DateTime.new(2000, 1, 15, 2),
# DateTime.new(2000, 12, 15, 2),
# "1mo",
# eager: true
# )
# }
# )
# df.select(Polars.col("dates").dt.month_start)
# # =>
# # shape: (12, 1)
# # ┌─────────────────────┐
# # │ dates │
# # │ --- │
# # │ datetime[ns] │
# # ╞═════════════════════╡
# # │ 2000-01-01 02:00:00 │
# # │ 2000-02-01 02:00:00 │
# # │ 2000-03-01 02:00:00 │
# # │ 2000-04-01 02:00:00 │
# # │ 2000-05-01 02:00:00 │
# # │ … │
# # │ 2000-08-01 02:00:00 │
# # │ 2000-09-01 02:00:00 │
# # │ 2000-10-01 02:00:00 │
# # │ 2000-11-01 02:00:00 │
# # │ 2000-12-01 02:00:00 │
# # └─────────────────────┘
def month_start
Utils.wrap_expr(_rbexpr.dt_month_start)
end
# Roll forward to the last day of the month.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "dates" => Polars.datetime_range(
# DateTime.new(2000, 1, 15, 2),
# DateTime.new(2000, 12, 15, 2),
# "1mo",
# eager: true
# )
# }
# )
# df.select(Polars.col("dates").dt.month_end)
# # =>
# # shape: (12, 1)
# # ┌─────────────────────┐
# # │ dates │
# # │ --- │
# # │ datetime[ns] │
# # ╞═════════════════════╡
# # │ 2000-01-31 02:00:00 │
# # │ 2000-02-29 02:00:00 │
# # │ 2000-03-31 02:00:00 │
# # │ 2000-04-30 02:00:00 │
# # │ 2000-05-31 02:00:00 │
# # │ … │
# # │ 2000-08-31 02:00:00 │
# # │ 2000-09-30 02:00:00 │
# # │ 2000-10-31 02:00:00 │
# # │ 2000-11-30 02:00:00 │
# # │ 2000-12-31 02:00:00 │
# # └─────────────────────┘
def month_end
Utils.wrap_expr(_rbexpr.dt_month_end)
end
# Base offset from UTC.
#
# This is usually constant for all datetimes in a given time zone, but
# may vary in the rare case that a country switches time zone, like
# Samoa (Apia) did at the end of 2011.
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "ts" => [DateTime.new(2011, 12, 29), DateTime.new(2012, 1, 1)],
# }
# )
# df = df.with_columns(Polars.col("ts").dt.replace_time_zone("Pacific/Apia"))
# df.with_columns(Polars.col("ts").dt.base_utc_offset.alias("base_utc_offset"))
# # =>
# # shape: (2, 2)
# # ┌────────────────────────────┬─────────────────┐
# # │ ts ┆ base_utc_offset │
# # │ --- ┆ --- │
# # │ datetime[ns, Pacific/Apia] ┆ duration[ms] │
# # ╞════════════════════════════╪═════════════════╡
# # │ 2011-12-29 00:00:00 -10 ┆ -11h │
# # │ 2012-01-01 00:00:00 +14 ┆ 13h │
# # └────────────────────────────┴─────────────────┘
def base_utc_offset
Utils.wrap_expr(_rbexpr.dt_base_utc_offset)
end
# Additional offset currently in effect (typically due to daylight saving time).
#
# @return [Expr]
#
# @example
# df = Polars::DataFrame.new(
# {
# "ts" => [DateTime.new(2020, 10, 25), DateTime.new(2020, 10, 26)],
# }
# )
# df = df.with_columns(Polars.col("ts").dt.replace_time_zone("Europe/London"))
# df.with_columns(Polars.col("ts").dt.dst_offset.alias("dst_offset"))
# # =>
# # shape: (2, 2)
# # ┌─────────────────────────────┬──────────────┐
# # │ ts ┆ dst_offset │
# # │ --- ┆ --- │
# # │ datetime[ns, Europe/London] ┆ duration[ms] │
# # ╞═════════════════════════════╪══════════════╡
# # │ 2020-10-25 00:00:00 BST ┆ 1h │
# # │ 2020-10-26 00:00:00 GMT ┆ 0ms │
# # └─────────────────────────────┴──────────────┘
def dst_offset
Utils.wrap_expr(_rbexpr.dt_dst_offset)
end
end
end