Skip to content

Fast RFC3339 compliant Python date-time library

License

Notifications You must be signed in to change notification settings

SkyTruth/udatetime

 
 

Repository files navigation

udatetime: Fast RFC3339 compliant date-time library

Handling date-times is a painful act because of the sheer endless amount of formats used by people. Luckily there are a couple of specified standards out there like ISO 8601. But even ISO 8601 leaves to many options on how to define date and time. That's why I encourage using the RFC3339 specified date-time format.

udatetime offers on average 76% faster datetime object instantiation, serialization and deserialization of RFC3339 date-time strings. udatetime is using Python's datetime class under the hood and code already using datetime should be able to easily switch to udatetime. All datetime objects created by udatetime are timezone aware.

Support Performance optimized Implementation
Python 2 ✔️ ✔️ C
Python 3 ✔️ ✔️ C
PyPy ✔️ ✔️ Pure Python
>>> udatetime.from_string("2016-07-15T12:33:20.123000+02:00")
datetime.datetime(2016, 7, 15, 12, 33, 20, 123000, tzinfo=+02:00)

>>> dt = udatetime.from_string("2016-07-15T12:33:20.123000+02:00")
>>> udatetime.to_string(dt)
'2016-07-15T12:33:20.123000+02:00'

>>> udatetime.now()
datetime.datetime(2016, 7, 29, 10, 15, 24, 472467, tzinfo=+02:00)

>>> udatetime.utcnow()
datetime.datetime(2016, 7, 29, 8, 15, 36, 184762, tzinfo=+00:00)

>>> udatetime.now_to_string()
'2016-07-29T10:15:46.641233+02:00'

>>> udatetime.utcnow_to_string()
'2016-07-29T08:15:56.129798+00:00'

>>> udatetime.to_string(udatetime.utcnow() - timedelta(hours=6))
'2016-07-29T02:16:05.770358+00:00'

>>> udatetime.fromtimestamp(time.time())
datetime.datetime(2016, 7, 30, 17, 45, 1, 536586, tzinfo=+02:00)

>>> udatetime.utcfromtimestamp(time.time())
datetime.datetime(2016, 8, 1, 10, 14, 53, tzinfo=+00:00)

Installation

Currently only POSIX compliant systems are supported. Working on cross-platform support.

$ pip install udatetime

You might need to install the header files of your Python installation and some essential tools to execute the build like a C compiler.

Python 2

$ sudo apt-get install python-dev build-essential

or

$ sudo yum install python-devel gcc

Python 3

$ sudo apt-get install python3-dev build-essential

or

$ sudo yum install python3-devel gcc

Benchmark

The benchmarks compare the performance of equivalent code of datetime and udatetime. The benchmark measures the time needed for 1 million executions and takes the min of 3 repeats. You can run the benchmark yourself and see the results on your machine by executing the bench_udatetime.py script.

Benchmark interpreter summary

Python 2.7

$ python scripts/bench_udatetime.py
Executing benchmarks ...

============ benchmark_parse
datetime_strptime 10.6306970119
udatetime_parse 1.109801054
udatetime is 9.6 times faster

============ benchmark_format
datetime_strftime 2.08363199234
udatetime_format 0.654432058334
udatetime is 3.2 times faster

============ benchmark_utcnow
datetime_utcnow 0.485884904861
udatetime_utcnow 0.237742185593
udatetime is 2.0 times faster

============ benchmark_now
datetime_now 1.37059998512
udatetime_now 0.235424041748
udatetime is 5.8 times faster

============ benchmark_utcnow_to_string
datetime_utcnow_to_string 2.56599593163
udatetime_utcnow_to_string 0.685483932495
udatetime is 3.7 times faster

============ benchmark_now_to_string
datetime_now_to_string 3.68989396095
udatetime_now_to_string 0.687911987305
udatetime is 5.4 times faster

============ benchmark_fromtimestamp
datetime_fromtimestamp 1.38640713692
udatetime_fromtimestamp 0.287910938263
udatetime is 4.8 times faster

============ benchmark_utcfromtimestamp
datetime_utcfromtimestamp 0.533131837845
udatetime_utcfromtimestamp 0.279694080353
udatetime is 1.9 times faster

Python 3.5

$ python scripts/bench_udatetime.py
Executing benchmarks ...

============ benchmark_parse
datetime_strptime 9.90670353400003
udatetime_parse 1.1668808249999074
udatetime is 8.5 times faster

============ benchmark_format
datetime_strftime 3.0286041580000074
udatetime_format 0.7153575119999687
udatetime is 4.2 times faster

============ benchmark_utcnow
datetime_utcnow 0.5638177430000724
udatetime_utcnow 0.2548112540000602
udatetime is 2.2 times faster

============ benchmark_now
datetime_now 1.457822759999999
udatetime_now 0.26195338699994863
udatetime is 5.6 times faster

============ benchmark_utcnow_to_string
datetime_utcnow_to_string 3.5347913849999486
udatetime_utcnow_to_string 0.750341161999927
udatetime is 4.7 times faster

============ benchmark_now_to_string
datetime_now_to_string 4.854975383999999
udatetime_now_to_string 0.7411948169999505
udatetime is 6.6 times faster

============ benchmark_fromtimestamp
datetime_fromtimestamp 1.4233373309999706
udatetime_fromtimestamp 0.31758270299997093
udatetime is 4.5 times faster

============ benchmark_utcfromtimestamp
datetime_utcfromtimestamp 0.5633522409999614
udatetime_utcfromtimestamp 0.305099536000057
udatetime is 1.8 times faster

PyPy 5.3.1

$ python scripts/bench_udatetime.py
Executing benchmarks ...

============ benchmark_parse
datetime_strptime 2.31050491333
udatetime_parse 0.838973045349
udatetime is 2.8 times faster

============ benchmark_format
datetime_strftime 0.957178115845
udatetime_format 0.162060976028
udatetime is 5.9 times faster

============ benchmark_utcnow
datetime_utcnow 0.149839878082
udatetime_utcnow 0.149217844009
udatetime is as fast as datetime

============ benchmark_now
datetime_now 0.967023134232
udatetime_now 0.15003991127
udatetime is 6.4 times faster

============ benchmark_utcnow_to_string
datetime_utcnow_to_string 1.24988698959
udatetime_utcnow_to_string 0.632546901703
udatetime is 2.0 times faster

============ benchmark_now_to_string
datetime_now_to_string 2.13041496277
udatetime_now_to_string 0.607964038849
udatetime is 3.5 times faster

============ benchmark_fromtimestamp
datetime_fromtimestamp 0.903736114502
udatetime_fromtimestamp 0.0907990932465
udatetime is 10.0 times faster

============ benchmark_utcfromtimestamp
datetime_utcfromtimestamp 0.0890419483185
udatetime_utcfromtimestamp 0.0907027721405
udatetime is as fast as datetime

Why RFC3339

The RFC3339 specification has 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, easily machine readable

RFC3339 format specification

date-fullyear   = 4DIGIT
date-month      = 2DIGIT  ; 01-12
date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on
                          ; month/year
time-hour       = 2DIGIT  ; 00-23
time-minute     = 2DIGIT  ; 00-59
time-second     = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second
                          ; rules
time-secfrac    = "." 1*DIGIT
time-numoffset  = ("+" / "-") time-hour ":" time-minute
time-offset     = "Z" / time-numoffset

partial-time    = time-hour ":" time-minute ":" time-second [time-secfrac]

full-date       = date-fullyear "-" date-month "-" date-mday
full-time       = partial-time time-offset

date-time       = full-date "T" full-time

udatetime specific format addons:

  • time-secfrac can be either 3DIGIT for milliseconds or 6DIGIT for microseconds
  • time-secfrac will be normalized to microseconds
  • time-offset is optional. Missing time-offset will be treated as UTC.
  • spaces will be eliminated

Why in C?

The Python datetime library is flexible but painfully slow, when it comes to parsing and formating. High performance applications like web services or logging benefit from fast underlying libraries. Restricting the input format to one standard allows for optimization and results in speed improvements.

About

Fast RFC3339 compliant Python date-time library

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 99.1%
  • Makefile 0.9%