arrow-py / arrow

🏹 Better dates & times for Python
https://arrow.readthedocs.io
Apache License 2.0
8.71k stars 673 forks source link

arrow.get(datetime.now()).humanize() == "6 hours ago" #944

Closed zachriggle closed 3 years ago

zachriggle commented 3 years ago

Issue Description

It looks like arrow is doing something weird timezone information when operating datetime objects, including datetime objects returned by e.g. dateparser.parse('now').

What is the appropriate incantation to get arrow and datetime to agree? I've managed to do it in one of my projects by using epochseconds, but would prefer to keep my dates as either datetime or arrow object representations.

I am in central standard time, so the "6 hours ago" seems to reflect my TZ offset from UTC, but even providing a ..., tzinfo= does not appear to do the correct thing.

Overall, the mixture of simple things that don't work is overwhelming, and there should maybe be some examples in the documentation for how to do this right (e.g. setting tzinfo on the datetime object makes arrow's object correct, but setting tzinfo on the arrow object does not).

date(1) vs datetime

As best I can tell, datetime.now() is correct, and lacks a specific timezone.

>>> import datetime, subprocess
>>> datetime.datetime.now()
datetime.datetime(2021, 3, 9, 19, 55, 36, 539945)
>>> subprocess.check_output('date', text=True)
'Tue Mar  9 19:55:39 CST 2021\n'

datetime vs arrow

This jives with the 6 hour time difference from UTC, but why does it say "6 hours ago"? Neither arrow nor datetime were provided with a timezone, so this seems odd.

>>> import arrow, datetime
>>> arrow.get(datetime.datetime.now()).humanize()
'6 hours ago'

datetime with tzinfo

This happens even with a custom timezone, unless the timezone is specified on the datetime object.

>>> import arrow, dateparser, dateutil, datetime
>>> tz = dateutil.tz.tzlocal()

# With the local timezone on arrow.get() does NOT work
>>> arrow.get(datetime.datetime.now(), tzinfo=tz).humanize()
'6 hours ago'

# With the local timezone on datetime.now() DOES work
>>> arrow.get(datetime.datetime.now(tz=tz)).humanize()
'just now'

# With a custom timezone (set to be GMT-6)
>>> mytz = dateutil.tz.tzoffset('MyTZ', datetime.timedelta(hours=6))
>>> mytz
tzoffset('MyTZ', 21600)
>>> arrow.get(datetime.datetime.now(), tzinfo=mytz).humanize()
'6 hours ago'

dateparser

Per the documentation:

dateparser relies on following libraries in some ways: • tzlocal to reliably get local timezone.

However, this doesn't seem to work correctly either.

>>> import arrow, dateparser
>>> arrow.get(dateparser.parse('now')).humanize()
'6 hours ago'

dateparser with tzinfo

This happens regardless of whether tzinfo is specified.

>>> import arrow, dateparser, dateutil
>>> tz = dateutil.tz.tzlocal()
>>> tz
tzlocal()
>>> arrow.get(dateparser.parse('now'), tzinfo=tz).humanize()
'6 hours ago'

Unix Timestamps

It looks like there's an issue with timestamps as well. If I pass the timestamp directly to arrow.get(), it returns the correct value.

>>> dt = datetime.datetime.fromtimestamp(1615341484)
>>> dt
datetime.datetime(2021, 3, 9, 19, 58, 4)
>>> !date
Tue Mar  9 20:02:38 CST 2021

# We can see that arrow is correct in its humanize()
>>> arrow.get(1615341484).humanize()
'4 minutes ago'

# However, using the datetime derived from the timestamp exhibits the same issue
>>> arrow.get(dt).humanize()
'6 hours ago'

System Info

$ sw_vers
ProductName:    macOS
ProductVersion: 11.3
BuildVersion:   20E5172h
$ python3 --version
Python 3.8.2
$ python3 -m pip freeze | grep arrow
arrow==1.0.3
systemcatch commented 3 years ago

Hey @zachriggle there definitely looks to be some weirdness going on with the tzinfo setting here. I will investigate this further.

Two other points, Arrow objects are never naive, they default to UTC if no tzinfo is provided. Also arrow.get(dateparser.parse('now')) is not officially supported but will probably work.

systemcatch commented 3 years ago

The problem is that the tzinfo supplied to arrow.get() is being ignored in certain cases, for example:

(arrow) chris@Z490:~/arrow$ python
Python 3.9.1 (default, Feb 17 2021, 16:56:42) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import arrow
>>> import datetime
>>> arrow.get(datetime.datetime.now(), tzinfo="America/Chicago")
<Arrow [2021-03-17T21:33:42.404027+00:00]>
>>> arrow.get(datetime.datetime.now(), tzinfo="America/Chicago").tzinfo
tzutc()

It defaults to UTC which causes strange results.

The bug is in https://github.com/arrow-py/arrow/blob/master/arrow/factory.py#L241 where the tzinfo kwarg is dropped.

zachriggle commented 3 years ago

Glad you found the culprit! I eagerly await a new Arrow release.

I can't contribute to FOSS (employer rules) otherwise I'd create the PR myself.

On Wed, Mar 17, 2021 at 4:47 PM Chris @.***> wrote:

The problem is that the tzinfo supplied to arrow.get() is being ignored in certain cases, for example:

(arrow) chris@Z490:~/arrow$ python Python 3.9.1 (default, Feb 17 2021, 16:56:42) [GCC 10.2.0] on linux Type "help", "copyright", "credits" or "license" for more information.

import arrow import datetime arrow.get(datetime.datetime.now(), tzinfo="America/Chicago")<Arrow [2021-03-17T21:33:42.404027+00:00]> arrow.get(datetime.datetime.now(), tzinfo="America/Chicago").tzinfotzutc()

It defaults to UTC which causes strange results.

The bug is in https://github.com/arrow-py/arrow/blob/master/arrow/factory.py#L241 where the tzinfo kwarg is dropped.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/arrow-py/arrow/issues/944#issuecomment-801464385, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAA3IGFWUUTAD6GGA352R5LTEEPORANCNFSM4Y45YTRQ .