Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

datetime.datetime.now() mangles tzinfo #43952

Closed
smontanaro opened this issue Sep 6, 2006 · 5 comments
Closed

datetime.datetime.now() mangles tzinfo #43952

smontanaro opened this issue Sep 6, 2006 · 5 comments
Labels
stdlib Python modules in the Lib dir

Comments

@smontanaro
Copy link
Contributor

BPO 1553577
Nosy @tim-one, @smontanaro

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2006-09-09.02:08:32.000>
created_at = <Date 2006-09-06.18:11:13.000>
labels = ['invalid', 'library']
title = 'datetime.datetime.now() mangles tzinfo'
updated_at = <Date 2006-09-09.02:08:32.000>
user = 'https://github.com/smontanaro'

bugs.python.org fields:

activity = <Date 2006-09-09.02:08:32.000>
actor = 'nnorwitz'
assignee = 'nnorwitz'
closed = True
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2006-09-06.18:11:13.000>
creator = 'skip.montanaro'
dependencies = []
files = []
hgrepos = []
issue_num = 1553577
keywords = []
message_count = 5.0
messages = ['29780', '29781', '29782', '29783', '29784']
nosy_count = 4.0
nosy_names = ['tim.peters', 'skip.montanaro', 'nnorwitz', 'zenzen']
pr_nums = []
priority = 'normal'
resolution = 'not a bug'
stage = None
status = 'closed'
superseder = None
type = None
url = 'https://bugs.python.org/issue1553577'
versions = []

@smontanaro
Copy link
Contributor Author

When using the pytz package (http://pytz.sf.net/) to create
timezone info objects datetime.datetime.now() behaves
differently than the regular datetime.datetime()
contstructor. Here's an example:

    >>> import pytz
    >>> info = pytz.timezone("US/Central")
    >>> info
    <DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>
    >>> import datetime
    >>> now = datetime.datetime.now(tz=info)
    >>> now
    datetime.datetime(2006, 9, 6, 12, 44, 18, 983849,
tzinfo=<DstTzInfo 'US/Central' CDT-1 day, 19:00:00 DST>)
    >>> t2 = datetime.datetime(2006, 9, 6, 12, 44, 18,
983849, tzinfo=info)
    >>> t2
    datetime.datetime(2006, 9, 6, 12, 44, 18, 983849,
tzinfo=<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>)
    >>> now.tzinfo == info
    False
    >>> t2.tzinfo == info
    True

It appears that datetime.datetime.now() makes an
off-by-one-hour copy of the timezone info it was passed.
I've reproduced this on 2.4.3 and 2.5c1 as of August 17.

(It's also a little annoying that the timezone arg for
datetime.datetime.now() is "tz" while the timezone arg for
datetime.datetime() is "tzinfo". Is there a good
reason for
them to be different?)

Skip

@smontanaro smontanaro added the stdlib Python modules in the Lib dir label Sep 6, 2006
@smontanaro smontanaro added the stdlib Python modules in the Lib dir label Sep 6, 2006
@nnorwitz
Copy link
Mannequin

nnorwitz mannequin commented Sep 7, 2006

Logged In: YES
user_id=33168

Since Tim wrote this code AFAIK, there *had* to be a good
reason. :-)

@tim-one
Copy link
Member

tim-one commented Sep 7, 2006

Logged In: YES
user_id=31435

tzinfo is the name of a datetime data attribute, and the
same name (i.e., "tzinfo") is generally used for arguments
that mindlessly attach a subclass of the tzinfo class to
an object as the value of its tzinfo data attribute. The
datetime constructor is an example of that. tz is
generally used when the time zone info is /actively
applied/, as now() does.

In contrast, the datetime constructor never makes any
attempt at conversion; if a tzinfo argument is passed, it's
merely tacked on to the datetime object.

Beyond that, I have no idea why the pytz class passed to
now() isn't showing up as the resulting datetime object's
tzinfo member. For example, that's not what happens if you
try this in the Python sandbox "datetime" directory:

>>> from US import Eastern
>>> from datetime import datetime
>>> now = datetime.now(Eastern)
>>> now
datetime.datetime(2006, 9, 7, 12, 49, 48, 430000,
tzinfo=<US.USTimeZone object at 0x009E89B0>)
>>> t2 = datetime(2006, 9, 7, 12, 49, 48, 430000,
tzinfo=Eastern)
>>> t2
datetime.datetime(2006, 9, 7, 12, 49, 48, 430000,
tzinfo=<US.USTimeZone object at 0x009E89B0>)
>>> now.tzinfo == Eastern
True
>>> t2.tzinfo == Eastern
True
>>> t2.tzinfo is now.tzinfo is Eastern
True

I expect the pytz developers could shed light on that.
datetime.now(tz) with tz not None first mindlessly
constructs a datetime object (let's call it self) based on
current (UTC) time with tz attached as the value of its
tzinfo data attribute, and then returns the result of invoking

tz.fromutc(self)

So if pytz overrides the default fromutc() implementation
in its tzinfo subclasses, you'll get back whatever they
decided to return from it. No code in Python "makes an
off-by-one-hour copy" here.

In short, I believe your primary question here is about how
pytz works, and I can't answer that. IIRC, pytz does fancy
stuff trying to avoid the 1-hour ambiguities at DST
transition times, and I wouldn't be surprised if, toward
that end, they have multiple internal tzinfo subclasses for
each "conceptual" time zone.

@zenzen
Copy link
Mannequin

zenzen mannequin commented Sep 8, 2006

Logged In: YES
user_id=46639

This is a pytz issue, and a result of me abusing Tim's code
in ways he never intended. Tim is quite correct in that
there are actually several tzinfo instances under the
covers. In order to to unambiguous localtime calculations,
an extra bit of information needs to be known (the is_dst
flag in most datetime libraries). pytz uses the tzinfo
instance to store this bit of information. The side affects
of doing this are the behavior you noticed, and confusion as
constructing datetime instances needs to be done as per
pytz's README rather than the documented method in the
Python Library Reference.

>>> import pytz
>>> info = pytz.timezone('US/Central')
>>> info
<DstTzInfo 'US/Central' CST-1 day, 18:00:00 STD>
>>> from datetime import datetime
>>> now = info.localize(datetime.now(), is_dst=True)
>>> now
datetime.datetime(2006, 9, 8, 11, 19, 29, 587943,
tzinfo=<DstTzInfo 'US/Central' CDT-1 day, 19:00:00 DST>)
>>> t2 = info.localize(datetime(2006, 9, 8, 11, 19, 29, 587943))
>>> t2
datetime.datetime(2006, 9, 8, 11, 19, 29, 587943,
tzinfo=<DstTzInfo 'US/Central' CDT-1 day, 19:00:00 DST>)
>>> now.tzinfo == info
False
>>> t2.tzinfo == info
False
>>> now.tzinfo == t2.tzinfo
True

Last time I tried, it seemed impossible to support both
pytz's goals and the datetime construction API specified in
the Python Library Reference without extending the Python
datetime module (and I have yet to specify what would be
required).

If I was to add an __eq__ method to the tzinfo classes, I'm
not actually sure what the correct behavior should be.
Should US/Eastern Daylight Savings Time really equate to
US/Eastern Standard Time? Should US/Eastern Daylight Savings
Time in 2002 really equate to US/Eastern Daylight Savings
Time in 2007? The umbrella timezone might be the same, but
the UTC offsets or switchover dates are different.

The pytz bugtracker is at
https://launchpad.net/products/pytz/+bugs

@nnorwitz
Copy link
Mannequin

nnorwitz mannequin commented Sep 9, 2006

Logged In: YES
user_id=33168

Based on Stuart's comment, I'm closing this. Skip, if
there's any part of this that you think is a bug, re-open
this with a comment about the precise issue or open a new
bug report. Thanks.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir
Projects
None yet
Development

No branches or pull requests

2 participants