Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add weekly timedeltas with regards to daylight saving timezones

I want to add or subtract weeks (or days or month or years) to localized datetime objects. The problem is, that the naive approach will result in 1 hour shifts due to daylight saving timezones.

2014-03-27 12:00 is right before the switch from winter to summer time. If I add a timedelta of one week to this date localized in timezone Europe/Berlin for example, the result will be 2014-04-03 13:00. I would like to have the same hour of day, 2014-04-03 12:00. I found a solution:

from datetime import datetime, timedelta
import pytz
my_tz = pytz.timezone("Europe/Berlin")

def add_relativedelta(date, delta):
    """
    Adds the given timedelta to the given date. Shifts in timezone offsets
    will be removed.
    """
    tz = date.tzinfo
    result = tz.normalize(date + delta)
    if result.utcoffset() != date.utcoffset():
        result = tz.normalize(date.utcoffset() - result.utcoffset() + result)
    return result

date = my_tz.localize(datetime(year=2014, month=3, day=27, hour=12, minute=0))
print """{} Original localized date (winter time)
{} One week later (summer time)
{} Date one week later preserving hour of day (summer time)""".format(date,
                     my_tz.normalize(date + timedelta(days=7)),
                     add_relativedelta(date, timedelta(days=7)))


2014-03-27 12:00:00+01:00 Original localized date (winter time)
2014-04-03 13:00:00+02:00 One week later (summer time)
2014-04-03 12:00:00+02:00 Date one week later preserving hour of day (summer time)

I was wondering if there is more generic/better solution. Are there any libraries that could solve this? This seems to be a pretty common issue.

like image 227
davidn Avatar asked Jul 10 '14 09:07

davidn


2 Answers

I use this simple code without the need for other libraries:

from datetime import datetime, timedelta
from pytz import timezone

tz = timezone('Europe/Berlin')
dt = tz.localize(datetime(2014, 3, 27, 12))

week_naive = datetime.combine(dt.date() + timedelta(days=7), dt.time())
week_local = dt.tzinfo.localize(week_naive)

print(dt, "Original datetime")
print(week_local, "Next week datetime")

Outputs:

2014-03-27 12:00:00+01:00 Original datetime
2014-04-03 12:00:00+02:00 Next week datetime
like image 111
petr Avatar answered Sep 28 '22 22:09

petr


timedelta(days=7) means 7 days, as in 7*24 hours - not "solar days". If you add 7 days to a timezone-aware datetime, you'll obtain a datetime that is 7 days later - independently of how that datetime is represented in the timezone.

It seems what you really want is to apply the delta to the time you specified, ignoring timezone details. Notice the difference:

In [13]: print my_tz.normalize( my_tz.localize( dt ) + delta )
2014-04-03 13:00:00+02:00

In [14]: print my_tz.normalize( my_tz.localize( dt + delta ) )
2014-04-03 12:00:00+02:00

So, if possible, apply the deltas to the datetimes before they are localized.

like image 23
loopbackbee Avatar answered Sep 28 '22 22:09

loopbackbee