Skip to content

Commit fdbe1df

Browse files
authored
Add unit property and as_unit method to Timestamp, Timedelta and NaTType (#864)
* Add unit property and as_unit method to NaTType * Add unit property to Timestamp * Add as_unit method to Timestamp * Add unit property to Timedelta * Add as_unit method to Timedelta * Add TimeUnit type alias * Use TimeUnit type instead of str or Literal["s", "ms", "us", "ns"]
1 parent 7978238 commit fdbe1df

File tree

9 files changed

+51
-19
lines changed

9 files changed

+51
-19
lines changed

pandas-stubs/_libs/tslibs/nattype.pyi

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ from datetime import (
55
)
66

77
import numpy as np
8-
from typing_extensions import TypeAlias
8+
from typing_extensions import (
9+
Self,
10+
TypeAlias,
11+
)
912

1013
from pandas._libs.tslibs.period import Period
14+
from pandas._typing import TimeUnit
1115

1216
NaT: NaTType
1317
iNaT: int
@@ -121,3 +125,6 @@ class NaTType:
121125
__le__: _NatComparison
122126
__gt__: _NatComparison
123127
__ge__: _NatComparison
128+
@property
129+
def unit(self) -> TimeUnit: ...
130+
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...

pandas-stubs/_libs/tslibs/timedeltas.pyi

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ from pandas._libs.tslibs import (
3131
)
3232
from pandas._libs.tslibs.period import Period
3333
from pandas._libs.tslibs.timestamps import Timestamp
34-
from pandas._typing import npt
34+
from pandas._typing import (
35+
TimeUnit,
36+
npt,
37+
)
3538

3639
class Components(NamedTuple):
3740
days: int
@@ -390,3 +393,6 @@ class Timedelta(timedelta):
390393
@property
391394
def components(self) -> Components: ...
392395
def view(self, dtype: npt.DTypeLike = ...) -> object: ...
396+
@property
397+
def unit(self) -> TimeUnit: ...
398+
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...

pandas-stubs/_libs/tslibs/timestamps.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ from pandas._libs.tslibs import (
3838
Timedelta,
3939
)
4040
from pandas._typing import (
41+
TimeUnit,
4142
np_ndarray_bool,
4243
npt,
4344
)
@@ -310,3 +311,6 @@ class Timestamp(datetime):
310311
def days_in_month(self) -> int: ...
311312
@property
312313
def daysinmonth(self) -> int: ...
314+
@property
315+
def unit(self) -> TimeUnit: ...
316+
def as_unit(self, unit: TimeUnit, round_ok: bool = ...) -> Self: ...

pandas-stubs/_typing.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ RandomState: TypeAlias = (
775775
| np.random.RandomState
776776
)
777777
Frequency: TypeAlias = str | BaseOffset
778+
TimeUnit: TypeAlias = Literal["s", "ms", "us", "ns"]
778779
TimeGrouperOrigin: TypeAlias = (
779780
Timestamp | Literal["epoch", "start", "start_day", "end", "end_day"]
780781
)

pandas-stubs/core/frame.pyi

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ from pandas._typing import (
116116
Suffixes,
117117
T as _T,
118118
TimestampConvention,
119+
TimeUnit,
119120
ValidationOptions,
120121
WriteBuffer,
121122
XMLParsers,
@@ -2090,7 +2091,7 @@ class DataFrame(NDFrame, OpsMixin):
20902091
date_format: Literal["epoch", "iso"] | None = ...,
20912092
double_precision: int = ...,
20922093
force_ascii: _bool = ...,
2093-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
2094+
date_unit: TimeUnit = ...,
20942095
default_handler: (
20952096
Callable[[Any], _str | float | _bool | list | dict] | None
20962097
) = ...,
@@ -2109,7 +2110,7 @@ class DataFrame(NDFrame, OpsMixin):
21092110
date_format: Literal["epoch", "iso"] | None = ...,
21102111
double_precision: int = ...,
21112112
force_ascii: _bool = ...,
2112-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
2113+
date_unit: TimeUnit = ...,
21132114
default_handler: (
21142115
Callable[[Any], _str | float | _bool | list | dict] | None
21152116
) = ...,
@@ -2127,7 +2128,7 @@ class DataFrame(NDFrame, OpsMixin):
21272128
date_format: Literal["epoch", "iso"] | None = ...,
21282129
double_precision: int = ...,
21292130
force_ascii: _bool = ...,
2130-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
2131+
date_unit: TimeUnit = ...,
21312132
default_handler: (
21322133
Callable[[Any], _str | float | _bool | list | dict] | None
21332134
) = ...,
@@ -2145,7 +2146,7 @@ class DataFrame(NDFrame, OpsMixin):
21452146
date_format: Literal["epoch", "iso"] | None = ...,
21462147
double_precision: int = ...,
21472148
force_ascii: _bool = ...,
2148-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
2149+
date_unit: TimeUnit = ...,
21492150
default_handler: (
21502151
Callable[[Any], _str | float | _bool | list | dict] | None
21512152
) = ...,

pandas-stubs/core/indexes/datetimes.pyi

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ from datetime import (
77
timedelta,
88
tzinfo,
99
)
10-
from typing import (
11-
Literal,
12-
overload,
13-
)
10+
from typing import overload
1411

1512
import numpy as np
1613
from pandas import (
@@ -32,6 +29,7 @@ from pandas._typing import (
3229
ArrayLike,
3330
DateAndDatetimeLike,
3431
IntervalClosedType,
32+
TimeUnit,
3533
)
3634

3735
from pandas.core.dtypes.dtypes import DatetimeTZDtype
@@ -100,7 +98,7 @@ def date_range(
10098
normalize: bool = ...,
10199
name: Hashable | None = ...,
102100
inclusive: IntervalClosedType = ...,
103-
unit: Literal["s", "ms", "us", "ns"] | None = ...,
101+
unit: TimeUnit | None = ...,
104102
) -> DatetimeIndex: ...
105103
@overload
106104
def bdate_range(

pandas-stubs/core/series.pyi

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ from pandas._typing import (
145145
TimedeltaDtypeArg,
146146
TimestampConvention,
147147
TimestampDtypeArg,
148+
TimeUnit,
148149
UIntDtypeArg,
149150
VoidDtypeArg,
150151
WriteBuffer,
@@ -493,7 +494,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
493494
date_format: Literal["epoch", "iso"] | None = ...,
494495
double_precision: int = ...,
495496
force_ascii: _bool = ...,
496-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
497+
date_unit: TimeUnit = ...,
497498
default_handler: (
498499
Callable[[Any], _str | float | _bool | list | dict] | None
499500
) = ...,
@@ -512,7 +513,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
512513
date_format: Literal["epoch", "iso"] | None = ...,
513514
double_precision: int = ...,
514515
force_ascii: _bool = ...,
515-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
516+
date_unit: TimeUnit = ...,
516517
default_handler: (
517518
Callable[[Any], _str | float | _bool | list | dict] | None
518519
) = ...,
@@ -530,7 +531,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
530531
date_format: Literal["epoch", "iso"] | None = ...,
531532
double_precision: int = ...,
532533
force_ascii: _bool = ...,
533-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
534+
date_unit: TimeUnit = ...,
534535
default_handler: (
535536
Callable[[Any], _str | float | _bool | list | dict] | None
536537
) = ...,
@@ -548,7 +549,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
548549
date_format: Literal["epoch", "iso"] | None = ...,
549550
double_precision: int = ...,
550551
force_ascii: _bool = ...,
551-
date_unit: Literal["s", "ms", "us", "ns"] = ...,
552+
date_unit: TimeUnit = ...,
552553
default_handler: (
553554
Callable[[Any], _str | float | _bool | list | dict] | None
554555
) = ...,

pandas-stubs/io/json/_json.pyi

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ from pandas._typing import (
2222
NDFrameT,
2323
ReadBuffer,
2424
StorageOptions,
25+
TimeUnit,
2526
)
2627

2728
@overload
@@ -35,7 +36,7 @@ def read_json(
3536
convert_dates: bool | list[str] = ...,
3637
keep_default_dates: bool = ...,
3738
precise_float: bool = ...,
38-
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
39+
date_unit: TimeUnit | None = ...,
3940
encoding: str | None = ...,
4041
encoding_errors: (
4142
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
@@ -59,7 +60,7 @@ def read_json(
5960
convert_dates: bool | list[str] = ...,
6061
keep_default_dates: bool = ...,
6162
precise_float: bool = ...,
62-
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
63+
date_unit: TimeUnit | None = ...,
6364
encoding: str | None = ...,
6465
encoding_errors: (
6566
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
@@ -83,7 +84,7 @@ def read_json(
8384
convert_dates: bool | list[str] = ...,
8485
keep_default_dates: bool = ...,
8586
precise_float: bool = ...,
86-
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
87+
date_unit: TimeUnit | None = ...,
8788
encoding: str | None = ...,
8889
encoding_errors: (
8990
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]
@@ -107,7 +108,7 @@ def read_json(
107108
convert_dates: bool | list[str] = ...,
108109
keep_default_dates: bool = ...,
109110
precise_float: bool = ...,
110-
date_unit: Literal["s", "ms", "us", "ns"] | None = ...,
111+
date_unit: TimeUnit | None = ...,
111112
encoding: str | None = ...,
112113
encoding_errors: (
113114
Literal["strict", "ignore", "replace", "backslashreplace", "surrogateescape"]

tests/test_scalars.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
NaTType,
2525
)
2626
from pandas._libs.tslibs.timedeltas import Components
27+
from pandas._typing import TimeUnit
2728

2829
from tests import (
2930
TYPE_CHECKING_INVALID_USAGE,
@@ -517,6 +518,7 @@ def test_timedelta_properties_methods() -> None:
517518
check(assert_type(td.value, int), int)
518519
check(assert_type(td.resolution_string, str), str)
519520
check(assert_type(td.components, Components), Components)
521+
check(assert_type(td.unit, TimeUnit), str)
520522

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

534+
check(assert_type(td.as_unit("s"), pd.Timedelta), pd.Timedelta)
535+
check(assert_type(td.as_unit("ms"), pd.Timedelta), pd.Timedelta)
536+
check(assert_type(td.as_unit("us", round_ok=True), pd.Timedelta), pd.Timedelta)
537+
check(assert_type(td.as_unit("ns", round_ok=False), pd.Timedelta), pd.Timedelta)
538+
532539

533540
def test_timedelta_add_sub() -> None:
534541
td = pd.Timedelta("1 day")
@@ -1189,6 +1196,7 @@ def test_timestamp_properties() -> None:
11891196
check(assert_type(ts.tzinfo, Optional[dt.tzinfo]), type(None))
11901197
check(assert_type(ts.value, int), int)
11911198
check(assert_type(ts.year, int), int)
1199+
check(assert_type(ts.unit, TimeUnit), str)
11921200

11931201

11941202
def test_timestamp_add_sub() -> None:
@@ -1645,6 +1653,11 @@ def test_timestamp_misc_methods() -> None:
16451653
pd.Timestamp,
16461654
)
16471655

1656+
check(assert_type(ts2.as_unit("s"), pd.Timestamp), pd.Timestamp)
1657+
check(assert_type(ts2.as_unit("ms"), pd.Timestamp), pd.Timestamp)
1658+
check(assert_type(ts2.as_unit("us", round_ok=True), pd.Timestamp), pd.Timestamp)
1659+
check(assert_type(ts2.as_unit("ns", round_ok=False), pd.Timestamp), pd.Timestamp)
1660+
16481661

16491662
def test_timestamp_types_arithmetic() -> None:
16501663
ts: pd.Timestamp = pd.to_datetime("2021-03-01")

0 commit comments

Comments
 (0)