Python - calendar.timegm() vs. time.mktime()
Asked Answered
C

3

74

I seem to have a hard time getting my head around this.

What's the difference between calendar.timegm() and time.mktime()?

Say I have a datetime.datetime with no tzinfo attached, shouldn't the two give the same output? Don't they both give the number of seconds between epoch and the date passed as a parameter? And since the date passed has no tzinfo, isn't that number of seconds the same?

>>> import calendar
>>> import time
>>> import datetime
>>> d = datetime.datetime(2010, 10, 10)
>>> calendar.timegm(d.timetuple())
1286668800
>>> time.mktime(d.timetuple())
1286640000.0
>>> 
Changeup answered 2/6, 2010 at 10:42 Comment(1)
See this question: #15448132Policewoman
O
114

time.mktime() assumes that the passed tuple is in local time, calendar.timegm() assumes it's in GMT/UTC. Depending on the interpretation the tuple represents a different time, so the functions return different values (seconds since the epoch are UTC based).

The difference between the values should be equal to the time zone offset of your local time zone.

Oyster answered 2/6, 2010 at 10:56 Comment(4)
Oh, I see, so basically timegm assumes I passed UTC and simply makes the difference between what I passed and 1970.01.01 UTC, whereas mktime first converts what I passed to UTC by adding my timezone offset and does what timegm did from there on?Changeup
But WHY if my datetime's tzinfo is None does mktime do ANY conversion? Shouldn't it leave it as it is? Why would it assume it's in local time zone?Changeup
@ibz: The timetuple parameter given to mktime() doesn't contain any time zone information (it never does, there is no time zone field in a timetuple). Therefore the function has to "guess" which time zone it might be, and mktime() just always assumes that it is local time. That's just how the function behaves.Oyster
also calendar.timegm() has epoch hardcoded to 1970-01-01UTC (posix epoch). time.mktime() might use different epoch. From the docs: For Unix, the epoch is 1970. To find out what the epoch is, look at gmtime(0). Though the rest of stdlib might assume posix epoch.Tortuous
E
11

calendar.timegm converts from UTC timestamp, time.mktime converts from local time not UTC.

8 hours difference in their results corresponds exactly to timezone of your location.

Excrescence answered 2/6, 2010 at 10:54 Comment(1)
More precisely, timegm interprets the given date as UTC, returning a timestamp, while mktime interprets the given date as local time, returning a timestamp.Whitley
D
1

Let us consider below example,

>>> import datetime
>>> import time
>>> import calendar
>>> utc_time_now = datetime.datetime.utcnow()
>>> utc_time_now
datetime.datetime(2022, 5, 21, 6, 47, 33, 929433)
>>> time.mktime(utc_time_now.timetuple())
1653095853.0
>>> calendar.timegm(utc_time_now.timetuple())
1653115653
>>> time.tzname
('IST', 'IST')
>>> time.strftime("%z", time.gmtime())
'+0530'
>>> (1653115653-1653095853)
19800

Before we begin with an explanation, please note that 'datetime.datetime.utcnow()' returns a DateTime object which is 'naive' and has no information about your local timezone.

time.mktime()
Applying the above function to a DateTime object considers the 'Timezone'.
So acc. to the example above, when you provide the time '2022-05-21 06:47:33' to time.gmtime(), it assumes that this time is not actually in UTC but in your local timezone ('IST' in my case).

In my case, the timezone is 'IST', which is +05:30 (5 hr, 30 mins) ahead of the UTC timezone. So to return the epoch in the UTC timezone, it subtracts +05:30 (19800 seconds) from the datetime object in its timestamp calculation.
Hence, it returns the timestamp (1653095853.0), which is 19800 seconds less than the original UTC epoch seconds (1653115653).

Note: If your timezone is negative (like -05:30), it will add 19800 seconds to the final epoch calculation. Hence you will see time.gmtime() timestamp is greater by 19800 seconds from the UTC epoch timestamp.

calendar.timegm()
It returns the corresponding Unix timestamp value, assuming an epoch of 1970-01-01. Hence no extra adjustment in the DateTime object is made.
Whatever DateTime object value it gets, it subtracts it from epoch time '1970-01-01' and returns the total seconds() elapsed.

BONUS
Get timestamp epoch milliseconds from your UTC DateTime object.
1st Method:

>>> utc_time_now
datetime.datetime(2022, 5, 21, 7, 16, 34, 938547)
>>> int((calendar.timegm(utc_time_now.timetuple()) + (utc_time_now.microsecond/1e6))*1000.0)
1653117394938

2nd Method:

>>> utc_time_epoch = datetime.datetime.utcfromtimestamp(0)
>>> utc_time_epoch 
datetime.datetime(1970, 1, 1, 0, 0)
>>> utc_time_now = datetime.datetime.utcnow()
>>> utc_time_now 
datetime.datetime(2022, 5, 21, 7, 16, 34, 938547)
>>> elapsed_time = utc_time_now - utc_time_epoch
>>> elapsed_time
datetime.timedelta(19133, 26194, 938547)
>>> int(elapsed_time.total_seconds() * 1000.0)
1653117394938

Have a good time :)

Disquiet answered 21/5, 2022 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.