Working with date-time formats can be pretty upsetting because of the variate of different formats people can come up with. date-times are used everywhere not just only logging or meta data in database entries and are pretty important. That’s why I encourage developers in using the ISO 8601 derived RFC3339 standard for their projects.

RFC3339 date-time: 2016-07-18T12:58:26.485897 +02:00

The RFC3339 specification offers the following advantages:

  • Defined date, time, timezone, date-time format
  • 4 digit year
  • Fractional seconds
  • Human readable
  • No redundant information like weekday name
  • Simple specification
  • Machine readable

Having a date-time standard is nice, but using Python’s datetime library to parse/format a RFC3339 date-time string or even create a datetime object in UTC or local timezone can be painful and slowwwww. That’s why I decided to implement a Python 2 library to deal with such tasks. The library is called udatetime and available on github or PyPI.

$ pip install udatetime

The goal of the library is to be fast and handy with RFC3339 date-time formatted strings. The average performance increase of udatetime compared to the equivalent datetime code is 76%. Due to the usage of Python2 CPython API and POSIX features the library is currently only supported on POSIX systems and not Python3 or Pypy compatible. I’m working on cross-platform and Pypy support. Support in working on the library is greatly appreciated.

Benchmark

The benchmark setup is the following.

from datetime import datetime
import udatetime

RFC3339_DATE = '2016-07-18'
RFC3339_TIME = '12:58:26.485897+02:00'
RFC3339_DATE_TIME = RFC3339_DATE + 'T' + RFC3339_TIME
RFC3339_DATE_TIME_DTLIB = RFC3339_DATE_TIME[:-6]  # datetime can't parse timezones through strptime
DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%f'
DATETIME_OBJ = datetime.strptime(RFC3339_DATE_TIME_DTLIB, DATE_TIME_FORMAT)


def benchmark_parse():
    def datetime_strptime():
        datetime.strptime(RFC3339_DATE_TIME_DTLIB, DATE_TIME_FORMAT)

    def udatetime_parse():
        udatetime.from_string(RFC3339_DATE_TIME)

    return (datetime_strptime, udatetime_parse)


def benchmark_format():
    def datetime_strftime():
        DATETIME_OBJ.strftime(DATE_TIME_FORMAT)

    def udatetime_format():
        udatetime.to_string(DATETIME_OBJ)

    return (datetime_strftime, udatetime_format)


def benchmark_utcnow():
    def datetime_utcnow():
        datetime.utcnow()

    def udatetime_utcnow():
        udatetime.utcnow()

    return (datetime_utcnow, udatetime_utcnow)


def benchmark_now():
    def datetime_now():
        datetime.now()

    def udatetime_now():
        udatetime.now()

    return (datetime_now, udatetime_now)


def benchmark_utcnow_to_string():
    def datetime_utcnow_to_string():
        datetime.utcnow().strftime(DATE_TIME_FORMAT)

    def udatetime_utcnow_to_string():
        udatetime.utcnow_to_string()

    return (datetime_utcnow_to_string, udatetime_utcnow_to_string)


def benchmark_now_to_string():
    def datetime_now_to_string():
        datetime.now().strftime(DATE_TIME_FORMAT)

    def udatetime_now_to_string():
        udatetime.now_to_string()

    return (datetime_now_to_string, udatetime_now_to_string)

If you like you can run the benchmark yourself by running the bench.py script from the repository.

The results of 1 million executions and 3 repeats look like this.

benchmark_parse

datetime.strptime(RFC3339_DATE_TIME_DTLIB, DATE_TIME_FORMAT)
vs
udatetime.from_string(RFC3339_DATE_TIME)

Benchmark results benchmark_parse

benchmark_format

DATETIME_OBJ.strftime(DATE_TIME_FORMAT)
vs
udatetime.to_string(DATETIME_OBJ)

Benchmark results benchmark_format

benchmark_now

datetime.now()
vs
udatetime.now()

Benchmark results benchmark_now

benchmark_utcnow

datetime.utcnow()
vs
udatetime.utcnow()

Benchmark results benchmark_utcnow

benchmark_now_to_string

datetime.now().strftime(DATE_TIME_FORMAT)
vs
udatetime.now_to_string()

Benchmark results benchmark_now_to_string

benchmark_utcnow_to_string

datetime.utcnow().strftime(DATE_TIME_FORMAT)
vs
udatetime.utcnow_to_string()

Benchmark results benchmark_utcnow_to_string