--- myst: html_meta: description: >- Copy-pasteable recipes for common tasks: current time, time zone conversion, parsing ISO 8601, rounding, ages, countdowns, and DST-safe scheduling. --- (examples)= # Examples ```{eval-rst} .. currentmodule:: whenever ``` Short, copy-pasteable recipes for the things you'll reach for most often. For the reasoning behind them, refer to the {ref}`guide`. ## Get the current time in UTC ```python >>> from whenever import Instant >>> Instant.now() Instant("2025-04-19 19:02:56.39569Z") ``` ## Convert UTC to the system time zone ```python >>> from whenever import Instant, SYSTEM_TZ >>> i = Instant.now() >>> i.to_tz(SYSTEM_TZ) ZonedDateTime("2025-04-19 21:02:56.39569+02:00[Europe/Berlin]") ``` ## Convert from one time zone to another ```python >>> from whenever import ZonedDateTime >>> d = ZonedDateTime(2025, 4, 19, hour=15, tz="America/New_York") >>> d.to_tz("Europe/Berlin") ZonedDateTime("2025-04-19 21:00:00+02:00[Europe/Berlin]") ``` ## Convert a date to datetime ```python >>> from whenever import Date, Time >>> date = Date(2023, 10, 1) >>> date.at(Time(12, 30)) PlainDateTime("2023-10-01 12:30:00") ``` ## Calculate somebody's age ```python >>> from whenever import Date >>> birth_date = Date(2000, 11, 2) >>> today = Date(2026, 3, 10) >>> today.since(birth_date, total="years") # scalar totals may be fractional 25.350684931506848 >>> years, months, days = today.since( ... birth_date, in_units=("years", "months", "days") ... ).values() >>> years # completed calendar years 25 >>> months, days # optional remainder (4, 8) ``` ## Assign a time zone to a datetime ```python >>> from whenever import PlainDateTime >>> datetime = PlainDateTime(2023, 10, 1, 12, 30) >>> datetime.assume_tz("America/New_York") ZonedDateTime("2023-10-01 12:30:00-04:00[America/New_York]") ``` ## Integrate with the standard library ```python >>> import datetime >>> py_dt = datetime.datetime.now(datetime.UTC) >>> from whenever import Instant >>> # create an Instant from any aware datetime >>> i = Instant(py_dt) Instant("2025-04-19 19:02:56.39569Z") >>> zdt = i.to_tz("America/New_York") ZonedDateTime("2025-04-19 15:02:56.39569-04:00[America/New_York]") >>> # convert back to the standard library >>> zdt.to_stdlib() datetime.datetime(2025, 4, 19, 15, 2, 56, 395690, tzinfo=zoneinfo.ZoneInfo(key='America/New_York')) ``` ## Parse an ISO 8601 string ```python >>> from whenever import Instant >>> Instant("2025-04-19T19:02+04:00") Instant("2025-04-19 15:02:00Z") ``` Or, if you want to keep the offset value: ```python >>> from whenever import OffsetDateTime >>> OffsetDateTime("2025-04-19T19:02+04:00") OffsetDateTime("2025-04-19 19:02:00+04:00") ``` ## Determine the start of the hour ```python >>> d = ZonedDateTime.now("America/New_York") ZonedDateTime("2025-04-19 15:46:41-04:00[America/New_York]") >>> d.start_of("hour") ZonedDateTime("2025-04-19 15:00:00-04:00[America/New_York]") ``` The {meth}`~whenever.ZonedDateTime.start_of` method also works with other units like ``"day"``, ``"minute"``, and more. See its documentation for more details. ## Determine the end of the day ```python >>> d = ZonedDateTime.now("America/New_York") ZonedDateTime("2025-04-19 15:46:41-04:00[America/New_York]") >>> d.end_of("day") ZonedDateTime("2025-04-19 23:59:59.999999999-04:00[America/New_York]") ``` ## Get the current timestamp ```python >>> from whenever import Instant >>> i = Instant.now() >>> i.timestamp() 1745090505 ``` Note that this is always in whole seconds. If you need additional precision: ```python >>> i.timestamp(unit="millisecond") 1745090505629 >>> i.timestamp(unit="nanosecond") 1745090505629346833 ``` ## Get a date and time from a timestamp ```python >>> from whenever import Instant >>> Instant.from_timestamp(1745090505).to_tz("America/New_York") ZonedDateTime("2025-04-19 15:21:45-04:00[America/New_York]") ``` ## Find the duration between two datetimes ```python >>> from whenever import ZonedDateTime >>> d = ZonedDateTime(2025, 1, 3, hour=15, tz="America/New_York") >>> d2 = ZonedDateTime(2025, 1, 5, hour=8, minute=24, tz="Europe/Paris") >>> d2 - d TimeDelta("PT35h24m") ``` ## Move a date by six months ```python >>> from whenever import Date >>> date = Date(2023, 10, 31) >>> date.add(months=6) Date("2024-04-30") ``` ## Discard fractional seconds ```python >>> from whenever import Instant >>> i = Instant.now() Instant("2025-04-19 19:02:56.39569Z") >>> i.round() Instant("2025-04-19 19:02:56Z") ``` Use the arguments of {meth}`~whenever.Instant.round` to customize the rounding behavior. ## Handling ambiguous datetimes Due to daylight saving time, some date and time values don't exist, or occur twice in a given time zone. In the example below, the clock was set forward by one hour at 2:00 AM, so the time 2:30 AM doesn't exist. ```python >>> from whenever import PlainDateTime, ZonedDateTime >>> # set up the date and time for the example >>> dt = PlainDateTime(2023, 3, 26, hour=2, minute=30) ``` For a repeated or skipped local time, choose the desired policy explicitly. The `"compatible"` policy follows established libraries and RFC 5545: ```python >>> zoned = dt.assume_tz("Europe/Berlin", disambiguation="compatible") ZonedDateTime("2023-03-26 03:30:00+02:00[Europe/Berlin]") ``` You can instead choose the earlier or later interpretation: ```python >>> zoned = dt.assume_tz("Europe/Berlin", disambiguation="earlier") ZonedDateTime("2023-03-26 01:30:00+01:00[Europe/Berlin]") ``` Or, you can even reject ambiguous datetimes altogether: ```python >>> dt.assume_tz("Europe/Berlin", disambiguation="raise") Traceback (most recent call last): ... whenever.SkippedTime: 2023-03-26 02:30:00 is skipped in time zone 'Europe/Berlin' ``` ## "Same time tomorrow" across DST Adding a day keeps the local time, even when a DST transition makes the day shorter or longer than 24 hours: ```python >>> from whenever import ZonedDateTime >>> # The night before Spring Forward in Amsterdam >>> eve = ZonedDateTime(2025, 3, 30, hour=1, tz="Europe/Amsterdam") >>> eve.add(days=1) # same local time ZonedDateTime("2025-03-31 01:00:00+02:00[Europe/Amsterdam]") >>> eve.add(hours=24) # exactly 24 hours — one hour later on the clock ZonedDateTime("2025-03-31 02:00:00+02:00[Europe/Amsterdam]") ``` ## Countdown to New Year's ```python >>> from whenever import ZonedDateTime >>> now = ZonedDateTime(2025, 12, 28, hour=14, tz="America/New_York") >>> new_year = ZonedDateTime(2026, 1, 1, tz="America/New_York") >>> days, hours = new_year.since(now, in_units=("days", "hours")).values() (3, 10) ``` ## Flight itinerary across time zones ```python >>> from whenever import OffsetDateTime, hours >>> departure = OffsetDateTime(2025, 7, 1, hour=9, offset=-hours(4)) # New York >>> arrival = OffsetDateTime(2025, 7, 1, hour=22, offset=hours(2)) # Amsterdam >>> flight_time = arrival - departure >>> flight_time.total("hours") 7.0 ``` ## Recurring monthly event When a monthly recurrence lands on a day that doesn't exist in the target month, the date is clamped to the last valid day: ```python >>> from whenever import Date >>> meeting = Date(2025, 1, 31) >>> meeting.add(months=1) # February doesn't have 31 days Date("2025-02-28") >>> meeting.add(months=2) Date("2025-03-31") ``` ## Sort a list of datetimes All *exact types* can be compared and sorted amongst each other: ```python >>> from whenever import Instant, ZonedDateTime, OffsetDateTime, hours >>> times = [ ... ZonedDateTime(2025, 6, 1, hour=12, tz="Asia/Tokyo"), ... Instant.from_utc(2025, 6, 1, hour=2), ... OffsetDateTime(2025, 6, 1, hour=6, offset=hours(4)), ... ] >>> sorted(times) # all represent the same moment—sorted by the underlying instant [...] ``` "Plain" datetimes cannot be mixed with exact types. This will be flagged by type checking. ## Format with a pattern For formats beyond ISO 8601, use pattern strings: ```python >>> from whenever import Date, PlainDateTime, OffsetDateTime >>> Date.parse("15 Mar 2024", pattern="DD MMM YYYY") Date("2024-03-15") >>> PlainDateTime.parse("03/15/2024 02:30 PM", pattern="MM/DD/YYYY ii:mm aa") PlainDateTime("2024-03-15 14:30:00") >>> OffsetDateTime.parse("2024-03-15 14:30+02:00", pattern="YYYY-MM-DD HH:mmxxx") OffsetDateTime("2024-03-15 14:30:00+02:00") ``` If your input doesn't include an offset or time zone, parse with {meth}`PlainDateTime.parse` and convert: ```python >>> from whenever import PlainDateTime >>> pdt = PlainDateTime.parse("2024-03-15 14:30", pattern="YYYY-MM-DD HH:mm") >>> pdt.assume_utc() Instant("2024-03-15 14:30:00Z") ``` It also integrates nicely with the standard library's formatting protocol (`__format__`), so you can use pattern strings in f-strings: ```python >>> from whenever import Date >>> d = Date(2024, 3, 15) >>> f"{d:DD/MM/YYYY}" '15/03/2024' >>> f"{d}" # empty spec falls back to str() '2024-03-15' ``` ## Roundtrip: datetime → string → datetime Every `whenever` type has an ISO-compatible reversible string representation: ```python >>> from whenever import ZonedDateTime >>> d = ZonedDateTime(2025, 6, 15, hour=14, minute=30, tz="Europe/Amsterdam") >>> s = str(d) >>> s '2025-06-15T14:30:00+02:00[Europe/Amsterdam]' >>> ZonedDateTime(s) == d True ```