Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

datetime - Python: Convert UTC time-tuple to UTC timestamp

My problem: I need to convert a UTC time-tuple into a UTC timestamp. But I have some confusions.

First a bit info:

  • time.mktime(tuple): this function always returns the timestamp in local time.

    This is the inverse function of localtime(). Its argument is the struct_time or full 9-tuple which expresses the time in local time, not UTC.

  • calendar.timegm(tuple): this returns the UTC timestamp from the supplied time tuple

    takes a time tuple such as returned by the gmtime() function in the time module, and returns the corresponding Unix timestamp value. In fact, time.gmtime() and timegm() are each others’ inverse

Now let's do a test:

>>> from datetime import datetime
>>> import time
>>> import calendar as cal

>>> utc_now = datetime.utcnow()
>>> now = datetime.now()
>>> utc_now
datetime.datetime(2013, 3, 16, 9, 17, 22, 489225)
>>> now
datetime.datetime(2013, 3, 16, 5, 17, 29, 736903)

>>> time.mktime(datetime.timetuple(utc_now)), time.mktime(datetime.timetuple(now))
(1363439842.0, 1363425449.0)
>>> cal.timegm(datetime.timetuple(utc_now)), cal.timegm(datetime.timetuple(now))
(1363425442, 1363411049)

Why are there four different values? And which one is right when I want to convert a UTC time-tuple into a UTC timestamp?

UPDATTE


I think I have found answers to my confusions, so let me explain.

First, we need to know something important:

There are two kinds of date and time objects: “naive” and “aware”.

An aware object has sufficient knowledge of applicable algorithmic and political time adjustments, such as time zone and daylight saving time information, to locate itself relative to other aware objects. An aware object is used to represent a specific moment in time that is not open to interpretation [1].

A naive object does not contain enough information to unambiguously locate itself relative to other date/time objects. Whether a naive object represents Coordinated Universal Time (UTC), local time, or time in some other timezone is purely up to the program, just like it is up to the program whether a particular number represents metres, miles, or mass. Naive objects are easy to understand and to work with, at the cost of ignoring some aspects of reality.

What we get from datetime.utcnow() or datetime.now() are "naive" objects. This means that the datetime object that is returned does not, in anyway, say anything about your local time or UTC time -- it just represents "some time". It just encapsulates date & time information (year, month, day, hour,minutes, seconds, etc.). It is YOUR responsibility to associate it with the notion of local or UTC.

So, remember that a naive datetime object just represents "some time". The datetime.now() function returns a "some time" that is equal to your current time, and the datetime.utcnow() function returns "some time" that is the current time in Greenwich England (which is what UTC is).

The "some time" is just a value for date & time. And note that on different locations on earth, the "some time" occurs on different times. For example, if a "some time" value is January 1, 10:30, than it will be the current time in Greenwich England some 5 hours BEFORE it becomes the current time in New York.

Hence we can see that there is two things: a generic "some time" value, and the notion that that "some time" becomes current time at different locations at different "times". (no pun here, read on)

Now, let's first define what is "epoch". We know that "some time" is just a generic value of time. Then, the epoch is a "some time" that occurred in Greenwich England where the values of the parameters are: January 1 1970, 00:00:00.

A "timestamp" is no. of seconds that have elapsed since the epoch. This means that the timestamp was 0 when the time was Jan 1, 1970, 00:00:00 in Greenwich England. But the timestamp was approx. (5 * 60 * 60) when time was Jan 1, 1970, 00:00:00 in New York.

>>> tt = datetime.timetuple(datetime(1970, 1, 1, 0, 0, 0))
>>> cal.timegm(tt)
0

Thus, we can see that the same "some time" value of Jan 1, 1970, 00:00:00 has different timestamps when we change locations. Hence when you talk about timestamp, you need to also say "what location" is the timestamp related to, and how much eastward or westward that location is related to Greenwich England. That location is expressed as a "timezone".

Now, every system (computer) has a timezone configured, and all timestamps related to that timezone effectively become the "local". The UTC is the global reference.

So, let's say you have an X value for "some time" which converts to:

  • Y timestamp in your local time
  • Z timestamp in UTC

then this means that Y no. of seconds will have to elapse for "some time" to become the current time in your location and Z no of seconds will have to pass in order for current time in Greenwich England to become "some time".

Now, finally, let's come back to our functions mktime and timegm. These take a time-tuple, which is just another representation for "some time". Remember that we're passing them a naive time which does not have any notion of local or UTC.

Let's say X is a time-tuple representing a naive "some time". Then

  • mktime(X) will return the no. of seconds that will have to elapse in order for your local current time to become that "some time", and
  • timegm(X) will return the no of seconds that will have to be spent to make the current time of Greenwich England equal to that "some time".

In the example above, now and utc_now represent naive "some time", and when we feed these "some time" values into mktime and timegm, they simply return the no. of seconds that have to pass for the corresponding locations (your location and Greenwich England) to have their current time be that "some time".


Finally, back to my problem: I need to convert a UTC time-tuple into a UTC timestamp.

Firstly, there is no concept of "UTC time-tuple" -- it's just "some time". If I need to convert it to UTC, I simply use timegm:

cal.timegm(datetime.timetuple(utc_now))

which will give me the timestamp for the current UTC time (i.e., the current "some time" in Greenwich England).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are effectively only three different values. These two values:

1363425449.0 (time.mktime(datetime.timetuple(now))
1363425442   (cal.timegm(datetime.timetuple(utc_now)))

only differ by 7 seconds, which is what you originally saw when you dumped the variables:

>>> utc_now
datetime.datetime(2013, 3, 16, 9, 17, 22, 489225)
>>> now
datetime.datetime(2013, 3, 16, 5, 17, 29, 736903)

(Note the 22 vs 29 in the seconds part of the output.)

The other two values are simply erroneous, because you're applying the wrong kind of arguments - you're calling time.mktime with UTC values instead of local values, and you're calling cal.timegm with local values instead of UTC values. The documentation clearly says what's expected - so make sure you only use the appropriate values. You're basically seeing your local time offset (4 hours, by the looks of it) being applied when it shouldn't be (in different directions depending on where the error is).

When you're diagnosing things like this, it's helpful to use epochconverter.com which will give you the current Unix timestamp, so you can compare it with your output.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...