How to find the Unix timestamp for the same hour, including DST, in Python?

In Python, I can find the Unix timestamp of local time knowing the timezone like this (using pytz):

>>> import datetime as DT >>> import pytz >>> mtl = pytz.timezone('America/Montreal') >>> naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d') >>> naive_time3 datetime.datetime(2013, 11, 3, 0, 0) >>> localized_time3 = mtl.localize(naive_time3) >>> localized_time3 datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>) >>> localized_time3.timestamp() 1383451200.0 

So far so good. naive_time does not know about the time zone, while localized_time knows its midnight on 2013/11/03 in Montreal , so the UTC Unix timestamp is good. This timezone is also my local timezone, and this timestamp seems to be correct:

 $ date -d @1383451200 Sun Nov 3 00:00:00 EDT 2013 

Now the clock was adjusted one hour ago on November 3 at 2 a.m. here in Montreal, so we got an extra hour that day. This means that there was 25 hours between 2013/11/03 and 2013/11/04. It shows:

 >>> naive_time4 = DT.datetime.strptime('2013/11/04', '%Y/%m/%d') >>> localized_time4 = mtl.localize(naive_time4) >>> localized_time4 datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>) >>> (localized_time4.timestamp() - localized_time3.timestamp()) / 3600 25.0 

Now I am looking for an easy way to get the localized_time4 object from localized_time3 , knowing that I want to get the next localized day at that hour (here, midnight), I tried timedelta , but I believe that it does not know about time zones or DST:

 >>> localized_time4td = localized_time3 + DT.timedelta(1) >>> localized_time4td datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>) >>> (localized_time4td.timestamp() - localized_time3.timestamp()) / 3600 24.0 

My goal is to get information about the log entries that are stored in their Unix timestamp for each local day. Of course, if I use localized_time3.timestamp() and add 24 * 3600 here (which will be the same as localized_time4td.timestamp() ), I will skip all the log entries that occurred between localized_time4td.timestamp() and localized_time4td.timestamp() + 3600 .

In other words, the function or method I'm looking for should know when to add 25 hours, 24 hours, or 23 hours, sometimes to the Unix timestamp, depending on when the DST shifts occur.

+6
source share
4 answers

Without using a new package:

 def add_day(x): d = x.date()+DT.timedelta(1) return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None)) 

Full script:

 import datetime as DT import pytz import calendar mtl = pytz.timezone('America/Montreal') naive_time3 = DT.datetime.strptime('2013/11/03', '%Y/%m/%d') print repr(naive_time3) #datetime.datetime(2013, 11, 3, 0, 0) localized_time3 = mtl.localize(naive_time3) print repr(localized_time3) #datetime.datetime(2013, 11, 3, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EDT-1 day, 20:00:00 DST>) print calendar.timegm(localized_time3.utctimetuple()) #1383451200.0 def add_day(x): d = x.date()+DT.timedelta(1) return mtl.localize(x.replace(year=d.year, month=d.month, day=d.day, tzinfo=None)) print repr(add_day(localized_time3)) #datetime.datetime(2013, 11, 4, 0, 0, tzinfo=<DstTzInfo 'America/Montreal' EST-1 day, 19:00:00 STD>) 

( calendar for Python2.)

+3
source

I gradually propose several solutions with the most reliable solution at the very end of this answer, which is trying to solve the following problems:

  • utc bias due to DST
  • past dates when the local time zone could have a different utc offset due to a non-DST reason. dateutil and dateutil solutions do not work on some systems, especially Windows
  • ambiguous time during DST (I don't know if Arrow provides an interface for handling it)
  • non-existent times during DST (same)

To find the POSIX timestamp for tomorrowโ€™s midnight (or other fixed hour) at a given time zone, you can use the code from How to get the UTC โ€œmidnightโ€ time for a given time zone :

 from datetime import datetime, time, timedelta import pytz DAY = timedelta(1) tz = pytz.timezone('America/Montreal') tomorrow = datetime(2013, 11, 3).date() + DAY midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None) timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds() 
Method

dt.date() returns the same naive date for both naive and dt timezone oriented objects.

The explicit formula for timestamp is used to support Python versions prior to Python 3.3. Otherwise, the .timestamp() method can be used in Python .timestamp() .

To avoid ambiguity when analyzing input dates during DST transitions, which are unavoidable for the .localize() method, if you do not know the is_dst parameter, you can use Unix timestamps stored with dates:

 from datetime import datetime, time, timedelta import pytz DAY = timedelta(1) tz = pytz.timezone('America/Montreal') local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz) tomorrow = local_dt.date() + DAY midnight = tz.localize(datetime.combine(tomorrow, time(0, 0)), is_dst=None) timestamp = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds() 

Support other fixed clocks (not only midnight):

 tomorrow = local_dt.replace(tzinfo=None) + DAY # tomorrow, same time dt_plus_day = tz.localize(tomorrow, is_dst=None) timestamp = dt_plus_day.timestamp() # use the explicit formula before Python 3.3 

is_dst=None throws an exception if the date of the result is ambiguous or does not exist. To exclude an exception, you can choose the time that is closest to the previous date from yesterday (the same DST state, i.e. is_dst=local_dt.dst() ):

 from datetime import datetime, time, timedelta import pytz DAY = timedelta(1) tz = pytz.timezone('America/Montreal') local_dt = datetime.fromtimestamp(timestamp_from_the_log, tz) tomorrow = local_dt.replace(tzinfo=None) + DAY dt_plus_day = tz.localize(tomorrow, is_dst=local_dt.dst()) dt_plus_day = tz.normalize(dt_plus_day) # to detect non-existent times timestamp = (dt_plus_day - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds() 

.localize() respects the given time, even if it does not exist, therefore .normalize() is required to fix the time. You can throw an exception here if the normalize() method changes its input (in this case no time is detected) to be consistent with other code examples.

+2
source

(Thanks to @rdodev for pointing to Arrow ).

Using the arrow, this operation becomes easy:

 >>> import arrow >>> import datetime as DT >>> lt3 = arrow.get(DT.datetime(2013, 11, 3), 'America/Montreal') >>> lt3 <Arrow [2013-11-03T00:00:00-04:00]> >>> lt4 = arrow.get(DT.datetime(2013, 11, 4), 'America/Montreal') >>> lt4 <Arrow [2013-11-04T00:00:00-05:00]> >>> lt4.timestamp - (lt3.replace(days=1).timestamp) 0 >>> (lt3.replace(days=1).timestamp - lt3.timestamp) / 3600 25.0 

Using the Arrow replace method, unit names replace this property, while the plural adds to it. So, lt3.replace(days=1) - November 4, 2013, and lt3.replace(day=1) - November 1, 2013.

+1
source

Here's an alternative based on dateutil :

 >>> # In Spain we changed DST 10/26/2013 >>> import datetime >>> import dateutil.tz >>> # tzlocal gets the timezone of the computer >>> dt1 = datetime.datetime(2013, 10, 26, 14, 00).replace(tzinfo=dateutil.tz.tzlocal()) >>> print dt1 2013-10-26 14:00:00+02:00 >>> dt2 = dt1 + datetime.timedelta(1) >>> print dt2 2013-10-27 14:00:00+01:00 # see if we hace 25 hours of difference >>> import time >>> (time.mktime(dt2.timetuple()) - time.mktime(dt1.timetuple())) / 3600.0 25.0 >>> (float(dt2.strftime('%s')) - float(dt1.strftime('%s'))) / 3600 # the same 25.0 
+1
source

Source: https://habr.com/ru/post/959015/


All Articles