Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,43 +12,43 @@

- `SystemDateTime` has been removed and merged into `ZonedDateTime`

To create a more consistent and intuitive API, the SystemDateTime class
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
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
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,
* 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.
* 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.
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
* 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().
`time.tzset()`.

**Migration:**
- Replace all `SystemDateTime` with `ZonedDateTime` in all type hints.
Expand Down
4 changes: 4 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Concrete classes
.. autoclass:: whenever.Instant
:members:
from_utc,
format_iso,
format_rfc2822,
parse_rfc2822,
add,
Expand All @@ -63,6 +64,7 @@ Concrete classes

.. autoclass:: whenever.PlainDateTime
:members:
format_iso,
assume_utc,
assume_fixed_offset,
assume_tz,
Expand All @@ -78,6 +80,7 @@ Concrete classes

.. autoclass:: whenever.OffsetDateTime
:members:
format_iso,
format_rfc2822,
parse_rfc2822,
parse_strptime,
Expand All @@ -86,6 +89,7 @@ Concrete classes

.. autoclass:: whenever.ZonedDateTime
:members:
format_iso,
now_in_system_tz,
from_system_tz,
tz,
Expand Down
59 changes: 47 additions & 12 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,15 @@ parts of the standard to support. ``whenever`` targets the most common
and widely-used subset of the standard, while avoiding the more obscure
and rarely-used parts, which are often the source of confusion and bugs.

.. note::

The ISO formats in ``whenever`` are designed so you can format and parse
them without losing information.
This makes it ideal for JSON serialization and other data interchange formats.

Parsing
*******

``whenever``'s
:meth:`~whenever._BasicConversions.parse_iso` methods take
mostly `after Temporal <https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar>`_,
Expand All @@ -696,6 +705,10 @@ namely:
- In the duration format, the ``W`` unit may be used alongside other calendar units
(``Y``, ``M``, ``D``).


Formatting
**********

Below are the default string formats you get for calling each type's
:meth:`~whenever._BasicConversions.format_iso` method:

Expand All @@ -711,22 +724,44 @@ Below are the default string formats you get for calling each type's
| :class:`~whenever.OffsetDateTime` | ``YYYY-MM-DDTHH:MM:SS±HH:MM`` |
+-----------------------------------------+------------------------------------------------+

Where applicable, the outputs can be customized using the ``unit``, ``basic``, ``sep``,
and ``tz`` keyword arguments. See the method documentation for details.
Where applicable, the outputs can be customized using these parameters:

Example usage:
- ``unit`` controls the smallest unit to include, ranging from ``"hour"`` to ``"nanosecond"``.
The default is ``"auto"``, which includes full precision, but without trailing zeros:

>>> d = OffsetDateTime(2023, 12, 28, 11, 30, offset=+5)
>>> d.format_iso()
'2023-12-28T11:30:00+05:00'
>>> OffsetDateTime.parse_iso('2021-07-13T09:45:00-09:00')
OffsetDateTime("2021-07-13 09:45:00-09:00")
>>> i = Instant.now()
>>> i.format_iso(unit="auto")
'2025-09-28T21:24:17.664328Z'
>>> d.format_iso(unit="minute")
'2025-09-28T21:24Z'
>>> d.format_iso(unit="nanosecond")
'2025-09-28T21:24:17.664328000Z' # fixed number of digits

.. note::
- ``basic`` controls whether to use the "basic" format (i.e. no date and time separators).
By default, the extended format is used.

>>> i.format_iso(basic=True)
'20250928T212417.664328Z'
>>> i.format_iso(basic=False)
'2025-09-28T21:24:17.664328Z'

- ``sep`` controls the separator between the date and time parts. `T` by default,
but a space (``" "``) may be used instead. Other separators may be allowed in the future.

>>> i.format_iso(sep=" ")
'2025-09-28 21:24:17.664328Z'

- ``tz`` controls whether to include the IANA timezone identifier in square brackets.
Default is ``"always"`` which will raise an error if there is no timezone identifier
(this may be the case for some system timezones). Use ``"never"`` to omit the timezone identifier,
or ``"auto"`` to include it if available.

>>> d = ZonedDateTime.now("Europe/Amsterdam")
>>> d.format_iso(tz="auto")
'2025-09-28T23:24:17.664328+02:00[Europe/Amsterdam]'
>>> d.format_iso(tz="never")
'2025-09-28T23:24:17.664328+02:00'

The ISO formats in ``whenever`` are designed so you can format and parse
them without losing information.
This makes it ideal for JSON serialization and other data interchange formats.

.. admonition:: Why not support the full ISO 8601 spec?

Expand Down
2 changes: 1 addition & 1 deletion pysrc/whenever/_pywhenever.py
Original file line number Diff line number Diff line change
Expand Up @@ -3468,7 +3468,7 @@ def format_iso(
basic: bool = False,
sep: Literal["T", " "] = "T",
) -> str:
"""Convert to the popular ISO format ``YYYY-MM-DDTHH:MM:SSZ``
"""Convert to the ISO 8601 string representation.

The inverse of the ``parse_iso()`` method.
"""
Expand Down
2 changes: 1 addition & 1 deletion src/docstrings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ pub(crate) const INSTANT_FORMAT_ISO: &CStr = c"\
format_iso($self, *, unit='auto', basic=False, sep='T')
--

Convert to the popular ISO format ``YYYY-MM-DDTHH:MM:SSZ``
Convert to the ISO 8601 string representation.

The inverse of the ``parse_iso()`` method.
";
Expand Down
Loading