Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit property and as_unit method to Timestamp, Timedelta and NaTType #864

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pandas-stubs/_libs/tslibs/nattype.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ from datetime import (
)

import numpy as np
from typing_extensions import TypeAlias
from typing_extensions import (
Self,
TypeAlias,
)

from pandas._libs.tslibs.period import Period
from pandas._typing import TimeUnit

NaT: NaTType
iNaT: int
Expand Down Expand Up @@ -121,3 +125,6 @@ class NaTType:
__le__: _NatComparison
__gt__: _NatComparison
__ge__: _NatComparison
@property
def unit(self) -> TimeUnit: ...
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...
8 changes: 7 additions & 1 deletion pandas-stubs/_libs/tslibs/timedeltas.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ from pandas._libs.tslibs import (
)
from pandas._libs.tslibs.period import Period
from pandas._libs.tslibs.timestamps import Timestamp
from pandas._typing import npt
from pandas._typing import (
TimeUnit,
npt,
)

class Components(NamedTuple):
days: int
Expand Down Expand Up @@ -390,3 +393,6 @@ class Timedelta(timedelta):
@property
def components(self) -> Components: ...
def view(self, dtype: npt.DTypeLike = ...) -> object: ...
@property
def unit(self) -> TimeUnit: ...
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...
4 changes: 4 additions & 0 deletions pandas-stubs/_libs/tslibs/timestamps.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ from pandas._libs.tslibs import (
Timedelta,
)
from pandas._typing import (
TimeUnit,
np_ndarray_bool,
npt,
)
Expand Down Expand Up @@ -310,3 +311,6 @@ class Timestamp(datetime):
def days_in_month(self) -> int: ...
@property
def daysinmonth(self) -> int: ...
@property
def unit(self) -> TimeUnit: ...
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...
1 change: 1 addition & 0 deletions pandas-stubs/_typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,7 @@ RandomState: TypeAlias = (
| np.random.RandomState
)
Frequency: TypeAlias = str | BaseOffset
TimeUnit: TypeAlias = Literal["s", "ms", "us", "ns"]
TimeGrouperOrigin: TypeAlias = (
Timestamp | Literal["epoch", "start", "start_day", "end", "end_day"]
)
Expand Down
9 changes: 5 additions & 4 deletions pandas-stubs/core/frame.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ from pandas._typing import (
StrLike,
Suffixes,
TimestampConvention,
TimeUnit,
ValidationOptions,
WriteBuffer,
XMLParsers,
Expand Down Expand Up @@ -2082,7 +2083,7 @@ class DataFrame(NDFrame, OpsMixin):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand All @@ -2101,7 +2102,7 @@ class DataFrame(NDFrame, OpsMixin):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand All @@ -2119,7 +2120,7 @@ class DataFrame(NDFrame, OpsMixin):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand All @@ -2137,7 +2138,7 @@ class DataFrame(NDFrame, OpsMixin):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand Down
8 changes: 3 additions & 5 deletions pandas-stubs/core/indexes/datetimes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ from datetime import (
timedelta,
tzinfo,
)
from typing import (
Literal,
overload,
)
from typing import overload

import numpy as np
from pandas import (
Expand All @@ -32,6 +29,7 @@ from pandas._typing import (
ArrayLike,
DateAndDatetimeLike,
IntervalClosedType,
TimeUnit,
)

from pandas.core.dtypes.dtypes import DatetimeTZDtype
Expand Down Expand Up @@ -100,7 +98,7 @@ def date_range(
normalize: bool = ...,
name: Hashable | None = ...,
inclusive: IntervalClosedType = ...,
unit: Literal["s", "ms", "us", "ns"] | None = ...,
unit: TimeUnit | None = ...,
) -> DatetimeIndex: ...
@overload
def bdate_range(
Expand Down
9 changes: 5 additions & 4 deletions pandas-stubs/core/series.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ from pandas._typing import (
TimedeltaDtypeArg,
TimestampConvention,
TimestampDtypeArg,
TimeUnit,
UIntDtypeArg,
VoidDtypeArg,
WriteBuffer,
Expand Down Expand Up @@ -486,7 +487,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand All @@ -505,7 +506,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand All @@ -523,7 +524,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand All @@ -541,7 +542,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
date_format: Literal["epoch", "iso"] | None = ...,
double_precision: int = ...,
force_ascii: _bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] = ...,
date_unit: TimeUnit = ...,
default_handler: (
Callable[[Any], _str | float | _bool | list | dict] | None
) = ...,
Expand Down
9 changes: 5 additions & 4 deletions pandas-stubs/io/json/_json.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ from pandas._typing import (
NDFrameT,
ReadBuffer,
StorageOptions,
TimeUnit,
)

@overload
Expand All @@ -35,7 +36,7 @@ def read_json(
convert_dates: bool | list[str] = ...,
keep_default_dates: bool = ...,
precise_float: bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
date_unit: TimeUnit | None = ...,
encoding: str | None = ...,
encoding_errors: (
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
Expand All @@ -59,7 +60,7 @@ def read_json(
convert_dates: bool | list[str] = ...,
keep_default_dates: bool = ...,
precise_float: bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
date_unit: TimeUnit | None = ...,
encoding: str | None = ...,
encoding_errors: (
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
Expand All @@ -83,7 +84,7 @@ def read_json(
convert_dates: bool | list[str] = ...,
keep_default_dates: bool = ...,
precise_float: bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
date_unit: TimeUnit | None = ...,
encoding: str | None = ...,
encoding_errors: (
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
Expand All @@ -107,7 +108,7 @@ def read_json(
convert_dates: bool | list[str] = ...,
keep_default_dates: bool = ...,
precise_float: bool = ...,
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
date_unit: TimeUnit | None = ...,
encoding: str | None = ...,
encoding_errors: (
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
Expand Down
13 changes: 13 additions & 0 deletions tests/test_scalars.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
NaTType,
)
from pandas._libs.tslibs.timedeltas import Components
from pandas._typing import TimeUnit

from tests import (
TYPE_CHECKING_INVALID_USAGE,
Expand Down Expand Up @@ -517,6 +518,7 @@ def test_timedelta_properties_methods() -> None:
check(assert_type(td.value, int), int)
check(assert_type(td.resolution_string, str), str)
check(assert_type(td.components, Components), Components)
check(assert_type(td.unit, TimeUnit), str)

check(assert_type(td.ceil("D"), pd.Timedelta), pd.Timedelta)
check(assert_type(td.floor(Day()), pd.Timedelta), pd.Timedelta)
Expand All @@ -529,6 +531,11 @@ def test_timedelta_properties_methods() -> None:
check(assert_type(td.view(np.int64), object), np.int64)
check(assert_type(td.view("i8"), object), np.int64)

check(assert_type(td.as_unit("s"), pd.Timedelta), pd.Timedelta)
check(assert_type(td.as_unit("ms"), pd.Timedelta), pd.Timedelta)
check(assert_type(td.as_unit("us", round_ok=True), pd.Timedelta), pd.Timedelta)
check(assert_type(td.as_unit("ns", round_ok=False), pd.Timedelta), pd.Timedelta)


def test_timedelta_add_sub() -> None:
td = pd.Timedelta("1 day")
Expand Down Expand Up @@ -1189,6 +1196,7 @@ def test_timestamp_properties() -> None:
check(assert_type(ts.tzinfo, Optional[dt.tzinfo]), type(None))
check(assert_type(ts.value, int), int)
check(assert_type(ts.year, int), int)
check(assert_type(ts.unit, TimeUnit), str)


def test_timestamp_add_sub() -> None:
Expand Down Expand Up @@ -1645,6 +1653,11 @@ def test_timestamp_misc_methods() -> None:
pd.Timestamp,
)

check(assert_type(ts2.as_unit("s"), pd.Timestamp), pd.Timestamp)
check(assert_type(ts2.as_unit("ms"), pd.Timestamp), pd.Timestamp)
check(assert_type(ts2.as_unit("us", round_ok=True), pd.Timestamp), pd.Timestamp)
check(assert_type(ts2.as_unit("ns", round_ok=False), pd.Timestamp), pd.Timestamp)


def test_timestamp_types_arithmetic() -> None:
ts: pd.Timestamp = pd.to_datetime("2021-03-01")
Expand Down
Loading