Skip to content

Commit

Permalink
Make pandas types inherit from datetime types (#77)
Browse files Browse the repository at this point in the history
* Inheritance, still a couple of problems

* Timestamps and timedeltas now inherit from datetime classes

* Fix an issue with tox not recognizing Python 3.6 and 3.7 versions

* Go back to previous tox configuration
  • Loading branch information
zkrolikowski-vl authored Oct 5, 2021
1 parent 91270b4 commit 73d3444
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 16 deletions.
18 changes: 18 additions & 0 deletions tests/snippets/test_timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pandas as pd
import datetime as dt


def test_types_init() -> None:
ts: pd.Timestamp = pd.Timestamp('2021-03-01T12')
ts1: pd.Timestamp = pd.Timestamp(dt.date(2021, 3, 15))
Expand All @@ -14,3 +15,20 @@ def test_types_init() -> None:
ts7: pd.Timestamp = pd.Timestamp(2021, 3, 10, 12)
ts8: pd.Timestamp = pd.Timestamp(year=2021, month=3, day=10, hour=12)
ts9: pd.Timestamp = pd.Timestamp(year=2021, month=3, day=10, hour=12, tz='US/Pacific')


def test_types_arithmetic() -> None:
ts: pd.Timestamp = pd.to_datetime("2021-03-01")
ts2: pd.Timestamp = pd.to_datetime("2021-01-01")
delta: pd.Timedelta = pd.to_timedelta("1 day")

tsr: pd.Timedelta = ts - ts2
tsr2: pd.Timestamp = ts + delta


def test_types_comparison() -> None:
ts: pd.Timestamp = pd.to_datetime("2021-03-01")
ts2: pd.Timestamp = pd.to_datetime("2021-01-01")

tsr: bool = ts < ts2
tsr2: bool = ts > ts2
2 changes: 1 addition & 1 deletion third_party/3/pandas/_libs/tslibs/timedeltas.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ from datetime import timedelta
from typing import Any, Union
import numpy as np

class Timedelta:
class Timedelta(timedelta):

def __init__(self, value: Union[Timedelta, timedelta, np.timedelta64, str, int], unit: str = ..., **kwargs: Any) -> None: ...
53 changes: 38 additions & 15 deletions third_party/3/pandas/_libs/tslibs/timestamps.pyi
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
"""
This class could be comprehensibly typed with the typeshed .pyi file but the
datatetime.datetime and pd.Timestamp API differ quite a bit and cannot be used interchangeably
"""
import sys
from typing import Any, Optional, Union, overload
from typing import Any, Optional, Union, overload, TypeVar, Type

from pandas._libs.tslibs.timedeltas import Timedelta

if sys.version_info >= (3, 8):
from typing import Literal
else:
from typing_extensions import Literal

from dateutil.tz import tzfile
from datetime import tzinfo, date
from datetime import tzinfo, datetime, date as _date, time, timedelta

from pandas._libs.tslibs.period import Period
from pandas._typing import TimestampConvertible
Expand All @@ -16,35 +22,52 @@ OptInt = Optional[int]

Fold = Literal[0, 1]

class Timestamp:
_S = TypeVar("_S")

class Timestamp(datetime):
@overload
def __init__(self, ts_input: TimestampConvertible, freq: Optional[str] = ..., tz: Optional[Union[str, tzinfo, tzfile]] = ..., unit: Optional[str] = ..., tzinfo: Optional[tzinfo] = ..., fold: Optional[Fold] = ...): ...
def __init__(self, ts_input: TimestampConvertible, freq: Optional[str] = ..., tz: Optional[Union[str, tzinfo, tzfile]] = ...,
unit: Optional[str] = ..., tzinfo: Optional[tzinfo] = ..., fold: Optional[Fold] = ...): ...
@overload
def __init__(self, year: OptInt = ..., month: OptInt = ..., day: OptInt = ..., hour: OptInt = ..., minute: OptInt = ..., second: OptInt = ..., microsecond: OptInt = ..., nanosecond: OptInt = ..., tz: Optional[Union[str, tzinfo, tzfile]] = ..., tzinfo: Optional[tzinfo] = ..., fold: Optional[Fold] = ..., freq: Optional[str] = ...): ...
def __init__(self, year: OptInt = ..., month: OptInt = ..., day: OptInt = ..., hour: OptInt = ..., minute: OptInt = ...,
second: OptInt = ..., microsecond: OptInt = ..., nanosecond: OptInt = ..., tz: Optional[Union[str, tzinfo, tzfile]] = ...,
tzinfo: Optional[tzinfo] = ..., fold: Optional[Fold] = ..., freq: Optional[str] = ...): ...
def to_period(self, freq: Optional[str]) -> Period: ...
def to_julian_date(self) -> float: ...
def tz_localize(self, tz: Any = ..., ambigious: Any = ..., nonexistent: Any = ...) -> Timestamp: ...
def tz_convert(self, tz: Any) -> Timestamp: ...
def astimezone(self, tz: Any) -> Timestamp: ...
def replace(self, year: OptInt = ..., month: OptInt = ..., day: OptInt = ..., hour: OptInt = ..., minute: OptInt = ..., second: OptInt = ..., microsecond: OptInt = ..., nanosecond: OptInt = ..., tzinfo: Optional[tzinfo] = ..., fold: Fold = ...) -> Timestamp: ...
@overload # type: ignore[override]
def __sub__(self, other: datetime) -> Timedelta: ...
@overload
def __sub__(self, other: timedelta) -> Timestamp: ...
def __add__(self, other: timedelta) -> Timestamp: ...
if sys.version_info >= (3, 8):
def astimezone(self: _S, tz: Optional[tzinfo] = ...) -> _S: ...
else:
def astimezone(self, tz: Optional[tzinfo] = ...) -> datetime: ...
# This is correct. Typeshed doesn't include Optionals, or the Literal correctly
def replace(self, year: OptInt = ..., month: OptInt = ..., # type: ignore[override]
day: OptInt = ..., hour: OptInt = ..., minute: OptInt = ...,
second: OptInt = ..., microsecond: OptInt = ..., nanosecond: OptInt = ...,
tzinfo: Optional[tzinfo] = ..., *, fold: Fold = ...) -> datetime: ...
def round(self, freq: str, ambiguous: Any = ..., nonexistent: Any = ...) -> Timestamp: ...
def ceil(self, freq: str, ambiguous: Any = ...,nonexistent: Any = ...) -> Timestamp: ...
def floor(self, freq: str, ambiguous: Any = ...,nonexistent: Any = ...) -> Timestamp: ...
def isoformat(self, sep: str = ...) -> str: ...
def ceil(self, freq: str, ambiguous: Any = ..., nonexistent: Any = ...) -> Timestamp: ...
def floor(self, freq: str, ambiguous: Any = ..., nonexistent: Any = ...) -> Timestamp: ...
def isoformat(self, sep: str = ..., timespec: str = ...) -> str: ...
def day_name(self, locale: Optional[str]) -> str: ...
def month_name(self, locale: Optional[str]) -> str: ...
def normalize(self) -> Timestamp: ...
def strftime(self, format: str) -> str: ...
def date(self) -> date: ...

def date(self) -> _date: ...
@classmethod
def utcnow(cls) -> Timestamp: ...
@classmethod
def utcfromtimestamp(cls, ts: Timestamp) -> Timestamp: ...
def utcfromtimestamp(cls, ts: float) -> Timestamp: ...
# Pandas doesn't accept timezone here, unlike datetime
@classmethod
def fromtimestamp(cls, ts: Timestamp) -> Timestamp: ...
def fromtimestamp(cls: Type[_S], t: float) -> _S: ... # type: ignore[override]
@classmethod
def combine(cls, date: Any, time: Any) -> Timestamp: ...
def combine(cls, date: _date, time: time, tzinfo: Optional[tzinfo] = ...) -> Timestamp: ...
@classmethod
def now(cls, tz: Optional[Any] = ...) -> Timestamp: ...
@classmethod
Expand Down

0 comments on commit 73d3444

Please sign in to comment.