Skip to content

0.10.0

Latest

Choose a tag to compare

@ariebovenberg ariebovenberg released this 05 Apr 18:56
· 19 commits to main since this release

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 per-method keyword arguments or Python's standard warning filters.

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

    Migration:

    • Replace ignore_dst=True with the appropriate keyword argument:
      • OffsetDateTime methods: stale_offset_ok=True
      • PlainDateTime methods: naive_arithmetic_ok=True
      • TimeDelta methods: days_assumed_24h_ok=True
    • Alternatively, use Python's warnings.filterwarnings() to suppress StaleOffsetWarning, NaiveArithmeticWarning, or DaysAssumed24HoursWarning.
    • ignore_dst is still accepted (with a deprecation warning) and will be removed in a future release.
  • 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.
  • 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.
  • ZonedDateTime.start_of_day(). Use start_of("day") instead.

Added or improved

  • Parsing methods now accept leap seconds (second value of 60), normalizing them to 59. This applies to ISO 8601, RFC 2822, and custom format strings.
  • New ZonedDateTime.next_transition() and ZonedDateTime.prev_transition() methods for finding the next or previous UTC offset transition (e.g. DST change) relative to the current datetime. Returns None for timezones without transitions (e.g. UTC or fixed-offset).
  • 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 (StaleOffsetWarning, NaiveArithmeticWarning, DaysAssumed24HoursWarning) and corresponding per-method keyword arguments (stale_offset_ok, naive_arithmetic_ok, days_assumed_24h_ok) for fine-grained control over DST-related warnings.
  • New IsoWeekDate type for representing ISO calendar week dates. Construct with IsoWeekDate(year, week, weekday) or parse with IsoWeekDate("2024-W01-1"). Convert from Date with Date.iso_week_date().
  • New calendar methods on Date: day_of_year(), days_in_month(), days_in_year(), in_leap_year(), next_day(), prev_day(), nth_weekday_of_month(), nth_weekday(), start_of(), end_of().
  • New calendar methods on PlainDateTime, ZonedDateTime, OffsetDateTime: day_of_year(), days_in_month(), days_in_year(), in_leap_year(), start_of(), end_of().
  • New calendar methods on YearMonth: days_in_month(), days_in_year(), in_leap_year().
  • 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
  • YearMonth, MonthDay, Weekday, and IsoWeekDate are now implemented in pure Python always, reducing the compiled extension size.
  • Instant.add/subtract now support passing TimeDelta instances. Instead of rejecting days and weeks, these methods now emit a warning about DST issues, consistent with the behavior of TimeDelta.
  • SQLAlchemy support, see https://pypi.org/project/whenever-sqlalchemy/

Fixed

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