(datetime — datetime).seconds
Yet another WTF in a standard library
Today I got yet another story to tell about pitfalls in the Python standard library. Most of the Python library is well-written and is obvious to use. That creates a false sense of security. Reading the docs is essential, and today I have a story of big WTF for those, who do not read them. Count me in…
datetime is a descent module for handling objects with date and time. Everything related to time is complicated, because we have days, years, hours, summer time, timezones, leap seconds, leap years, leap weeks (…nah, I’ve invented that …I hope). Anyway, datetime gives a sane interface to handle some of that complexity. One of it’s nice features is a timedelta type, which is a return type for subtraction between two datetype objects. It says how much time is between two events. It even have a human-readable form for it:
>>> event1 = datetime.datetime(2018, 12, 4, 15, 9, 58, 1)
>>> event2 = datetime.datetime(2018, 12, 19, 17, 55, 0, 2)
>>> event2 - event1
datetime.timedelta(15, 9902, 1)
>>> str(event2 - event1)
'15 days, 2:45:02.000001'
What a nice library!
I used it in my code to calculate number of seconds between to events. I made dir() on timedelta object and found this:
['__abs__',... 'days', 'max', 'microseconds', 'min', 'resolution', 'seconds'...
Good! timedelta.seconds is the answer. I had had a suspicion that it could be ‘number of seconds which is less then a minute (so, days=15, hours=2, minutes=45 seconds=2 for example from above). But it was a pretty big number, so I was wrong:
>>> (event2 - event1).seconds
Excellent, it’s a number of seconds, I thought.
… Only after few months in production and after hour of debugging of some bizarre inconsistency in my script, I found that….
(yes, READ THE DOCS!)
| Data descriptors defined here:
| Number of days.
| Number of microseconds (>= 0 and less than 1 second).
| Number of seconds (>= 0 and less than 1 day).
The variable seconds have number of seconds less than day. The proper function to call (for my purposes is timedelta.total_seconds()).
So, timedelta.seconds is timedelta.total_seconds() % 86400
Good luck with debugging this.
Was I wrong? Yes. Have I made a mistake? Yes.
Do I feel WTFed? Yes, beyond recognition.