2

I have two operations I want to perform, one the inverse of the other.

  1. I have a UNIX timestamp at UTC, say for instance, 1425508527. From this I want to get the year, month, day etc. given a UTC offset. EG. what is the year/month/day/time in (UTC -6 hours)? The answer is March 4, 2015 at 16:35:27. Without providing an offset (or offset zero) the answer should be March 4, 2015 at 22:35:27.

  2. Now I have the date at some location, along with the UTC offset. For instance March 4, 2015 at 16:35:27 and the offset (UTC -6 hours). The UNIX UTC timestamp I should get should be 1425508527.

I am able to almost do 2. (using python datetime library) like this:

import datetime.datetime as datetime
import time
import dateutil.tz as tz

utc_offset = 6
time.mktime(datetime(2015,3,4,16,35,27,
                     tzinfo=tz.tzoffset(None, utc_offset*60*60)).utctimetuple())
# => 1425486927

The problem with the above is that utc_offset has to be given the wrong sign. According to this map, utc_offset should be set to -6. Number 1. I've had no luck with. I don't need/want to deal with timezone information like daylight savings time. How do I implement this in Python?

jfs
  • 399,953
  • 195
  • 994
  • 1,670
bill_e
  • 930
  • 2
  • 12
  • 24

1 Answers1

4

If your system uses Unix time, which does not count leap seconds, then the conversion can be done as follows:

Part 1: timestamp and offset to local date

import datetime as DT
import calendar

timestamp = 1425508527
offset = -6

date = DT.datetime(1970,1,1) + DT.timedelta(seconds=timestamp)
print(date)
# 2015-03-04 22:35:27

localdate = date + DT.timedelta(hours=offset)
print(localdate)
# 2015-03-04 16:35:27

Part 2: local date and offset to timestamp

utcdate = localdate - DT.timedelta(hours=offset)
assert date == utcdate

timetuple = utcdate.utctimetuple()
timestamp2 = calendar.timegm(timetuple)
print(timestamp2)
# 1425508527
assert timestamp == timestamp2
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 1
    pico-nitpick: if the system uses "right" timezone then `utcfromtimestamp()` interprets the input as a value returned by `time.time()` that may count leap seconds in this case while `calendar.timegm()` returns POSIX timestamp that doesn't count leaps seconds (it forgets them). To make the code self-consistent, you could use `date = datetime(1970,1,1) + timedelta(seconds=timestamp)` instead of `utcfromtimestamp(timestamp)` here. To convert UTC time to "right" timestamp, you have to know about leap seconds (`pytz`) or use `mktime()` if the local utc offset is known. – jfs Mar 09 '15 at 20:15
  • @J.F. Sebastian: Thank you for the comment. I would like to understand this better. I tried experimenting with `leapsecond_timestamp = 1341100800`. But both `utcfromtimestamp(leapsecond_timestamp)` and `datetime(1970,1,1) + timedelta(seconds=leapsecond_timestamp)` return the same *wrong* datetime, `datetime(2012, 7, 1, 0, 0)`. It should be `2012-6-30 23:59:60`, a date which is inexpressible with Python's datetime object. (cont'd)... – unutbu Mar 09 '15 at 21:22
  • So my questions are: (1) why is `datetime(1970,1,1) + timedelta(seconds=leapsecond_timestamp)` preferred if both it and `utcfromtimestamp` return the same wrong datetime? (2) how does one use `pytz` or `mktime` to convert UTC datetime to the right timestamp if `2012-6-30 23:59:60` is not expressible as a datetime to begin with? – unutbu Mar 09 '15 at 21:23
  • 1
    It is not about the moment *during* a positive leap second; `datetime` module doesn't support it at all. It is about the counting of *past* leap seconds. If I run `TZ=right/UTC python` on my system then `utcfromtimestamp()` returns `2015-03-04 22:35:02` (25 seconds off) while `epoch + timestamp()` returns `2015-03-04 22:35:27`. – jfs Mar 09 '15 at 21:35
  • Note: On 2015-07-01UTC the difference will be 26 seconds ([one more intercalary leap second](ftp://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat)) – jfs Mar 09 '15 at 21:48
  • 1
    forgot to mention: with `TZ=right/UTC`: `time.mktime(datetime(2015, 3, 4, 22, 35, 27).timetuple())` -> `1425508552.0`!!! (NON-POSIX -- it counts leap seconds -- different time scale and epoch: `01-Jan-1970 00:00:10 TAI`) and `datetime.utcfromtimestamp(1425508552)` -> `datetime(2015, 3, 4, 22, 35, 27)`. [`pytz` stores leap seconds and you can get their count](http://stackoverflow.com/q/19332902/4279) – jfs Mar 09 '15 at 22:01