Changelog

0.10.0b1 (2026-03-20)

A big release with several breaking changes and improvements. Highlights are the new delta API, customizable string formatting and parsing, and since()/until() methods for calculating differences between datetimes. See the full list below.

Breaking changes

  • DateTimeDelta and DateDelta have been replaced by ItemizedDelta and ItemizedDateDelta, respectively. The helper functions for creating calendar deltas (years(), months(), weeks(), days()) have also been deprecated.

    The new deltas are fully un-normalized, meaning “90 minutes” and “1 hour and 30 minutes” are distinct values. They implement the Mapping interface and support a rich set of operations including add(), subtract(), total(), in_units(), replace(), and sign().

    Rationale: the “partially” normalized approach was confusing to users. A fully un-normalized approach also better fits the new API for calculating deltas between datetimes. This approach is also more consistent with other libraries, and allows for more control over formatting and parsing of deltas.

    Migration:

    • Replace DateDelta(...) with ItemizedDateDelta(...).

    • Replace DateTimeDelta(...) with ItemizedDelta(...).

    • Replace years(), months(), weeks(), days() helper functions with ItemizedDateDelta(years=...), etc. Or, if passing to a datetime method, use keyword arguments directly (e.g. dt.add(years=1, months=2)).

    • Replace .in_months_days() and .in_months_days_secs_nanos() with .in_units(['months', 'days']) and .in_units(['months', 'days', 'seconds', 'nanoseconds']), respectively.

    • The Date +/- operators with DateDelta are deprecated; use add()/subtract() instead.

    • The Date - operator between two dates is deprecated; use since() or subtract() instead.

  • The ignore_dst parameter (which was used to enable DST-unsafe operations) has been replaced by a warnings mechanism that allows users to suppress or escalate DST-related warnings using context managers or Python’s standard warning filters.

    Rationale: The ignore_dst parameter was a source of confusion, and made the OffsetDateTime APIs less compatible.

    Migration:

    • Remove ignore_dst=True from all calls.

    • For OffsetDateTime operations: use with ignore_potentially_stale_offset_warning(): ... to suppress warnings.

    • For PlainDateTime operations: use with ignore_timezone_unaware_arithmetic_warning(): ... to suppress warnings.

    • Alternatively, use Python’s warnings.filterwarnings() to suppress PotentiallyStaleOffsetWarning or TimeZoneUnawareArithmeticWarning.

  • Behavior of an edge case is changed: disambiguation of non-existent times as a result of calendar arithmetic (or replace()) no longer tries to reuse the previous offset. This change also fixes a rare bug in case a timezone transition skips an entire day (like the Samoa timezone did in 2011) (#252).

    Rationale: Unlike the case of repeated times, reusing the previous offset for non-existent times doesn’t have the advantage of preventing unexpected jumps in time. The new behavior is consistent with other libraries.

  • Dropped Python 3.9 support

    Rationale: Python 3.9 is EOL since October 2025. Python 3.9 only accounts for less than 0.1% of downloads.

  • Removed format_common_iso() and parse_common_iso() methods. Use format_iso() and parse_iso() instead. These have been deprecated since 0.9.0.

  • The round() methods are stricter about keyword-only and positional-only arguments.

Deprecated

  • TimeDelta.in_hours(), .in_minutes(), .in_seconds(), .in_milliseconds(), .in_microseconds(), .in_nanoseconds(), .in_days_of_24h(), and .in_hrs_mins_secs_nanos(). Use total() or in_units() instead.

  • The difference() method on datetimes is deprecated. Instead, use the - subtraction operator, or the new since() method.

  • Date.days_since() and Date.days_until(). Use since() and until() with total='days' instead.

  • py_date(), py_time(), py_datetime(), and py_timedelta(). Use the new to_stdlib() method instead, which provides a consistent name across all types.

  • from_py_date(), from_py_time(), from_py_datetime(), and from_py_timedelta(). Use the constructor directly instead (e.g. Date(datetime.date(...))).

  • parse_strptime() methods on OffsetDateTime and PlainDateTime. Use the new parse() method instead.

Improved

  • New since() and until() methods on Date, ZonedDateTime, OffsetDateTime, and PlainDateTime for calculating the difference between two values in terms of specific calendar/time units.

  • New format() and parse() methods on Date, Time, PlainDateTime, OffsetDateTime, ZonedDateTime, and Instant for custom format/parse patterns. Example: Date(2024, 3, 15).format("YYYY/MM/DD")"2024/03/15". These types also support __format__, enabling f-string usage: f"{date:YYYY/MM/DD}". See the pattern format documentation for details.

  • New TimeDelta.total() and TimeDelta.in_units() methods for converting a time delta into specific units.

  • New TimeDelta.add() and TimeDelta.subtract() methods. The operators + and - were supported already, but these methods make it easier for simple operations, as well as making the API more consistent with other classes.

  • New OffsetDateTime.assume_tz() method for associating an offset datetime with a timezone.

  • round() methods now support four new rounding modes: trunc, expand, half_trunc, and half_expand. They also now support larger and irregular values for increment. TimeDelta.round() now supports days and weeks as rounding units (with a warning about 24-hour days).

  • All types that have a Python standard library equivalent now also accept these objects in the constructor. For example: Date(datetime.date(2024, 1, 1)).

  • New ZonedDateTime.dst_offset() and ZonedDateTime.tz_abbrev() methods for querying timezone metadata (DST offset adjustment and timezone abbreviation).

  • Warning classes (PotentiallyStaleOffsetWarning, TimeZoneUnawareArithmeticWarning, DaysNotAlways24HoursWarning) and corresponding context managers (ignore_potentially_stale_offset_warning(), ignore_timezone_unaware_arithmetic_warning(), ignore_days_not_always_24h_warning()) for fine-grained control over DST-related warnings.

  • A huge revamp and expansion of the documentation. The structure and navigability of API reference and overview pages has been improved. Several new pages have been added, including:

    • An explanation of the fundamental concepts of time

    • An overview of Python’s datetime pitfalls

    • Explanation of the rounding API

Fixed

  • (Pure-Python version) Fixed incorrect behavior of < operator between Time instances if nanoseconds are involved.

0.9.5 (2026-01-11)

Fix issue where not all windows wheels were built and uploaded (#317)

0.9.4 (2025-12-14)

Added support for free-threaded Python (#166)

0.9.3 (2025-10-16)

Fixed incorrect offsets for some timezones before the start of their first recorded transition (typically pre-1950) (#296)

0.9.2 (2025-09-29)

Methods that take an int, float, or str now also accept subclasses of these types. This is consistent with the behavior of the standard library and improves compatibility with libraries like numpy and pandas (#260)

0.9.1 (2025-09-28)

Added ZonedDateTime.now_in_system_tz() and ZonedDateTime.from_system_tz() as convenience methods to ease migration away from SystemDateTime.

0.9.0 (2025-09-25)

Breaking Changes

  • SystemDateTime has been removed and merged into ZonedDateTime

    To create a more consistent and intuitive API, the SystemDateTime class has been removed. Its functionality is now fully integrated into an enhanced ZonedDateTime, which now serves as the single, canonical class for all timezone-aware datetimes, including those based on the system’s local timezone.

    Rationale:

    The SystemDateTime class, while useful, created several challenges that compromised the library’s consistency and predictability:

    • Inconsistent Behavior: Methods like replace() and add() on a SystemDateTime instance would use the current system timezone definition, not necessarily the one that was active when the instance was created. This could lead to subtle and unpredictable bugs if the system timezone changed during the program’s execution.

    • API Division: Despite having nearly identical interfaces, SystemDateTime and ZonedDateTime were not interchangeable. A function expecting a ZonedDateTime could not accept a SystemDateTime, forcing users to write more complex code with Union type hints.

    • Maintenance Overhead: Maintaining two parallel APIs for timezone-aware datetimes led to significant code duplication and a higher maintenance burden.

    This change unifies the API by integrating system timezone support directly into ZonedDateTime, providing a single, consistent way to handle all timezone-aware datetimes. The original use cases for SystemDateTime are fully supported by the improved ZonedDateTime.

    This new, unified approach also provides two major benefits:

    • Performance: Operations on a ZonedDateTime representing a system time are now orders of magnitude faster than they were on the old SystemDateTime.

    • Cross-Platform Consistency: The new whenever.reset_system_tz() function provides a reliable, cross-platform way to update the library’s view of the system timezone, replacing the previous reliance on the Unix-only time.tzset().

    Migration:

    • Replace all SystemDateTime with ZonedDateTime in all type hints.

    • Replace SystemDateTime.now() with ZonedDateTime.now_in_system_tz() (whenever >=0.9.1) or Instant.now().to_system_tz() (whenever <0.9.1)

    • Replace SystemDateTime(...) constructor calls with ZonedDateTime.from_system_tz(...) (whenever >=0.9.1) or PlainDateTime(...).assume_system_tz() (whenever <0.9.1)

    • Check calls to .to_system_tz() and .assume_system_tz(): these methods now return a ZonedDateTime instance. In most cases, no code change is needed.

    • Instead of time.tzset(), use whenever.reset_system_tz() to update the system timezone (for whenever only).

  • ZonedDateTime instances with a system timezone may in rare cases not have a known IANA timezone ID (the tz property will be None). This is an unfortunate limitation of some platforms. Such ZonedDateTime instances can still be used for all operations, and will account for DST correctly. However, these instances cannot be pickled, and their ISO format will not be able to include the timezone ID.

    Rationale: This is an necessary compromise for broad system timezone support. Other libraries (and Python’s own zoneinfo) have similar limitations.

  • The repr() of all classes now includes quotes: e.g. Date("2023-10-05"). Since all constructors now also accept ISO 8601 strings, the repr() output can be directly used as input and thus eval(repr(obj)) == obj.

    Rationale: This makes the types easier to use in interactive sessions and tests. A round-trippable repr() is also a common expectation for primitive types.

  • Renamed [format|parse]_common_iso methods to [format|parse]_iso. The old methods are still available (but deprecated) to ease the transition.

    Rationale: The “common” qualifier is no longer necessary because these methods have been expanded to handle a wider range of ISO 8601 formats.

  • Removed the deprecated local() methods (use to_plain() instead).

  • Removed the deprecated instant() method (use to_instant() instead).

Improved

  • All classes can now be directly instantiated from an ISO 8601 formatted string passed as a sole argument. For example, Date("2023-10-05") is equivalent to Date(2023, 10, 5) (which is still supported, of course).

  • Customizable ISO 8601 Formatting: The format_iso() methods now accept parameters to customize the output. You can control the separator (e.g., 'T' or ' '), the smallest unit (from hour to nanosecond), and toggle the “basic” (compact) or “extended” format.

    Also, the formatting is now significantly faster. Up to 5x faster for ZonedDateTime, which is now 10x faster than the standard library’s datetime.isoformat().

Fixed

  • Resolved a memory leak in the Rust extension where timezone objects that were no longer in use were not properly evicted from the cache.

  • Fixed a rare bug in determining the UTC offset for times far in the future

  • Fixed PlainDateTime constructor raising TypeError instead of ValueError when passed invalid parameters.

  • TZ IDs starting with a ./ are now properly rejected. Other path traversal attempts were already handled correctly.

  • More robust timezone refcounting in the Rust extension, preventing crashes in rare cases (#270)

  • Panics in Rust extension no longer crash the interpreter, raise RuntimeError instead

0.8.9 (2025-09-21)

  • Fixed not all test files included in source distribution (#266)

  • Uploaded missing Python 3.14 wheels

0.8.8 (2025-07-24)

  • Add wheels for Python 3.14 now that its ABI is stable.

  • Add a pure Python wheel so platforms without binary wheels can use whenever’s pure Python version without having to go through the source build process (#256)

0.8.7 (2025-07-18)

  • Fix some MIN and MAX constants not documented in the API reference.

  • Add Time.MIN alias for Time.MIDNIGHT for consistency (#245)

  • Fix bug in rounding of midnight ZonedDateTime values in “ceil”/day mode (#249)

0.8.6 (2025-06-23)

  • Improve error message of ZonedDateTime.from_py_datetime() in case the datetime’s ZoneInfo.key is None.

  • Fix performance regression in Date.day_of_week() (#244)

0.8.5 (2025-06-09)

  • Relax build requirements. It now only depends on setuptools_rust if opting to build the Rust extension (#240)

  • Fixed not all Rust files included in source distribution.

  • Update some outdated docstrings.

0.8.4 (2025-05-28)

  • Fix Pydantic JSON schema generation in certain contexts, which affected FastAPI doc generation.

0.8.3 (2025-05-22)

  • Ensure Pydantic parsing failures of whenever types always result in a proper ValidationError, not a TypeError.

0.8.2 (2025-05-21)

  • Allow Pydantic to generate JSON schema for whenever types. This is particularly useful for generating OpenAPI schemas for FastAPI.

0.8.1 (2025-05-21)

New

  • Added support for Pydantic serialization/deserialization of whenever types in the ISO 8601 format. This functionality is in preview, and may be subject to change in the future. (#175)

Fixed

  • Weekday enum values from the Rust extension are now pickleable.

  • Solve crash if Python’s garbage collection occurs while the Rust extension is still initializing.

  • Fixed a crash in parsing malformed fractional TimeDelta seconds (#234)

Improved

  • Time.from_py() now ignores any tzinfo, instead of raising an error.

  • A comprehensive refactor of the Rust extension module eliminates most unnecessary unsafe code, making it safer and more idiomatic.

0.8.0 (2025-05-01)

A big release with several improvements and breaking changes that lay the groundwork for the eventual 1.0 release.

Improved

  • Timezone operations in the Rust extension are now a lot faster (5-8x), due to a new implementation replacing the use of the standard library zoneinfo module. (#202)

  • The parse_common_iso() methods support a wider range of ISO 8601 formats. See the updated documentation for details. (#204)

  • Added an “examples” page to the documentation with practical snippets. (#198)

  • RFC2822 parsing is now more robust and faster. (#200)

  • Import speed is improved significantly for both the Rust and pure Python versions (#228)

Breaking changes

  • LocalDateTime has been renamed to PlainDateTime, and the local() method has been renamed to to_plain(). The old names are still available (but deprecated) to ease the transition.

    Rationale: In observing adoption of the library, the term “local” causes confusion for a number of users, since the term “local” is so overloaded in the Python world. PlainDateTime is used in Javascript’s Temporal API, and seems to resonate better with users. See the FAQ for a detailed discussion on the name.

  • Rename instant() method to to_instant()

    Rationale: The new name is more consistent with the rest of the API.

  • Removed the [format|parse]_rfc3339 method.

    Rationale: The improved ISO 8601 parsing method is now RFC 3339 compatible, making this method unnecessary. Strict RFC 3339 parsing can still be done with strptime, if desired

  • Passing invalid timezone names now raise a whenever.TimeZoneNotFoundError (subclass of ValueError) instead of zoneinfo.ZoneInfoNotFoundError (subclass of KeyError).

    Rationale: This ensures whenever is independent of the zoneinfo module, and its particularities don’t leak into the whenever API.

  • TimeDelta.from_py_timedelta no longer accepts timedelta subclasses.

    Rationale: timedelta subclasses (like pendulum.Duration) often add other time components, which cannot be guaranteed to be handled correctly.

  • The strptime methods have been renamed parse_strptime, and its format argument is now a keyword-only argument.

    Rationale: This ensures all parsing methods have the parse_ prefix, helping in API consistency and discoverability. The keyword-only argument helps distinguish between the format string and the string to parse.

  • The InvalidOffset exception has been renamed InvalidOffsetError

    Rationale: this more clearly indicates that this is an error condition. See #154 for discussion.

  • SkippedTime and RepeatedTime are now subclasses of ValueError.

    Rationale: it ensures these exceptions can be caught together with other exceptions like InvalidOffsetError and TimeZoneNotFoundError during parsing.

  • Whenever is no longer affected by ZoneInfo.clear_cache() or zoneinfo.reset_tzpath(), since it now uses its own cache with corresponding methods.

    Rationale: This ensures whenever is independent of zoneinfo in both Rust and pure Python implementations.

Fixed

  • Improved robustness of date calculations at extreme boundaries. (#219)

  • Fixed a bug in the pure-Python version of ZonedDateTime.exact_eq() that could cause false positives in some cases.

  • Fixed incorrect type stubs for day_length() and start_of_day() methods.

  • Corrected the description of parameters accepted by now(). (#213)

0.7.3 (2025-03-19)

  • Fixed type annotations of Weekday enum values, so they are properly marked as int.

0.7.2 (2025-02-25)

  • Fixed round() method behaving incorrectly when increment argument is not passed explicitly (#209)

0.7.1 (2025-02-24)

  • Date.add and Date.subtract now support DateDelta to be passed as sole positional argument. This is consistent with the behavior of datetime classes.

  • Improved performance and robustness of date calculations at extreme boundaries

  • Minor fixes to docstrings

0.7.0 (2025-02-20)

This release adds rounding functionality, along with a small breaking change (see below).

Breaking changes

  • TimeDelta.py_timedelta() now truncates nanoseconds to microseconds instead of rounding them. Use the new round() method to customize rounding behavior.

Added

  • Added round() to all datetime, Instant, and TimeDelta classes

  • Add floor division and modulo operators to TimeDelta

  • Add is_ambiguous(), day_length() and start_of_day() to SystemDateTime, for consistency with ZonedDateTime.

  • Improvements to documentation

0.6.17 (2025-01-30)

  • Added day_length() and start_of_day() methods to ZonedDateTime to make it easier to work with edge cases around DST transitions, and prepare for implementing rounding methods in the future.

  • Fix cases in type stubs where positional-only arguments weren’t marked as such

0.6.16 (2024-12-22)

  • Fix bug in ZonedDateTime repr() that would mangle some timezone names

  • Make disambiguate argument optional, defaulting to "compatible".

    Rationale: This required parameter was a frequent source of irritation for users. Although “explicit is better than implicit”, other modern libraries and standards also choose an (implicit) default. For those that do want to enforce explicit handling of ambiguous times, a special stubs file or other plugin may be introduced in the future.

  • Various small fixes to the docs

0.6.15 (2024-12-11)

  • Add Date.days_[since|until] methods for calculating the difference between two dates in days only (no months or years)

  • Improve docs about arithmetic rules for calendar and time units.

0.6.14 (2024-11-27)

  • Ensure docstrings and error messages are consistent in Rust extension as well as the pure-Python version

  • Remove undocumented properties hour/minute/etc from Instant that were accidentally left in the Rust extension.

  • exact_eq() now also raises TypeError in the pure Python version when comparing different types.

0.6.13 (2024-11-17)

Added

  • Make from_py_datetime() on Instant/OffsetDateTime less pedantic. They now accept any aware datetime

  • New Date.today_in_system_tz() convenience method

Fixed

  • Parsing UTC offsets with out-of-range minute components (e.g. 06:79) now raises the expected parsing failure.

  • Note in parse_rfc2822() docstring that it doesn’t (yet) validate the input, due to limitations in the underlying parser.

0.6.12 (2024-11-08)

  • Fixed format_rfc3339() docstrings that incorrectly included a T separator. Clarified that T can be added by using the format_common_iso() method instead. (#185)

0.6.11 (2024-11-04)

Added

  • Added YearMonth and MonthDay classes for working with year-month and month-day pairs

Fixed

  • whenever.__version__ is now also accessible when Rust extension is used

0.6.10 (2024-10-30)

Improved

  • Improve method documentation and autocomplete support (#172, #173, #176)

Fixed

  • Remove lingering undocumented offset on Instant

  • Fix incorrect LocalDateTime.difference return type annotation

0.6.9 (2024-09-12)

  • Clarify DST-related error messages (#169)

0.6.8 (2024-09-05)

  • Fix object deallocation bug that caused a crash in rare cases (#167)

0.6.7 (2024-08-06)

  • Add Python 3.13 binary wheels, now that its ABI is stable

  • Small improvements to import speed

0.6.6 (2024-07-27)

  • Fix potential memory leak in .now() if time-machine is used

0.6.5 (2024-07-27)

  • from_timestamp now also accepts floats, to ease porting code from datetime (#159)

  • Fixed incorrect fractional seconds when parsing negative values in from_timestamp methods.

  • Fix some places where ValueError was raised instead of TypeError

0.6.4 (2024-07-26)

  • Add helper patch_current_time for patching current time in whenever (only) (#147)

  • Support patching the current time with time-machine (#147)

  • Remove undocumented year/month/day/offset properties from Instant

  • Reduce size of binary distributions

  • Clarify contribution guidelines

0.6.3 (2024-07-13)

  • Improve robustness and speed of keyword argument parsing in Rust extension (#149)

  • Add more answers to common questions in the docs and FAQ (#148, #150)

0.6.2 (2024-07-04)

  • Add third-party licenses to distributions

0.6.1 (2024-07-04)

  • Small updates to project metadata

0.6.0 (2024-07-04)

A big release touting a Rust extension module and an API more consistent with other modern libraries.

Added or improved

  • Implement as a Rust extension module, leading to a big speedup

  • Add replace_date and replace_time methods to datetimes.

  • Add Date.MIN and Date.MAX constants.

  • from_py_* methods are more robust.

  • The pickle format for most types is now more efficient.

Breaking changes

  • UTCDateTime is now Instant. Removed methods that were specific to UTC.

    Rationale: Instant is simpler and more conceptually clear. It also avoids the mistake of performing calendar arithmetic in UTC.

  • NaiveDateTime is now LocalDateTime

    Rationale: “Local” is more descriptive for describing the concept of “wall clock” time observed locally by humans. It’s also consistent with other libraries and standards.

  • Nanosecond precision is now the default for all datetimes and deltas. nanosecond is a keyword-only argument for all constructors, to prevent mistakes porting code from datetime (which uses microseconds).

    Rationale: Nanosecond precision is the standard for modern datetime libraries.

  • Unified [from_]canonical_format methods with [from_]common_iso8601 methods into [format|parse]_common_iso methods.

    Rationale: This cuts down on the number of methods; the performance benefits of separate methods aren’t worth the clutter.

  • Timestamp methods now use integers instead of floats. There are now separate methods for seconds, milliseconds, and nanoseconds.

    Rationale: This prevents loss of precision when converting to floats, and is more in line with other modern libraries.

  • Renamed [from_][rfc3339|rfc2822] methods to [format|parse]_[rfc3339|rfc2822].

    Rationale: Consistency with other methods.

  • Added explicit ignore_dst=True flag to DST-unsafe operations such as shifting an offset datetime.

    Rationale: Previously, DST-unsafe operations were completely disallowed, but to a frustrating degree. This flag is a better alternative than having users resort to workarounds.

  • Renamed as_utc, as_offset, as_zoned, as_local to to_utc, to_fixed_offset, to_tz, to_system_tz, and the NaiveDateTime.assume_* methods accordingly

    Rationale: “to” better clarifies a conversion is being made (not a replacement), and “fixed offset” and “tz” are more descriptive than “offset” and “zoned”.

  • disambiguate= is non-optional for all relevant methods. The only exception is the constructor, which defaults to “raise”.

    Rationale: This makes it explicit how ambiguous and non-existent times are handled.

  • Removed weakref support.

    Rationale: The overhead of weakrefs was too high for such primitive objects, and the use case was not clear.

  • Weekdays are now an enum instead of an integer.

    Rationale: Enums are more descriptive and less error-prone, especially since ISO weekdays start at 1 and Python weekdays at 0.

  • Calendar units in Date[Time]Delta can now only be retrieved together. For example, there is no delta.months or delta.days anymore, delta.in_months_days() should be used in this case.

    Rationale: This safeguards against mistakes like (date1 - date2).days which would only return the days component of the delta, excluding months. Having to call in_months_days() is more explicit that both parts are needed.

  • Units in delta cannot be different signs anymore (after normalization).

    Rationale: The use case for mixed sign deltas (e.g. 2 months and -15 days) is unclear, and having a consistent sign makes it easier to reason about. It also aligns with the most well-known version of the ISO format.

  • Calendar units are normalized, but only in so far as they can be converted strictly. For example, 1 year is always equal to 12 months, but 1 month isn’t equal to a fixed number of days. Refer to the delta docs for more information.

    Rationale: This is more in line with TimeDelta which also normalizes.

  • Renamed AmbiguousTime to RepeatedTime.

    Rationale: The new name is more descriptive for repeated times occurring twice due to DST. It also clarifies the difference between “repeated” times and “ambiguous” times (which can also refer to non-existent times).

  • Dropped Python 3.8 support

    Rationale: Rust extension relies on C API features added in Python 3.9. Python 3.8 will be EOL later this year.

0.5.1 (2024-04-02)

  • Fix LocalSystemDateTime.now() not setting the correct offset (#104)

0.5.0 (2024-03-21)

Breaking changes

  • Fix handling of -0000 offset in RFC2822 format, which was not according to the standard. NaiveDateTime can now no longer be created from this format.

  • DateDelta canonical format now uses P prefix.

Improved

  • Add explicit ISO8601 formatting/parsing methods to datetimes, date, time, and deltas.

  • Add missing Date.from_canonical_format method.

  • Separate docs for deltas and datetimes.

  • NaiveDateTime.assume_offset now also accepts integers as hour offsets.

0.4.0 (2024-03-13)

A big release with the main feature being the addition of date/time deltas. I’ve also tried to bundle as many small breaking changes as possible into this release, to avoid having to do them in the future.

Breaking changes

  • LocalDateTime renamed to LocalSystemDateTime.

    Rationale: The LocalDateTime name is used in other libraries for naive datetimes, and the new name is more explicit.

  • LocalSystemDateTime no longer adjusts automatically to changes in the system timezone. Now, LocalSystemDateTime reflects the system timezone at the moment of instantiation. It can be updated explicitly.

    Rationale: The old behavior was dependent on too many assumptions, and behaved unintuitively in some cases. It also made the class dependent on shared mutable state, which made it hard to reason about.

  • The disambiguate= argument now also determines how non-existent times are handled.

    Rationale: This makes it possible to handle both ambiguous and non-existent times gracefully and in a consistent way. This behavior is also more in line with the RFC5545 standard, and Temporal.

  • from_naive() removed in favor of methods on NaiveDateTime. For example, UTCDateTime.from_naive(n) becomes n.assume_utc().

    Rationale: It’s shorter, and more explicit about assumptions.

  • Renamed ZonedDateTime.disambiguated() to .is_ambiguous().

    Rationale: The new name distinguishes it from the disambiguate= argument, which also affects non-existent times.

  • Replaced .py property with .py_datetime() method.

    Rationale: Although it currently works fine as a property, this may be changed in the future if the library no longer contains a datetime internally.

  • Removed properties that simply delegated to the underlying datetime object: tzinfo, weekday, and fold. date and time now return whenever.Date and whenever.Time objects.

    Rationale: Removing these properties makes it possible to create improved versions. If needed, these properties can be accessed from the underlying datetime object with .py_datetime().

  • Renamed .canonical_str() to .canonical_format().

    Rationale: A more descriptive name.

  • Renamed DoesntExistInZone to SkippedTime, Ambiguous to AmbiguousTime.

    Rationale: The new names are shorter and more consistent.

  • Renamed min and max to MIN and MAX.

    Rationale: Consistency with other uppercase class constants

Improved

  • Added a disambiguation="compatible" option that matches the behavior of other languages and the RFC5545 standard.

  • Shortened the repr() of all types, use space separator instead of T.

  • Added sep="T" or " " option to canonical_format()

  • OffsetDateTime constructor and methods creating offset datetimes now accept integers as hour offsets.

  • Added Date and Time classes for working with dates and times separately.

0.3.4 (2024-02-07)

  • Improved exception messages for ambiguous or non-existent times (#26)

0.3.3 (2024-02-04)

  • Add CPython-maintained tzdata package as Windows dependency (#32)

0.3.2 (2024-02-03)

  • Relax overly strict Python version constraint in package metadata (#33)

0.3.1 (2024-02-01)

  • Fix packaging metadata issue involving README and CHANGELOG being installed in the wrong place (#23)

0.3.0 (2024-01-23)

Breaking changes

  • Change pickle format so that backwards-compatible unpickling is possible in the future.

Added

  • Added strptime() to UTCDateTime, OffsetDateTime and NaiveDateTime.

  • Added rfc2822()/from_rfc2822() to UTCDateTime, OffsetDateTime and NaiveDateTime.

  • Added rfc3339()/from_rfc3339() to UTCDateTime and OffsetDateTime

0.2.1 (2024-01-20)

  • added days() timedelta alias

  • Improvements to README, other docs

0.2.0 (2024-01-10)

Breaking changes

  • Disambiguation of local datetimes is now consistent with zoned datetimes, and is also run on replace().

  • Renamed:

    • from_strfrom_canonical_str

    • to_utc/offset/zoned/localas_utc/offset/zoned/local.

    • ZonedDateTime.zoneZonedDateTime.tz

Added

  • Support comparison between all aware datetimes

  • support subtraction between all aware datetimes

  • Convenience methods for converting between aware/naive

  • More robust handling of zoned/local edge cases

Docs

  • Cleaned up API reference

  • Added high-level overview

0.1.0 (2023-12-20)

  • Implement OffsetDateTime, ZonedDateTime and LocalDateTime

0.0.4 (2023-11-30)

  • Revert to pure Python implementation, as Rust extension disadvantages outweigh its advantages

  • Implement NaiveDateTime

0.0.3 (2023-11-16)

  • Implement basic UTCDateTime

0.0.2 (2023-11-10)

  • Empty release with Rust extension module

0.0.1

  • Dummy release