1

I'm trying to shift a tz-aware timestamp for one month. Since datetime.timedelta does not have a month option, package relativedelta should be used.

The problem arises when the time point is after the summer DST and the result after the shift will be before summer DST.

For example shift 2022-04-26 07:00:00+02:00 one month back. Expected is 2022-03-26 07:00:00+01:00 However, the result will be 2022-03-26 07:00:00+02:00 (+02:00 instead of +01:00)

Here is a code example:

import pytz
import datetime
from dateutil.relativedelta import relativedelta

berlin = pytz.timezone("Europe/Berlin")
base_date = berlin.localize(datetime.datetime(2022, 4, 26, 7, 0))
offset = relativedelta(months=1, day=0, 
                       hour=base_date.hour, minute=base_date.minute, 
                       second=base_date.second, microsecond=0)
shifted = base_date - offset

expected = berlin.localize(datetime.datetime(2022, 3, 26, 7, 0))
print("shifted: ", shifted)
print("expected: ", expected)

This will print:

shifted:  2022-03-26 07:00:00+02:00
expected:  2022-03-26 07:00:00+01:00

The result is wrong because the day 2022-03-26 CET must always have a +1 hour shift.

Amir P
  • 123
  • 5

2 Answers2

1

since you use dateutil anyway, why not use it for tz handling?

from datetime import datetime
import dateutil

offset = dateutil.relativedelta.relativedelta(months=1)

dt = datetime(2022,4,26,7,tzinfo=dateutil.tz.gettz("Europe/Berlin"))

print(dt, dt-offset)
# 2022-04-26 07:00:00+02:00 2022-03-26 07:00:00+01:00

Side note, dateutil semantics are also consistend with the Python 3.9 way to handle time zone.

FObersteiner
  • 22,500
  • 8
  • 42
  • 72
0

So it seems you need to use .astimezone to get the desired results. As seen below.


import pytz
import datetime
from dateutil.relativedelta import relativedelta

berlin = pytz.timezone("Europe/Berlin")
base_date = berlin.localize(datetime.datetime(2022, 4, 26, 7, 0))
offset = relativedelta(months=1)
shifted = (base_date - offset).astimezone(berlin)
# shifted = shifted.astimezone(berlin)

expected = berlin.localize(datetime.datetime(2022, 3, 26, 7, 0))
print("base:", base_date)
print("shifted: ", shifted)
print("expected: ", expected)

I found this under examples and usage at PyTz

Jarvis
  • 669
  • 4
  • 10
  • This unfortunately won't work. It'll just move the one hour shift to the hour element. In fact, it just tries to apply the tz-shift on the hour to get the tz-shift that matches to expected. So the actual time point is still wrong by one hour. – Amir P May 10 '22 at 11:14
  • Ah, dang I missed that last night… hrm – Jarvis May 10 '22 at 20:50