Jean-Pierre Schnyder
Jean-Pierre Schnyder

Reputation: 1934

Why is there a difference between datetime.utcnow() and datetime.now(timezone('UTC'))?

As you can see from the snippet below, there's a one hour difference between the two method. What is the reason ?

from datetime import datetime
from pytz import timezone
import time

def timestamp2date(timestamp):
    # function converts a UTC timestamp into Europe/Zurich Gregorian date
    DATE_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
    utcTimeStamp = datetime.fromtimestamp(int(timestamp)).replace(tzinfo=timezone('UTC'))
    return utcTimeStamp.astimezone(timezone('Europe/Zurich')).strftime(DATE_TIME_FORMAT)

timeStampUTC_1 = time.mktime(datetime.utcnow().timetuple())
print(timeStampUTC_1)
print(timestamp2date(timeStampUTC_1))
timeStampUTC_2 = time.mktime(datetime.now(timezone('UTC')).timetuple())
print(timeStampUTC_2)
print(timestamp2date(timeStampUTC_2))
print(timeStampUTC_2 - timeStampUTC_1)

# 1504385450.0
# 2017-09-03 00:50:50   this the right time
# 1504389050.0
# 2017-09-03 01:50:50
# 3600.0

Upvotes: 0

Views: 1017

Answers (2)

Jean-Pierre Schnyder
Jean-Pierre Schnyder

Reputation: 1934

Here's the result of my further exploration of the timezone concept associated with the daylight saving time flag. The code is pretty straightforward.

To summarize the results, there's a right way to handle date/time information:

  1. Date/time object must be localized
  2. If localized correctly, the date/time object shows right daylight saving time information
  3. This is reflected when you convert your localized date/time object to a UTC date/time object which is by definition DST agnostic
  4. All those considerations are particularly relevant when, for example, you access a stock or currency historical minute rate API

code:

from datetime import datetime
from pytz import timezone
import time

print("---- Winter time (CET=Central European Time) ----")
dateStr = "2014-02-28 22:28:15"
datetimeObjUnlocalized = datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S")
print('UNL: ' + datetimeObjUnlocalized.strftime("%Y-%m-%d %H:%M:%S %Z%z"))
print('     datetimeObjUnlocalized-->tm_isdst=' + str(datetimeObjUnlocalized.timetuple()[8]))
datetimeObjZH = timezone('Europe/Zurich').localize(datetimeObjUnlocalized)
print('ZH:  ' + datetimeObjZH.strftime("%Y-%m-%d %H:%M:%S %Z%z"))
print('     datetimeObjZH-->tm_isdst=' + str(datetimeObjZH.timetuple()[8]))
print("UTC: " + datetimeObjZH.astimezone(timezone('UTC')).strftime("%Y-%m-%d %H:%M:%S %Z%z"))

print("\n---- Summer time (CEST=Central European Summer Time) ----")
dateStr = "2014-06-28 22:28:15"
datetimeObjUnlocalized = datetime.strptime(dateStr, "%Y-%m-%d %H:%M:%S")
print('UNL: ' + datetimeObjUnlocalized.strftime("%Y-%m-%d %H:%M:%S %Z%z"))
print('     datetimeObjUnlocalized-->tm_isdst=' + str(datetimeObjUnlocalized.timetuple()[8]))
datetimeObjZH = timezone('Europe/Zurich').localize(datetimeObjUnlocalized)
print('ZH:  ' + datetimeObjZH.strftime("%Y-%m-%d %H:%M:%S %Z%z"))
print('     datetimeObjZH-->tm_isdst=' + str(datetimeObjZH.timetuple()[8]))
print("UTC: " + datetimeObjZH.astimezone(timezone('UTC')).strftime("%Y-%m-%d %H:%M:%S %Z%z"))

Console output

Upvotes: 0

AChampion
AChampion

Reputation: 30258

The reason is that datetime.timetuple() sets dst=-1 if the datetime is not offset aware and dst=0 or 1 when it is offset aware.

From the documentation:

The tm_isdst flag of the result is set according to the dst() method: tzinfo is None or dst() returns None, tm_isdst is set to -1; else if dst() returns a non-zero value, tm_isdst is set to 1; else tm_isdst is set to 0.

In []:
datetime.utcnow()
Out[]:
datetime.datetime(2017, 9, 2, 23, 9, 12, 715042)

In []:
print(datetime.utcnow().dst())
Out[]:
None

In []:
datetime.now(timezone('UTC'))
Out[]:
datetime.datetime(2017, 9, 2, 23, 9, 15, 856983, tzinfo=<UTC>)

In []:
datetime.now(timezone('UTC')).dst()
Out[]
datetime.timedelta(0)

In []:
datetime(2017, 9, 2, 23, 9, 15, 856983).timetuple()
Out[]:
time.struct_time(tm_year=2017, tm_mon=9, tm_mday=2, tm_hour=23, tm_min=9, 
                 tm_sec=15, tm_wday=5, tm_yday=245, tm_isdst=-1)
                                                             ^^
In []:
datetime(2017, 9, 2, 23, 9, 15, 856983, timezone('UTC')).timetuple()
Out[]:
time.struct_time(tm_year=2017, tm_mon=9, tm_mday=2, tm_hour=23, tm_min=9, 
                 tm_sec=15, tm_wday=5, tm_yday=245, tm_isdst=0)
                                                             ^

This changes the timestamp that time.mktime() generates because mktime treats -1 unknown and uses localtime, so may calculate dst=1 and hence they could be an 1 hour different (3600s):

In []:
time.mktime(datetime(2017, 9, 2, 23, 9, 15, 856983).timetuple())
Out[]:
1504411755.0

In []:
time.mktime(datetime(2017, 9, 2, 23, 9, 15, 856983, timezone('UTC')).timetuple())
Out[]:
1504415355.0

Upvotes: 1

Related Questions