Skip to main content

Representing years, quarters, months, weeks and days as integers

Project description

unit_of_time

PyPI - Python Version

unit_of_time is a small package to represents time ranges through an int, this means we can easily store these, for example in a database. It also offers functions to determine the previous and next time range.

Time units

The package by default comes with a year, quarter, month, week and day as time units. We want to be able to convert a certain week into an int, and back. For this, we have defined the following rule:

  • a time range should encode to one integer, not per se the other way around;
  • the time ranges should be orderable such that we can easily order and sort items in the database; and
  • it should be easy for humans to read the integers.

For this we have defined the following format:

YYYYMMDDK

Here YYYY-MM-DD is the start date of the time range, and K is the "kind" of time range. The longer a time unit, the lower its kind.

So a year for example has kind 1, this means that we encode the year 2025 as 202501011, the month january 2025 is encoded as 202501015.

If we thus order the time units, we first sort on the start of the time unit, and the ordering will put longer time units first, so the "year 2025 starts 'earlier' than the quarter 2025Q1 which starts earlier than 2025-01".

Utility functions

The package provides some utility functions to make working with the time units more convenient.

Moving forward and backward

For example when we construct a year with

year1958 = Year(date(1958, 3, 25))

we can go to the next and previous year with:

year1959 = year1958.next
year1957 = year1958.previous

one can also use .ancestors and .successors which are generators that will keep proposing previous and next time units respectively, so we can walk over the years since 1958 with:

for year in year1958.successors:
  print(year)

Membership checks

We can also determine if a date, or another time range is fully enclosed by another one, for example:

Month(date(1958, 3, 25)) in Year(date(1958, 3, 25))  # True
Month(date(2019, 11, 25)) in Year(date(1958, 3, 25))  # False

or check if there is overlap between two time units, especially since weeks are not always fully enclosed by the month, quarter, etc. when the week starts or ends. For example:

Week(date(1957, 12, 31)).overlaps_with(Year(date(1958, 1, 1)))  # True

since the week with 1957-12-31 starts on December 30th.

Ordering

We can also check if one time unit starts before another time unit if these are of the same kind, like:

Week(date(1957, 12, 31)) <= Week(date(1958, 3, 25))

Time units as a collection of dates

A time unit itself is iterable: it will yield all dates contained by the time unit. For example we can get all dates of 1958Q1 with:

for dt in Quarter(date(1958, 3, 25)):
  print(dt)  # 1958-01-01 to 1958-03-31

we can also convert such collection to a list.

Hash and index

A time unit is hashable, it uses the int representation as hash. It is also indexable, and uses the int representation.

We can thus make a (very) long list, and work with:

specials_unit_of_times = [False] * 202512319
specials_unit_of_times[Day(date(1958, 3, 25))] = True

we can even use this to slice, although it probably is not very useful.

Registering a new time unit

We can register a new time unit, for example a decade with:

class Decade(TimeunitKind):
    kind_int = 0
    formatter = "%Ys"

    @classmethod
    def truncate(cls, dt):
        return date(dt.year - dt.year % 10, 1, 1)

    @classmethod
    def _next(cls, dt):
        return date(dt.year + 10, 1, 1)

Subclassing TimeunitKind will automatically register it. One has to fill in the kind_int, which is an integer, preferrably between 0 and 9, although one can register outside the range. If that is the case, the "kind" will take two or more digits when converting to an int.

One can also implment a formatter. This is strictly speaking not necessary, since one can also implement a .to_str() method:

class Decade(TimeunitKind):
    kind_int = 0
    
    def to_str(cls, dt):
        return dt.strftime('%Ys')

    # ...

this might be useful if the formatting is more advanced than what Python's date formatter can handle.

Furthermore, one implements the .truncate(..) class method to convert a date to the start of the date range, and the _next(..) which returns the first date for the next decade.

With these functions, we have registered a new time unit.

Pre-installed time units

The package ships with the following time units:

  • 1: year;
  • 3: quarter;
  • 5: month;
  • 7: week; and
  • 9: day.

There is deliberately always one integer between the two time units, such that one can always put a customized one between any of the two.

195803259 – 201911259

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unit_of_time-0.1.0.0.tar.gz (12.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

unit_of_time-0.1.0.0-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file unit_of_time-0.1.0.0.tar.gz.

File metadata

  • Download URL: unit_of_time-0.1.0.0.tar.gz
  • Upload date:
  • Size: 12.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for unit_of_time-0.1.0.0.tar.gz
Algorithm Hash digest
SHA256 777bc82a20519e7cc7380cd96183867a04e3ae7b1e4c200da60e7e5af8d08d55
MD5 c83802735d6cde61a135e7b5f4241a22
BLAKE2b-256 ecdd96f60d911e502f77789b6bcac8a36ca7272c6ece22012cd3ed169df92da7

See more details on using hashes here.

File details

Details for the file unit_of_time-0.1.0.0-py3-none-any.whl.

File metadata

  • Download URL: unit_of_time-0.1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for unit_of_time-0.1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 abeab78c332dfe9ec97f1eb0238b7bc6593bf50a4cbd900fcd3204f1146da3e1
MD5 13c171ad0ab40cf430bced01f5dd1cf5
BLAKE2b-256 220ff7906d5e53f6dbf32cb8195d32a34e45621553f9b3498c9b3153b91dc57e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page