1

I'd like to convert an epoch time stamp (eg, et =1351036517.179364) to a datetime.datetime object. So far, I've used time.ctime(et) which gives me a string of something like "Fri Oct 5 22:20:33 2012 ".

Eventually, I need the datetime object to compute time difference between two datapoints, the other datapoint is a datetime object as well.

Thanks!

msunbot
  • 1,871
  • 4
  • 16
  • 16

3 Answers3

3

It's worthwhile to remember that a timestamp has no associated timezone information. Unix time is unambiguously UTC, but it's not unusual to store local time in Unix-style timestamps. You must check against a known reference to be sure.


If your timestamps are already in UTC (Unix time), you can use Marc B's suggestion of subtract two unix timestamps directly to give you the number of seconds of difference between them. You may then want to create a timedelta, which will allow you to easily manipulate datetimes with it (you can sum or subtract a timedelta to/from a datetime, for example).

datetime.timedelta( seconds= n_seconds )


If your timestamps are in local time, don't subtract them directly, as you'll get potentially incorrect results.

Instead, you should use datetime.fromtimestamp first, attach a timezone to each datetime, and only then subtract them to get the timedelta.

To convert back from a timedelta to the number of seconds, there's timedelta.total_seconds, assuming you're using python 2.7 . If you have an earlier version, you'll have to do the calculation by hand, or pip install datetime

loopbackbee
  • 21,962
  • 10
  • 62
  • 97
  • Got it. Thanks so much @goncalopp and Marc B! – msunbot Nov 13 '12 at 21:22
  • it is wrong. There is no such thing "unix timestamp in local time". `1351036517.179364` unix timestamp corresponds to `2012-10-24T01:55:17.179363` in Paris, `2012-10-23T19:55:17.179363` in New York. You can substruct timestamps directly -- they are "seconds since Epoch" (elapsed seconds if we ignore the difference between SI seconds and mean solart (UT1) seconds. Ignoring leap seconds in other words). – jfs Dec 02 '14 at 15:06
  • @J.F.Sebastian Technically, you're right, because such is the definition of "unix time". However, it's certainly not unheard of to encode timestamps obtained from local time as if it were UTC (`time.mktime(datetime.now().timetuple())` vs `time.mktime(datetime.utcnow().timetuple())`). Of course, *strictly*, these are not **unix time** timestamps. I'll edit the answer to make this clearer – loopbackbee Dec 02 '14 at 17:30
  • don't confuse people. OP asks about "epoch timestamp" meaning POSIX timestamp (POSIX time). – jfs Dec 03 '14 at 07:37
  • 2
    @J.F.Sebastian You'll also notice that OP posted an example timestamp and seemingly automatically assumes it's Unix time. Personally, having had to parse timestamps in localtime before while assuming they were Unix time, I'd much rather that someone had pointed this out to me than insisted that everything that people informally call "Unix time" is in UTC. IMMV. – loopbackbee Dec 03 '14 at 13:15
  • @goncalopp: `mktime()` accepts local time, you should not pass it utc time. It is wrong. period. – jfs Dec 03 '14 at 14:54
1

To convert POSIX timestamp to datetime.datetime object that represents time in UTC:

from datetime import datetime, timedelta, timezone

timestamp = 1351036517.179364
utc_time = datetime(1970, 1, 1, tzinfo=timezone.utc) + timedelta(seconds=timestamp)
# -> 2012-10-23 23:55:17.179363+00:00

If your Python version does not support datetime.timezone; you could omit it, to get a naive datetime object in UTC.


I need the datetime object to compute time difference between two datapoints, the other datapoint is a datetime object as well.

If naive datetime objects represent local times that have different UTC offsets than you can't compare them directly otherwise you may get a wrong result. You either need timezone-aware datetime objects or you should convert the datetime objects to UTC first. See

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

TLDR

use fromtimestamp as referenced in the documentation

Return the local date corresponding to the POSIX timestamp [...and if tz is supplied...] the timestamp is converted to [supplied] tz’s time zone.

Given a UNIX / EPOCH Timestamp

Here is an example of such a timestamp as generated by python(updated thanks to @jfs and Paul Ganssle)

from datetime import datetime, timezone
epoch = datetime.now(tz=timezone.utc).timestamp()  # example: 1520020585.536527

NOTE: not all epoch's are alike and using time.time() will give seconds elapsed since a platform dependent epoch, where as date.timestamp() gives you what its claims is a POSIX timestamp see caveats below

NOTE: I'm specifically refraining from calling .timestamp()'s output a "POSIX compliant" timestamp. If your application depends on that level of accuracy then you may want to use something other than .timestamp()

NOTE: using datetime.now as it may give a higher precision

NOTE: we could have omitted the tz argument above, but it's good habit to use timezone aware dates, as python 3 may make incorrect assumptions when working with naive dates(or not throw errors where we think it would)

Convert Timestamp to datetime object

Given the timestamp (e.g. 1520020585.536527), we may convert it to a datetime tz aware object using fromtimestamp like so:

from datetime import datetime, timezone
ts = 1520020585.536527  # remember me? I'm the epoch example output from above
utc_date = datetime.fromtimestamp(ts, tz=timezone.utc) # outputs: datetime date object

NOTE: fromtimestamp expects a "POSIX" timestamp, the tz we supply here is only to avoid naive datetime objects. fromtimestamp will perform any needed conversion for the timezone we provide, but what timezone is better than UTC, right?

NOTE: also although the python literature explicitly references the input to fromtimestamp() as a "POSIX" timestamp it is not explicitly referring to the input as "POSIX compliant" and therefore we can only infer what a "POSIX" timestamp is from other parts of its literature, like when it refers to POSIX output as "returned by time.time()"

Marc
  • 4,820
  • 3
  • 38
  • 36
  • 1
    `DT.datetime.utcnow().timestamp()` is *wrong.* `.utcnow()` returns a naive datetime object (no tzinfo). `.timestamp()` interprets a naive datetime object as a local time (which may have a non-zero utc offset (likely)). To get the current "epoch timestamp" just call `time.time()`. If you want to make the `.timestamp()` method to work, then pass it a timezone aware datetime object e.g., `DT.datetime.now(DT.timezone.utc).timestamp()`. And in reverse, to get a timezone aware datetime object from the "epoch timestamp": `DT.datetime.fromtimestamp(ts, tz)` where `tz` is the desired timezone. – jfs Nov 15 '20 at 11:41
  • @jfs many thanks for taking the time to point out my mistake and also giving the correction! I should have read the documentation closer given I was linking to it. I have updated the solution with your help. I believe it's in good shape now :D – Marc Nov 16 '20 at 19:38
  • text is misleading: the fromtimestamp argument ("seconds since the epoch") does not depend on the local timezone where its arg was generated. `time.time()` returns the same value whatever your current local time zone is. – jfs Nov 17 '20 at 05:15
  • @jfs good catches, I've made some updates given your feedback, I appreciate it! – Marc Nov 18 '20 at 01:21
  • `time.gmtime(0)` is the same time instance on all systems (POSIX Epoch), unless you are talking about "right" timezones (probably not -- it is a different time scale) or synchronization issues (ntp and the like), stdlib would break in several places if `gmtime(0)` differs from POSIX Epoch. btw, to avoid confusion, don't call a "seconds since the epoch" value just `epoch` in this answer. – jfs Nov 18 '20 at 15:06
  • `now(utc).timestamp()` can return non-POSIX timestamp: `for tz in UTC right/UTC; do TZ=$tz python3.9 -c $'import datetime as DT, os, time; print(f"{os.environ[\'TZ\']=:9} | {time.time()=} | {DT.datetime.now(DT.timezone.utc).timestamp()=}")'; done`. There is a subtle distinction between POSIX timestamp and Unix time ("seconds since epoch"). It can be ignored in most cases where they are the same that is why my answer uses an explicit formula because I wanted to get POSIX timestamp in all cases. – jfs Nov 18 '20 at 15:07
  • @jfs the equation looks very similar to what native [python computes internally](https://docs.python.org/3.3/library/datetime.html#datetime.datetime.timestamp) when `.timstamp()` is run: `(dt - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds()` which, to me, would seem that it's doing the calculation to ensure that we're getting seconds since 1970. I'm guessing you may be referring to the leap seconds? Regardless I have learned a lot from you and I like how thorough you've been with your critique. It's beneficial to us all, given time isn't as simple as it would appear :D. Cheers! – Marc Nov 18 '20 at 15:49
  • 1
    the point is that the code in the answer produces a wrong value (not the one it suggests it should produce). Run the example that I provided above to see for yourself that `DT.datetime.now(DT.timezone.utc).timestamp()` does not return POSIX timestamp in some cases. I didn't look into it too closely but my guess is that `now(utc)` with `TZ=right/UTC` produces a different time but `.timestamp()` formula fails for "right" timezone ("right" timestamp counts leap-seconds and a different epoch is used) https://repl.it/@zed1/unix-time-in-right-timezone – jfs Nov 18 '20 at 17:19
  • @jfs this is a very good point and your repl.it illuminates something I was not aware of `UTC` vs `right/UTC`. From what I gather this is what differentiates something from being either POSIX compliant or not. I'll make some changes. Awesome use of `repl.it` to drive home a point. – Marc Nov 19 '20 at 00:14