Using freezegun, why do pytz.utc and utcnow() output different datetimes?
Asked Answered
F

1

12

I'm puzzled why a function that freezes time with freezegun outputs different UTC times depending on whether datetime.datetime.utcnow() is called, or datetime.datetime.now(pytz.utc). I'm not saying it's broken, just that I don't understand why, and would like to know!

eg, using this function:

@freeze_time("2012-01-14 03:21:34", tz_offset=-4)
def test():
    print("utcnow(): %s" % datetime.datetime.utcnow())
    print("pytz.utc: %s" % datetime.datetime.now(pytz.utc))

the output is:

utcnow(): 2012-01-14 03:21:34
pytz.utc: 2012-01-13 23:21:34+00:00

I guess the first is a naive datetime, but why are they different times?

(Ultimately why I want to know: if I'm using freezegun in my tests, and I use pytz to generate times in my code being tested, I want to know what its 'correct' behaviour should be.)

Fruin answered 2/7, 2015 at 20:9 Comment(13)
Isn't that correct considering tz_offset=-4 ? One is aware the other is naiveLeucippus
I don't know, that's why I'm asking :) I assumed that given you can set a timezone offset with freezegun, and you're asking for UTC from each, that they would both have the same time. Even if only one had the timezone attached.Fruin
I mean, if you output datetime.datetime.now() in that function you get 2012-01-13 23:21:34. Is freezegun freezing the time to 2012-01-14 03:21 UTC or 2012-01-13 23:21 UTC?Fruin
Using now the date and time are converted to the tz‘s time zone. utcnow is gmtLeucippus
So you're saying: freezegun is setting the time to 2012-01-14 03:21 UTC, and timezone to -4 hrs. datetime.datetime.now() is the local time (ie, 2012-01-13 23:21) and I'm artificially forcing that time to have UTC timezone info?Fruin
Yes 2012-01-13 23:21) is the local time, it is also shown in the docs under timezones github.com/spulec/freezegun although the assert would fail for datetime.today because the time is not in datetime.today so it would be 00:00:00 for datetime.datetime(2012, 01, 13). If you use assert datetime.date.today() == datetime.datetime(2012, 01, 13).date() it should passLeucippus
No worries, datetimes can be confusing at the best of times, the fact the assertion fails in the docs would not help.Leucippus
According to the docs, the 2nd one should show: 2012-01-14 03:21:34+00:00. 23:21:34+00:00 looks like a bug.Rancor
@J.F.Sebastian, why should it be 03:21:34+00:00?Leucippus
@PadraicCunningham: what is unclear? Do you understand that utcnow() == now(pytz.utc).replace(tzinfo=None)?Rancor
@J.F.Sebastian,I thought you were referring to the example in the docs, so basically in the OPs code pytz.utc is being ignored?Leucippus
I think that applying pytz.utc like that doesn't affect the calculation of the time value - it simply takes the time that now() generated and gives it the UTC timezone, without actually changing the time. I may be wrong, but that's what I'm assuming is happening. So the docs are correct.Fruin
@PadraicCunningham: click the link in my comment: it shows the example from the docs that asserts that utcnow() == 03:21:34 and therefore now(pytz.utc) must be 03:21:34+00:00.Rancor
P
5

This is an issue within freezegun see here and here.

It does not look like that this will be fixed soon. In the end I used this as a workaround:

def freezegun_utc_workaround():
    return datetime.utcnow().replace(tzinfo=pytz.utc)

For this

 datetime.datetime.now(pytz.utc)

May be it is even better to wrap this and patch it manually.

Parvenu answered 22/4, 2019 at 13:15 Comment(2)
How did you use this? Can you include a full code sample?Frill
Replace all occurances of "datetime.datetime.now(pytz.utc)" with "freezegun_utc_workaround()". After that use "patch" from "unittest.mock" to make "freezegun_utc_workaround" return whatever you desire.Parvenu

© 2022 - 2024 — McMap. All rights reserved.