12

I am parsing some data that has the leapsecond timestampe datetime 2012-06-30T23:59:60.209215. I used following code to parse that string and convert to a datetime object:

    nofrag, frag = t.split('.')
    nofrag_dt = datetime.datetime.strptime(nofrag, "%Y-%m-%dT%H:%M:%S")
    dt = nofrag_dt.replace(microsecond=int(frag))

Python documentation claims that this shouldn't be an issue as %S accepts [0, 61]. But, I get this error with the above timestamp

nofrag_dt = datetime.datetime.strptime(nofrag, "%Y-%m-%dT%H:%M:%S")
ValueError: second must be in 0..59

Thanks

jfs
  • 399,953
  • 195
  • 994
  • 1,670
madtowneast
  • 2,350
  • 3
  • 22
  • 31
  • 3
    Where does the documentation claim that? http://docs.python.org/2/library/datetime.html says "Unlike the time module, the datetime module does not support leap seconds." – Wooble Jan 09 '14 at 17:59
  • http://docs.python.org/2/library/datetime.html#datetime.datetime.strptime points to http://docs.python.org/2/library/time.html#time.strptime, where http://docs.python.org/2/library/time.html#time.strftime claims to support leap second – madtowneast Jan 09 '14 at 18:02
  • Ah. The problem isn't the parsing; that is done by `time.strptime` and works fine; the problem is that if the seconds are > 59, it can't be converted back to a `datetime.datetime`. – Wooble Jan 09 '14 at 18:16
  • unlrelated: `int(frag)` may return a wrong number of microseconds e.g., `.123` should correspond to `123000` microseconds, not `123`. – jfs Mar 11 '15 at 11:13

2 Answers2

8

The documentation for %S says:

Unlike the time module, the datetime module does not support leap seconds.

The time string "2012-06-30T23:59:60.209215" implies that the time is in UTC (it is the last leap second at the moment):

import time
from calendar import timegm
from datetime import datetime, timedelta

time_string = '2012-06-30T23:59:60.209215'
time_string, dot, us = time_string.partition('.')
utc_time_tuple = time.strptime(time_string, "%Y-%m-%dT%H:%M:%S")
dt = datetime(1970, 1, 1) + timedelta(seconds=timegm(utc_time_tuple))
if dot:
    dt = dt.replace(microsecond=datetime.strptime(us, '%f').microsecond)
print(dt)
# -> 2012-07-01 00:00:00.209215
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

Do this:

import time
import datetime 
t = '2012-06-30T23:59:60.209215'
nofrag, frag = t.split('.')
nofrag_dt = time.strptime(nofrag, "%Y-%m-%dT%H:%M:%S")
ts = datetime.datetime.fromtimestamp(time.mktime(nofrag_dt))
dt = ts.replace(microsecond=int(frag))
print(dt)

Output is:

2012-07-01 00:00:00.209215
Velimir Mlaker
  • 10,664
  • 4
  • 46
  • 58
  • note: the round trip through the local time may fail (e.g., during DST transitions) and it is *unnecessary* as [my answer demonstrates](http://stackoverflow.com/a/21029510/4279). – jfs Mar 10 '15 at 02:27