python / python/typing

Distinguish between datetime.datetime objects with a timezone and those without

Đang mở
#1,962 1 bình luận 1 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

topic: feature
Ngôn ngữ chính
Python
Star
1.8k
Fork
302
Merge trung bình
23 giờ
Pull request đã merge (30 ngày)
8

Mô tả

datetime.datetime objects created with timezone information cannot be compared to datetime objects without timezone information. This error comes up

TypeError: can't compare offset-naive and offset-aware datetimes

I believe it is possible for the type system to handle distinguishing between these two objects.

I have a proof of concept at my Company that looks something like this. The idea is to distinguish between these two object types and forbid their corresponding comparisons.

class DatetimeWithTimezone(datetime_lib.datetime):
  """Datetime with timezone."""

  @overload
  def replace(
      self,
      year: SupportsIndex = ...,
      month: SupportsIndex = ...,
      day: SupportsIndex = ...,
      hour: SupportsIndex = ...,
      minute: SupportsIndex = ...,
      second: SupportsIndex = ...,
      microsecond: SupportsIndex = ...,
      tzinfo: _TzInfo = ...,
      *,
      fold: int = ...,
  ) -> Self: ...
  @overload
  def replace(
      self,
      year: SupportsIndex = ...,
      month: SupportsIndex = ...,
      day: SupportsIndex = ...,
      hour: SupportsIndex = ...,
      minute: SupportsIndex = ...,
      second: SupportsIndex = ...,
      microsecond: SupportsIndex = ...,
      tzinfo: None = ...,
      *,
      fold: int = ...,
  ) -> DatetimeWithoutTimezone: ...
  @overload
  def astimezone(self, tz: _TzInfo) -> Self: ...
  @overload
  def astimezone(self, tz: None) -> DatetimeWithoutTimezone: ...
  def utcoffset(self) -> timedelta: ...
  def tzname(self) -> str: ...
  def dst(self) -> timedelta: ...
  def __le__(self, value: DatetimeWithTimezone, /) -> bool: ...  # type: ignore[override]
  def __lt__(self, value: DatetimeWithTimezone, /) -> bool: ...  # type: ignore[override]
  def __ge__(self, value: DatetimeWithTimezone, /) -> bool: ...  # type: ignore[override]
  def __gt__(self, value: DatetimeWithTimezone, /) -> bool: ...  # type: ignore[override]
  @overload  # type: ignore[override]
  def __sub__(self, value: Self, /) -> timedelta: ...
  @overload
  def __sub__(self, value: timedelta, /) -> Self: ...

class DatetimeWithoutTimezone(datetime_lib.datetime):
  """Datetime without timezone."""

  @overload
  def replace(
      self,
      year: SupportsIndex = ...,
      month: SupportsIndex = ...,
      day: SupportsIndex = ...,
      hour: SupportsIndex = ...,
      minute: SupportsIndex = ...,
      second: SupportsIndex = ...,
      microsecond: SupportsIndex = ...,
      tzinfo: _TzInfo = ...,
      *,
      fold: int = ...,
  ) -> DatetimeWithTimezone: ...
  @overload
  def replace(
      self,
      year: SupportsIndex = ...,
      month: SupportsIndex = ...,
      day: SupportsIndex = ...,
      hour: SupportsIndex = ...,
      minute: SupportsIndex = ...,
      second: SupportsIndex = ...,
      microsecond: SupportsIndex = ...,
      tzinfo: None = ...,
      *,
      fold: int = ...,
  ) -> Self: ...
  @overload
  def astimezone(self, tz: _TzInfo) -> DatetimeWithoutTimezone: ...
  @overload
  def astimezone(self, tz: None) -> Self: ...
  def utcoffset(self) -> None: ...
  def tzname(self) -> None: ...
  def dst(self) -> None: ...
  def __le__(self, value: DatetimeWithoutTimezone, /) -> bool: ...  # type: ignore[override]
  def __lt__(self, value: DatetimeWithoutTimezone, /) -> bool: ...  # type: ignore[override]
  def __ge__(self, value: DatetimeWithoutTimezone, /) -> bool: ...  # type: ignore[override]
  def __gt__(self, value: DatetimeWithoutTimezone, /) -> bool: ...  # type: ignore[override]
  @overload  # type: ignore[override]
  def __sub__(self, value: Self, /) -> timedelta: ...
  @overload
  def __sub__(self, value: timedelta, /) -> Self: ...

class datetime(datetime_lib.datetime):
  @overload
  def __new__(
      cls,
      year: SupportsIndex,
      month: SupportsIndex,
      day: SupportsIndex,
      hour: SupportsIndex = ...,
      minute: SupportsIndex = ...,
      second: SupportsIndex = ...,
      microsecond: SupportsIndex = ...,
      tzinfo: _TzInfo = ...,
      *,
      fold: int = ...,
  ) -> DatetimeWithTimezone: ...
  @overload
  def __new__(
      cls,
      year: SupportsIndex,
      month: SupportsIndex,
      day: SupportsIndex,
      hour: SupportsIndex = ...,
      minute: SupportsIndex = ...,
      second: SupportsIndex = ...,
      microsecond: SupportsIndex = ...,
      tzinfo: None = ...,
      *,
      fold: int = ...,
  ) -> DatetimeWithoutTimezone: ...
  # On <3.12, the name of the first parameter in the pure-Python implementation
  # didn't match the name in the C implementation,
  # meaning it is only *safe* to pass it as a keyword argument on 3.12+
  # Assume are on 3.12+
  @overload
  @classmethod
  def fromtimestamp(
      cls, timestamp: float, tz: None = ...
  ) -> DatetimeWithoutTimezone: ...
  @overload
  @classmethod
  def fromtimestamp(
      cls, timestamp: float, tz: _TzInfo
  ) -> DatetimeWithTimezone: ...
  @overload
  @classmethod
  def now(cls, tz: _TzInfo) -> DatetimeWithTimezone: ...
  @overload
  @classmethod
  def now(cls, tz: None = ...) -> DatetimeWithoutTimezone: ...
  @overload
  @classmethod
  def combine(
      cls, date: _Date, time: _Time, tzinfo: None = ...
  ) -> DatetimeWithoutTimezone: ...
  @overload
  @classmethod
  def combine(
      cls, date: _Date, time: _Time, tzinfo: _TzInfo
  ) -> DatetimeWithTimezone: ...
  @classmethod
  def strptime(
      cls, date_string: str, format: str, /
  ) -> DatetimeWithTimezone | DatetimeWithoutTimezone: ...

I have some questions before I'm certain this could be possible for everyone

  1. Is it possible to make this backwards compatible?
  2. Is this something others are interested in?
  3. Should this actually be solved in the datetime library directly?

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu bằng việc xem xét các kiểu DatetimeWithTimezone và DatetimeWithoutTimezone được đề xuất, cùng với các hàm khởi tạo và phương thức datetime được liệt kê. Xác định xem có thể thêm sự phân biệt này mà vẫn tương thích với Python typing hay không và liệu nó có thuộc thư viện datetime hay không; để hoàn thành cần giải quyết các câu hỏi về thiết kế này và xác định một phạm vi đã được thống nhất.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
developer-experience
Loại issue
Tính năng
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Cần làm rõ
Mức phù hợp với người mới
25/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.