Whenever – Thư viện xử lý ngày giờ an toàn về kiểu và an toàn với DST cho Python
(github.com/ariebovenberg)- Whenever là thư viện ngày giờ cho Python, ngăn việc trộn naive/aware và các lỗi tính toán DST trong
datetimebằng hệ thống kiểu, đồng thời có thể dùng dưới dạng triển khai Rust hoặc triển khai thuần Python - Thư viện chuẩn
datetimekhông phải lúc nào cũng phản ánh DST, và bài viết đưa ra ví dụdatetime(2023, 3, 25, 22, tzinfo=ZoneInfo("Europe/Paris")) + timedelta(hours=8)trả về 6 giờ thay vì 7 giờ dù một giờ đã bị bỏ qua do DST - Hệ thống kiểu không thể phân biệt naive và aware chỉ với một
datetime, nhưng Whenever tách ra các kiểu theo từng mục đích sử dụng nhưInstant,ZonedDateTime,PlainDateTimeđể trình kiểm tra kiểu có thể phát hiện lỗi - Trong bảng so sánh, Arrow và Pendulum không đồng thời đáp ứng được tính an toàn với DST, phân biệt aware/naive bằng kiểu, và hiệu năng; còn Whenever được giới thiệu là hỗ trợ đầy đủ DST-safe, typed aware/naive và fast
- Vẫn còn trước phiên bản 1.0, nên API có thể thay đổi ở các bản phát hành minor; các thay đổi được mô tả trong changelog, và trình kiểm tra kiểu hoặc IDE có thể hỗ trợ điều chỉnh
Vấn đề Whenever muốn giải quyết
- Whenever là thư viện giúp viết mã ngày giờ đúng đắn và có thể kiểm tra kiểu trong Python
- Thư viện cung cấp triển khai Rust, đồng thời cũng có phiên bản thuần Python cho người dùng không muốn dùng Rust
- Được thiết kế dựa trên các khái niệm đã được kiểm chứng từ thư viện ngày giờ của các ngôn ngữ hiện đại khác
- Được giới thiệu là nhanh hơn rất nhiều so với các thư viện bên thứ ba khác, và thường còn nhanh hơn cả thư viện chuẩn
- Bản phát hành 1.0 đang được hoãn lại để tiếp tục tinh chỉnh API dài hạn, đặc biệt nhóm phát triển đang thu thập phản hồi về duration
Giới hạn của thư viện chuẩn datetime
datetimecủa Python đã phát triển hơn 20 năm và có những điểm không còn phù hợp với kỳ vọng dành cho thư viện ngày giờ hiện đại-
Vấn đề tính toán DST
datetimekhông phải lúc nào cũng tính đến Daylight Saving Time (DST)- Trong ví dụ mã, khi cộng 8 giờ vào thời điểm 22 giờ ngày 25/03/2023 ở múi giờ
Europe/Paris, kết quả đúng ra phải là 7 giờ vì DST đã làm mất một giờ, nhưng thư viện lại trả về 6 giờ - Đây không phải lỗi mà là quyết định thiết kế: chỉ tính đến DST trong các phép tính có liên quan tới hai múi giờ
-
Vấn đề phân biệt kiểu naive/aware
- Chỉ với một kiểu
datetime, hệ thống kiểu không thể phân biệt naive datetime và aware datetime - Một chữ ký hàm như
def schedule_meeting(at: datetime) -> None: ...không cho biết nó mong đợi naive hay aware
- Chỉ với một kiểu
So sánh với Arrow và Pendulum
- Theo bảng so sánh, Whenever đáp ứng đầy đủ DST-safe, typed aware/naive và fast
datetimechỉ đáp ứng tiêu chí fast, còn DST-safe và typed aware/naive thì không- Arrow cố cung cấp API thân thiện hơn thư viện chuẩn, nhưng không giải quyết được vấn đề cốt lõi
- Vẫn giữ nguyên những “footgun” tương tự
- Quyết định gom mọi thứ vào một kiểu
arrow.Arrowkhiến trình kiểm tra kiểu càng khó phát hiện lỗi hơn
- Pendulum từng quảng bá khả năng xử lý DST tốt hơn và cải thiện hiệu năng vào năm 2016, nhưng chỉ sửa được một phần các bẫy liên quan đến DST
- Hiệu năng đã suy giảm đáng kể theo thời gian
- Trong 4 năm gần đây chỉ có 2 bản phát hành, cho thấy giai đoạn trì trệ bảo trì kéo dài, và còn nhiều issue nghiêm trọng, tồn đọng lâu năm
Tính năng chính
- Hỗ trợ DST-safe arithmetic
- API an toàn về kiểu để ngăn các lỗi phổ biến
- Khắc phục những vấn đề mà Arrow và Pendulum chưa sửa được
- Dựa trên các khái niệm đã được kiểm chứng và quen thuộc
- Cung cấp hiệu năng cao
- Có kiểm thử và tài liệu đầy đủ
- Hỗ trợ số học ngày tháng
- Hỗ trợ độ chính xác nano giây
- Cung cấp hỗ trợ SQLAlchemy
- Hỗ trợ Pydantic đang ở trạng thái beta
- Cung cấp triển khai Rust và tùy chọn thuần Python
- Hỗ trợ free-threading đang ở trạng thái beta
- Hỗ trợ per-interpreter GIL
Luồng ví dụ sử dụng
- Import các kiểu tường minh như
Instant,ZonedDateTime,PlainDateTimeđể biểu đạt các trường hợp sử dụng khác nhau Instant.now()xác định một thời điểm cụ thể mà không cần xử lý độ phức tạp của múi giờ hay lịch- Chuyển đổi múi giờ một cách tường minh như
now.to_tz("Europe/Paris") PlainDateTime("2023-10-28 22:00")sẽ không vô tình bị trộn với các kiểu khác- Khi gọi
add(hours=6), nó phát raNaiveArithmeticWarningđể cảnh báo rằng việc điều chỉnh giờ địa phương đang bỏ qua DST - Cần giả định múi giờ một cách tường minh bằng
assume_tz("Europe/Amsterdam")
- Khi gọi
- Khi thực hiện
add(hours=6)trênZonedDateTime, kết quả sẽ phản ánh quá trình chuyển DST và trả về2023-10-29 03:00:00+01:00[Europe/Amsterdam] - Hỗ trợ so sánh, làm tròn và cắt bớt, phân tích định dạng ISO8601·RFC3339·RFC2822, và định dạng theo mẫu tùy chỉnh
- Khi cần, có thể chuyển đổi sang thư viện chuẩn
datetimehoặc chuyển ngược lại - Cách dùng chi tiết có thể xem tại feature overview và API reference
Giới hạn và chính sách ổn định
- Phạm vi hỗ trợ là lịch Gregory kéo dài từ năm 1 đến năm 9999
- Độ lệch múi giờ bị giới hạn ở số nguyên theo giây, nhất quán với IANA TZ DB
- Whenever tuân theo semantic versioning
- Trước phiên bản 1.0, API có thể thay đổi trong các bản phát hành minor
- Các breaking change được mô tả chi tiết trong changelog
- API được type đầy đủ, nên trình kiểm tra kiểu hoặc IDE có thể hỗ trợ thích ứng với thay đổi API
Giấy phép và ảnh hưởng thiết kế
- Whenever được phát hành theo MIT License
- Binary wheel bao gồm các phụ thuộc Rust với giấy phép tương tự, dễ dãi như MIT, Apache-2.0, v.v.
- Noda Time và Joda Time là những thư viện tiên phong với cách tiếp cận phân tách kiểu theo khái niệm, và hệ phân cấp kiểu của Noda Time là nguồn cảm hứng trực tiếp cho thiết kế của Whenever
- Temporal truyền cảm hứng cho cách xử lý các trường hợp phức tạp quanh tính mơ hồ của DST và việc làm tròn
- Mô-đun
datetimecủa Python được dùng rộng rãi cho xử lý ngày giờ mức thấp trong triển khai thuần Python của Whenever - Một số ý tưởng cũng được mượn từ Jiff
- Biểu đồ benchmark so sánh được phỏng theo từ dự án Ruff
Chưa có bình luận nào.