5

I found some strange things. Here some examples.

from django.utils import timezone
value = u'2014-10-26 01:45:00'
#I know that a variable has  'Europe / Moscow' timezone. Let's tell Django about it.
TZ = timezone.pytz.timezone('Europe/Moscow')
d = timezone.datetime.strptime(value,'%Y-%m-%d %H:%M:%S')
print timezone.make_aware(d,TZ)
#raised AmbiguousTimeError: 2014-10-26 01:45:00

And then the fun begins

print timezone.make_aware(d+timezone.timedelta(minutes=15),TZ)
#out: 2014-10-26 02:00:00+03:00
print timezone.make_aware(d+timezone.timedelta(minutes=14),TZ)
#raised AmbiguousTimeError
print timezone.make_aware(d-timezone.timedelta(minutes=46),TZ)
#out: 2014-10-26 00:59:00+04:00
print timezone.make_aware(d-timezone.timedelta(minutes=45),TZ)
#raised AmbiguousTimeError     

So AmbiguousTimeError raised between 2014-10-26 00:59:00 and 2014-10-26 02:00:00

WHY? And how solve it?

hloroform
  • 133
  • 7

2 Answers2

3

timezon.make_aware(d, TZ) is equivalent to TZ.localize(d, is_dst=None) that raises an error for ambiguous times: 2014-10-26 01:45:00 happens twice in Europe/Moscow timezone:

# Europe/Moscow               UTC                           timestamp
2014-10-26 00:45:00 MSK+0400; 2014-10-25 20:45:00 UTC+0000; 1414269900
2014-10-26 01:00:00 MSK+0400; 2014-10-25 21:00:00 UTC+0000; 1414270800
2014-10-26 01:15:00 MSK+0400; 2014-10-25 21:15:00 UTC+0000; 1414271700
2014-10-26 01:30:00 MSK+0400; 2014-10-25 21:30:00 UTC+0000; 1414272600
2014-10-26 01:45:00 MSK+0400; 2014-10-25 21:45:00 UTC+0000; 1414273500
2014-10-26 01:15:00 MSK+0300; 2014-10-25 22:15:00 UTC+0000; 1414275300
2014-10-26 01:30:00 MSK+0300; 2014-10-25 22:30:00 UTC+0000; 1414276200
2014-10-26 01:45:00 MSK+0300; 2014-10-25 22:45:00 UTC+0000; 1414277100
2014-10-26 02:00:00 MSK+0300; 2014-10-25 23:00:00 UTC+0000; 1414278000

Notice: the utc offset is changed from +0400 to +0300 at 2am (Федеральный закон от 21 июля 2014 г. N 248-ФЗ).

To avoid the exception, you could call TZ.localize(d) (note: no is_dst=None) that works fine for existing non-ambiguous times but may fail (return wrong answer) for non-existing or ambiguous times.

If pytz Bug #1378150: Enhance support for end-of-DST-like ambiguous time is fixed then you could use TZ.localize(d, is_dst=True), TZ.localize(d, is_dst=False) to get time before and after the transition correspondingly.

If the bug is not fixed you could use my answer from Parsing of Ordered Timestamps in Local Time (to UTC) While Observing Daylight Saving Time to get the time after the transition:

# `naive` is a naive datetime object in local (Europe/Moscow) time
if tz.localize(naive, is_dst=False) == tz.localize(naive, is_dst=True):
    # Example: 2014/10/26 in Europe/Moscow timezone
    # ambiguous time but is_dst=False/True yield the same result
    # i.e., tz.localize() can't help, find UTC time  manually
    #NOTE: assume there is no other changes to UTC offset today (local.day)
    new_offset = tz.localize(naive + timedelta(1), is_dst=None).utcoffset()
    assert tz.localize(naive).utcoffset() != new_offset
    utc = (naive - new_offset).replace(tzinfo=pytz.utc)
    local = utc.astimezone(tz)
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
2

It's because of Daylight Savings Time.

At 2:00am on October 26, Muscovites will set their clocks back an hour. That means that they will see, for example, 1:30am twice on that day. So times between 1:00am and 2:00am are ambiguous, and Python / pytz is telling you that.

How you deal with that is going to be application-specific. You need to decide which 1:30am you're talking about (i.e. which UTC time you're talking about).

(Apparently this is the last year that most Russians will deal with DST. See this article for some interesting background.)

Kevin Christopher Henry
  • 46,175
  • 7
  • 116
  • 102
  • it is not DST. Europe/Moscow has UTC+0400 offset all year round. That offset is changed (by a specific government order) to UTC+0300 at 26 October 2014. Though the effect is the same as end-of-DST transition ("fall back") e.g., see [Bug #1378150 "Enhance support for end-of-DST-like ambiguous time..." `pytz` bug](https://bugs.launchpad.net/pytz/+bug/1378150) -- it means that even explicit `TZ.localize(naive, is_dst=True)` (or `is_dst=False`) won't give the desired time until the bug is fixed. – jfs Oct 21 '14 at 03:55