Skip to content

Releases: ariebovenberg/whenever

0.9.3

16 Oct 19:45

Choose a tag to compare

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

0.8.10

16 Oct 20:44

Choose a tag to compare

Backport of timezone fixes from 0.9.x to 0.8.x that affected some timezones far into the future or past

0.9.2

29 Sep 18:42

Choose a tag to compare

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

28 Sep 19:00

Choose a tag to compare

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

0.9.0

28 Sep 18:59

Choose a tag to compare

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.9.0rc1

25 Sep 18:51

Choose a tag to compare

0.9.0rc1 Pre-release
Pre-release

A small addition to 0.9.0rc0 that fixes some bugs, and makes the transition from format/parse_common_iso to format/parse_iso easier by deprecating (not removing) the old names. Full list of changes since 0.8.9 below:

Breaking Changes

  • SystemDateTime has been removed and its functionality is now integrated into ZonedDateTime.
    Migration:

    • SystemDateTime.now() can be replaced with Instant.now().to_system_tz().
    • to_system_tz() and assume_system_tz() now return a ZonedDateTime instead of a SystemDateTime.

    Rationale: The SystemDateTime class was an awkward corner of the API,
    creating inconsistencies and overlapping with ZonedDateTime.
    This change unifies the API, providing a single, consistent way to handle
    all timezone-aware datetimes. The original use cases are fully supported
    by the improved ZonedDateTime.

  • 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.

  • 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).
    The repr() of all classes now includes quotes, so that the output
    can be directly used as input and thus eval(repr(obj)) == obj.

    Rationale: This makes the types a lot easier to use in interactive sessions
    and tests. It also makes repr() round-trippable, which is 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

  • 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.9.0rc0

22 Sep 08:09

Choose a tag to compare

0.9.0rc0 Pre-release
Pre-release

First release candidate for the next big release: 0.9.0.

Breaking Changes

  • SystemDateTime has been removed and its functionality is now integrated into ZonedDateTime.
    Migration:

    • SystemDateTime.now() can be replaced with Instant.now().to_system_tz().
    • to_system_tz() and assume_system_tz() now return a ZonedDateTime instead of a SystemDateTime.

    Rationale: The SystemDateTime class was an awkward corner of the API,
    creating inconsistencies and overlapping with ZonedDateTime.
    This change unifies the API, providing a single, consistent way to handle
    all timezone-aware datetimes. The original use cases are fully supported
    by the improved ZonedDateTime.

  • 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).
    The repr() of all classes now includes quotes, so that the output
    can be directly used as input and thus eval(repr(obj)) == obj.

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

  • Renamed [format|parse]_common_iso methods to [format|parse]_iso.

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

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

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

Improved

  • 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)

0.8.9

21 Sep 13:11

Choose a tag to compare

  • Fixed not all test files included in source distribution (#266)
  • Uploaded missing Python 3.14 wheels

0.8.9 is a small release while I work on 0.9.0. See here for the changes expected in the next major release.

0.8.8

24 Jul 20:59

Choose a tag to compare

  • 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

18 Jul 19:10

Choose a tag to compare

  • 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)