Daylight savings time question

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Dan Stromberg-2
Is there a way of "adding" 4 hours and getting a jump of 5 hours on
March 8th, 2015 (due to Daylight Savings Time), without hardcoding
when to spring forward and when to fall back?  I'd love it if there's
some library that'll do this for me.

#!/usr/bin/python

import pytz
import datetime

def main():
    # On 2015-03-08, 2:00 AM to 2:59AM Pacific time does not exist -
the clock jumps forward an hour.
    weird_naive_datetime = datetime.datetime(2015, 3, 8, 1, 0,
0).replace(tzinfo=pytz.timezone('US/Pacific'))
    weird_tz_aware_datetime =
weird_naive_datetime.replace(tzinfo=pytz.timezone('US/Pacific'))
    print(weird_tz_aware_datetime)
    four_hours=datetime.timedelta(hours=4)
    print('Four hours later is:')
    print(weird_tz_aware_datetime + four_hours)
    print('...but I want numerically 5 hours later, because of
Daylight Savings Time')

main()


Thanks!


Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Gary Herron-2
On 03/24/2015 03:24 PM, Dan Stromberg wrote:

> Is there a way of "adding" 4 hours and getting a jump of 5 hours on
> March 8th, 2015 (due to Daylight Savings Time), without hardcoding
> when to spring forward and when to fall back?  I'd love it if there's
> some library that'll do this for me.
>
> #!/usr/bin/python
>
> import pytz
> import datetime
>
> def main():
>      # On 2015-03-08, 2:00 AM to 2:59AM Pacific time does not exist -
> the clock jumps forward an hour.
>      weird_naive_datetime = datetime.datetime(2015, 3, 8, 1, 0,
> 0).replace(tzinfo=pytz.timezone('US/Pacific'))
>      weird_tz_aware_datetime =
> weird_naive_datetime.replace(tzinfo=pytz.timezone('US/Pacific'))
>      print(weird_tz_aware_datetime)
>      four_hours=datetime.timedelta(hours=4)
>      print('Four hours later is:')
>      print(weird_tz_aware_datetime + four_hours)
>      print('...but I want numerically 5 hours later, because of
> Daylight Savings Time')
>
> main()
>
>
> Thanks!

The pyzt module (which you've imported) has lots to say about this. Look
at its procedures "localize' and 'normalize' and all the rest of the
pyzt documentation.



--
Dr. Gary Herron
Department of Computer Science
DigiPen Institute of Technology
(425) 895-4418



Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Chris Angelico
In reply to this post by Dan Stromberg-2
On Wed, Mar 25, 2015 at 9:24 AM, Dan Stromberg <drsalists at gmail.com> wrote:
> Is there a way of "adding" 4 hours and getting a jump of 5 hours on
> March 8th, 2015 (due to Daylight Savings Time), without hardcoding
> when to spring forward and when to fall back?  I'd love it if there's
> some library that'll do this for me.

Fundamentally, this requires knowledge of timezone data. That means
you have to select a political time zone, which basically means you
want the Olsen database (tzdata) which primarily works with city
names. I'm not sure whether "US/Pacific" is suitable; I usually use
"America/Los_Angeles" for Pacific US time.

But that aside, what Gary said is what I would recommend.

ChrisA


Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Carl Meyer-4
In reply to this post by Dan Stromberg-2
Hi Dan,

On 03/24/2015 04:24 PM, Dan Stromberg wrote:

> Is there a way of "adding" 4 hours and getting a jump of 5 hours on
> March 8th, 2015 (due to Daylight Savings Time), without hardcoding
> when to spring forward and when to fall back?  I'd love it if there's
> some library that'll do this for me.
>
> #!/usr/bin/python
>
> import pytz
> import datetime
>
> def main():
>     # On 2015-03-08, 2:00 AM to 2:59AM Pacific time does not exist -
> the clock jumps forward an hour.
>     weird_naive_datetime = datetime.datetime(2015, 3, 8, 1, 0,
> 0).replace(tzinfo=pytz.timezone('US/Pacific'))
>     weird_tz_aware_datetime =
> weird_naive_datetime.replace(tzinfo=pytz.timezone('US/Pacific'))
>     print(weird_tz_aware_datetime)
>     four_hours=datetime.timedelta(hours=4)
>     print('Four hours later is:')
>     print(weird_tz_aware_datetime + four_hours)
>     print('...but I want numerically 5 hours later, because of
> Daylight Savings Time')
>
> main()

Much like the best advice for handling character encodings is "do all
your internal work in unicode, and decode/encode at your input/output
boundaries", the best advice for working with datetimes is "do all your
internal work in UTC; convert to UTC from the input timezone and
localize to the output timezone at your input/output boundaries." UTC
has no daylight savings time, so these issues disappear when you do your
calculations in UTC, and then the resulting dates are appropriately
handled by pytz when converting to local timezone at output.

So in this case, the following code gives the correct answer:

    naive_dt = datetime.datetime(2015, 3, 8, 1)
    tz = pytz.timezone('US/Pacific')
    local_dt = tz.localize(naive_dt)
    utc_dt = pytz.utc.normalize(local_dt)
    four_hours = datetime.timedelta(hours=4)
    new_utc_dt = utc_dt + four_hours
    new_local_dt = tz.normalize(new_utc_dt)

Someone may point out that you can actually just use pytz.normalize() to
solve this particular problem more directly, without the conversion to
UTC and back:

    naive_dt = datetime.datetime(2015, 3, 8, 1)
    tz = pytz.timezone('US/Pacific')
    local_dt = tz.localize(naive_dt)
    four_hours = datetime.timedelta(hours=4)
    new_local_dt = tz.normalize(utc_dt + four_hours)

(On the last line here, tz.normalize() is able to see that it's been
given a datetime which claims to be PST, but is after the spring
transition so should actually be PDT, and fixes it for you, correctly
bumping it by an hour in the process.)

While it's true that for this specific case the non-UTC method is more
direct, if you're writing any kind of sizable system that needs to
handle timezones correctly, you'll still be doing yourself a favor by
handling all datetimes in UTC internally.

Also, unless you really know what you're doing, you should generally use

    a_pytz_timezone_obj.localize(naive_dt)

to turn a naive datetime into a timezone-aware one, instead of

    naive_dt.replace(tzinfo=a_pytz_timezone_obj).

The latter just blindly uses the exact timezone object you give it,
without regard for when the datetime actually is (thus fails to respect
whether the timezone should be in DST or not, or any other historical
local-time transitions), whereas the `localize` method ensures that the
resulting aware datetime actually has the correct "version" of the
timezone applied to it, given when it is.

You can easily observe this difference, because the "default" version of
a timezone in pytz is the first one listed in the timezone database,
which for many timezones is LMT (local mean time), a historical timezone
offset abandoned in the late 1800s most places, which is often offset
from modern timezones by odd amounts like seven or eight minutes. For
example:

    >>> import pytz, datetime
    >>> dt = datetime.datetime(2015, 3, 8, 1)
    >>> tz = pytz.timezone('US/Pacific')
    >>> tz
    <DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>
    >>> bad = dt.replace(tzinfo=tz)
    >>> bad
    datetime.datetime(2015, 3, 8, 1, 0, tzinfo=<DstTzInfo 'US/Pacific'
LMT-1 day, 16:07:00 STD>)
    >>> in_utc = pytz.utc.normalize(bad)
    >>> in_utc
    datetime.datetime(2015, 3, 8, 8, 53, tzinfo=<UTC>)

Note that the timezone assigned to the `bad` datetime is US/Pacific LMT,
which hasn't been in use since Nov 1883 [1] and which is 7 minutes
offset from modern timezones, resulting in a very surprising result when
you then convert that to UTC. In contrast, localize() does the right
thing, using PST instead of LMT because it knows that's the correct
offset for US/Pacific at 1am on March 8, 2015:

    >>> good = tz.localize(dt)
    >>> good
    datetime.datetime(2015, 3, 8, 1, 0, tzinfo=<DstTzInfo 'US/Pacific'
PST-1 day, 16:00:00 STD>)
    >>> in_utc = pytz.utc.normalize(good)
    >>> in_utc
    datetime.datetime(2015, 3, 8, 9, 0, tzinfo=<UTC>)


Carl

 [1] https://github.com/eggert/tz/blob/master/northamerica#L409

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/python-list/attachments/20150324/38b1b073/attachment.sig>

Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Carl Meyer-4
In reply to this post by Chris Angelico
On 03/24/2015 04:56 PM, Chris Angelico wrote:
> On Wed, Mar 25, 2015 at 9:24 AM, Dan Stromberg <drsalists at gmail.com> wrote:
>> Is there a way of "adding" 4 hours and getting a jump of 5 hours on
>> March 8th, 2015 (due to Daylight Savings Time), without hardcoding
>> when to spring forward and when to fall back?  I'd love it if there's
>> some library that'll do this for me.
>
> Fundamentally, this requires knowledge of timezone data. That means
> you have to select a political time zone, which basically means you
> want the Olsen database (tzdata)

Yes, which is made available in Python via the pytz package.

> which primarily works with city
> names. I'm not sure whether "US/Pacific" is suitable; I usually use
> "America/Los_Angeles" for Pacific US time.

US/Pacific is an alias for America/Los_Angeles, and is also part of the
Olson database (though I guess it's considered an "old" name for the
timezone): https://github.com/eggert/tz/blob/master/backward

Carl

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/python-list/attachments/20150324/cdeaf353/attachment.sig>

Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Dan Stromberg-2
In reply to this post by Dan Stromberg-2
This appears to do what I wanted:

#!/usr/bin/python

from __future__ import print_function

import pytz
import datetime

# Is there a good way of jumping ahead 5 hours instead of 4 on 2015-03-08?

def main():
    # On 2015-03-08, 2:00 AM to 2:59AM Pacific time does not exist -
the clock jumps forward an hour.
    us_pacific = pytz.timezone('US/Pacific')

    weird_naive_datetime = datetime.datetime(2015, 3, 8, 1, 0, 0)
    print('weird_naive_datetime:  ', weird_naive_datetime)

    weird_tz_aware_datetime = us_pacific.localize(weird_naive_datetime)
    print('weird_tz_aware_datetime', weird_tz_aware_datetime)

    four_hours=datetime.timedelta(hours=4)
    print('Four hours later is:   ',
us_pacific.normalize(weird_tz_aware_datetime + four_hours))

    print('...we want numerically 5 hours later (so 6AM), because of
Daylight Savings Time')

main()


On Tue, Mar 24, 2015 at 3:24 PM, Dan Stromberg <drsalists at gmail.com> wrote:

> Is there a way of "adding" 4 hours and getting a jump of 5 hours on
> March 8th, 2015 (due to Daylight Savings Time), without hardcoding
> when to spring forward and when to fall back?  I'd love it if there's
> some library that'll do this for me.
>
> #!/usr/bin/python
>
> import pytz
> import datetime
>
> def main():
>     # On 2015-03-08, 2:00 AM to 2:59AM Pacific time does not exist -
> the clock jumps forward an hour.
>     weird_naive_datetime = datetime.datetime(2015, 3, 8, 1, 0,
> 0).replace(tzinfo=pytz.timezone('US/Pacific'))
>     weird_tz_aware_datetime =
> weird_naive_datetime.replace(tzinfo=pytz.timezone('US/Pacific'))
>     print(weird_tz_aware_datetime)
>     four_hours=datetime.timedelta(hours=4)
>     print('Four hours later is:')
>     print(weird_tz_aware_datetime + four_hours)
>     print('...but I want numerically 5 hours later, because of
> Daylight Savings Time')
>
> main()
>
>
> Thanks!


Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

Chris Angelico
In reply to this post by Carl Meyer-4
On Wed, Mar 25, 2015 at 10:18 AM, Carl Meyer <carl at oddbird.net> wrote:
> US/Pacific is an alias for America/Los_Angeles, and is also part of the
> Olson database (though I guess it's considered an "old" name for the
> timezone): https://github.com/eggert/tz/blob/master/backward

Ah, okay. No problem then. If it works, it works.

ChrisA


Reply | Threaded
Open this post in threaded view
|

Daylight savings time question

random832@fastmail.us
In reply to this post by Dan Stromberg-2
On Tue, Mar 24, 2015, at 18:24, Dan Stromberg wrote:
> Is there a way of "adding" 4 hours and getting a jump of 5 hours on
> March 8th, 2015 (due to Daylight Savings Time), without hardcoding
> when to spring forward and when to fall back?  I'd love it if there's
> some library that'll do this for me.

If you're adding an extra four hours to your "local" time, then your new
offset is going to be -3 hours instead of -7: to do this "properly" you
would have to define a new timezone.