I'm implementing feature with scheduled publishing of object.
User chooses the time to publish and i created a cron task to run every minute and check if it's the time to publish.
Users are from different timezones.
So i need to compare two datetimes:
>>user_chosen_time
datetime.datetime(2012, 12, 4, 14, 0, tzinfo=tzinfo(120))
>>curdate=datetime.datetime.now()
datetime.datetime(2012, 12, 4, 18, 4, 20, 17340)
>>user_chosen_time==curdate
*** TypeError: can't compare offset-naive and offset-aware datetimes
Sorry for rather stupid question but i need to discuss this. Thanks
As the error suggests you "can't compare offset-naive and offset-aware datetimes". It means that you should compare two datetimes that are both timezone-aware or both timezone-naive (not timezone-aware). In your codes, curdate has no timezone info and thus could not be compared with user_chosen_time which is timezone-aware.
First you should assign correct timezone to each datetime. And then you could directly compare two datetimes with different timezones.
Example (with pytz):
import pytz
import datetime as dt
# create timezone
nytz=pytz.timezone('America/New_York')
jptz=pytz.timezone('Asia/Tokyo')
# randomly initiate two timestamps
a=dt.datetime(2018,12,13,11,2)
b=dt.datetime(2018,12,13,22,45)
# assign timezone to timestamps
a=nytz.localize(a)
b=jptz.localize(b)
# a = datetime.datetime(2018, 12, 13, 11, 2, tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
# b = datetime.datetime(2018, 12, 13, 22, 45, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)
a>b # True
b>a # False
For other methods you could refer to Convert a python UTC datetime to a local datetime using only python standard library?.
http://pytz.sourceforge.net/ is where you want to look when you want to eliminate the timezone differencies :)
edit: just found this post on SO that may give you a lot more informations on your problem
Related
There's lots of SO answers on ensuring datetimes are a particular timezone. For example you can ensure your datetime is UTC with
from datetime import datetime
import pytz
now_utc = datetime.utcnow()
which yields:
datetime.datetime(2017, 5, 11, 17, 37, 5, 602054)
you can make that datetime aware of its timezone (e.g. for asserting two different datetime objects are from the same timezone) with
now_utc_aware = datetime.now(pytz.utc)
datetime.datetime(2017, 5, 11, 17, 38, 2, 757587, tzinfo=< UTC>)
But when I pull the date from a timezone-aware datetime, I seem to lose the timezone-awareness.
now_utc_aware.date()
datetime.date(2017, 5, 11)
Interestingly, there's a SO question which seems to ask exactly this, and about a date specifically (datetime.today()), but the answers (including an accepted one) relate to datetimes. The code I've seen to add timezone awareness to datetimes all seem to throw errors on my datetime.date object.
Is it possible to add timezone awareness to a date object?
From the Python docs:
class datetime.date
An idealized naive date, ... Attributes: year, month, and day.
There's nothing there for time or time zone. It's just a date.
While it is true that not everywhere on Earth is on the same date simultaneously (because of time zones), that doesn't mean a date itself has time zone awareness.
As a real-world analogy, think of a date as just a square on a calendar. One cannot start talking about timezones without introducing time, which is measured by a clock, not a calendar.
I'm trying to understand why I'm getting these results when converting time zones to UTC:
In [74]: d1 = datetime(2007, 12, 5, 6, 30,tzinfo=pytz.timezone('US/Pacific'))
In [75]: d1
Out[75]: datetime.datetime(2007, 12, 5, 6, 30, tzinfo=<DstTzInfo 'US/Pacific' LMT-1 day, **16:07:00 STD**>)
In [76]: d1.astimezone(pytz.utc)
Out[76]: datetime.datetime(2007, 12, 5, 14, 23, tzinfo=<UTC>)
Why did 6:30am become 2:23pm?
On the other hand, if I use the following approach, I get the expected result:
In [90]: d2 = datetime(2007, 12, 5, 6, 30)
In [91]: uspac = pytz.timezone('US/Pacific')
In [92]: d2_aware = uspac.localize(d2)
In [94]: d2_aware.astimezone(pytz.utc)
Out[94]: datetime.datetime(2007, 12, 5, 14, 30, tzinfo=<UTC>)
What I got is just a workaround, the simple rule is Never create datetime with timezone info by using datetime().
This sample would give you a hint for this. As you see, you could avoid the unexpected difference, once and only you make "naive" datetime (it is, datetime without timezone info) and then localize it (it is not applied when you create datetime on UTC though) :
import pytz
from datetime import datetime
# make Jan 1 on PDT -> UTC
pdt = pytz.timezone("America/Los_Angeles")
pdtnow1 = datetime(2014,1,1, tzinfo=pdt)
pdtnow2 = pdt.localize(datetime(2014,1,1))
pytz.utc.normalize(pdtnow1)
# > datetime.datetime(2014, 1, 1, 7, 53, tzinfo=<UTC>)
pytz.utc.normalize(pdtnow2)
# > datetime.datetime(2014, 1, 1, 8, 0, tzinfo=<UTC>)
# make Jan 1 on UTC -> PDT
utcnow1 = datetime(2014,1,1, tzinfo=pytz.utc)
utcnow2 = pytz.utc.localize(datetime(2014,1,1))
pdt.normalize(utcnow1)
# > datetime.datetime(2013, 12, 31, 16, 0,
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
pdt.normalize(utcnow2)
# > datetime.datetime(2013, 12, 31, 16, 0,
# > tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
From the partial documentation:
http://pytz.sourceforge.net/#localized-times-and-date-arithmetic
Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones. [...] It is safe for timezones without daylight saving transitions though, such as UTC. [...] The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.
Unfortunately, creating timezone aware dates using this method doesn't work.
If you are using Django, they have a utility function, make_aware, that does this correctly.
from django.utils.timezone import make_aware
from pytz import timezone
unaware_datetime = datetime(2007, 12, 5)
local_datetime = make_aware(datetime(2007, 12, 5))
specific_datetime = make_aware(datetime(2007, 12, 5), timezone("Australia/Melbourne"))
If you're not using Django, then the source code for the make_aware function may give you inspiration.
I'm revisiting some questions about date and time to see if some of the newer libraries prove more helpful in situations like this (or not). pendulum is one that stores timezone with date and time, making it particularly valuable in situations such as this.
>>> import pendulum
>>> d1 = pendulum.datetime(2007,12,5,6,30, tzinfo='US/Pacific')
>>> d1
<Pendulum [2007-12-05T06:30:00-08:00]>
>>> d1.timezone
<Timezone [US/Pacific]>
>>> d1.astimezone(tz='UTC')
<Pendulum [2007-12-05T14:30:00+00:00]>
Lots of other sweet features too.
Print d2_aware before .astimezone and you see PST-1 (Pacific Standard Time) but in first example you have LMT-1 (Local Mean Time) - and probably it can give 7 minutes difference.
But I don't know why pytz use different timezones.
I have some timestamp data from a database and I need to convert it to a Python datetime (in tuple format). How can I do that?
For instance, I need to convert 2013-04-16 16:31:35.649+05:30 format to datetime(2013, 4, 16, 16, 31, 35).
I am trying to do the, Django, new user registration part of the project and whenever the user submits the data and registers then he gets an verification email but whenever the user clicks the verification link then, it produces the following error:
can't compare offset-naive and offset-aware datetimes
from dateutil import parser
dt = parser.parse("2013-04-16 16:31:35.649+05:30")
# dt == datetime.datetime(2013, 4, 16, 16, 31, 35, 649000, tzinfo=tzoffset(None, 19800))
dt.astimezone(pytz.utc).replace(tzinfo=None)
returns datetime.datetime(2013, 4, 16, 11, 1, 35, 649000) which is the same instant in UTC and without tzinfo (offset-naive).
If you want to store datetime objects without tzinfo, then store it in UTC.
Large parts of the world use daylight saving time, which makes some offset-naive datetimes ambiguous. UTC does not have these problems.
I am writing a program which deals a lot with timezones and crossing them. The two things I deal with most are creating a datetime object from "now" and then localizing a naive datetime object.
To create a datetime object from now in the pacific timezone, I am currently doing this (python 2.7.2+)
from datetime import datetime
import pytz
la = pytz.timezone("America/Los_Angeles")
now = datetime.now(la)
Is this correct with regards to DST? If not, I suppose I should be doing:
now2 = la.localize(datetime.now())
My question is why? Can anyone show me a case where the first is wrong and the seconds is right?
As for my seconds question, suppose I had a naive date and time from some user input for 9/1/2012 at 8:00am in Los Angeles, CA. Is the right way to make the datetime like this:
la.localize(datetime(2012, 9, 1, 8, 0))
If not, how should I be building these datetimes?
From the pytz documentation:
The preferred way of dealing with times is to always work in UTC, converting to localtime only when generating output to be read by humans.
So ideally you should be using utcnow instead of now.
Assuming for some reason that your hands are tied and you need to work with local times, you can still run into a problem with trying to localize the current time if you're doing it during the daylight saving transition window. The same datetime might occur twice, once during daylight time and again during standard time, and the localize method doesn't know how to settle the conflict unless you tell it explicitly with the is_dst parameter.
So to get the current UTC time:
utc = pytz.timezone('UTC')
now = utc.localize(datetime.datetime.utcnow())
And to convert it to your local time (but only when you must):
la = pytz.timezone('America/Los_Angeles')
local_time = now.astimezone(la)
Edit: as pointed out in the comments by #J.F. Sebastian, your first example using datetime.now(tz) will work in all cases. Your second example fails during the fall transition as I outlined above. I still advocate using UTC instead of local time for everything except display.
The first solution is correct with regards to DST, and the second solution is bad.
I'll give an example. Here in Europe, when running this code:
from datetime import datetime
import pytz # $ pip install pytz
la = pytz.timezone("America/Los_Angeles")
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
now = datetime.now(la)
now2 = la.localize(datetime.now())
now3 = datetime.now()
print(now.strftime(fmt))
print(now2.strftime(fmt))
print(now3.strftime(fmt))
I get the following:
2012-08-30 12:34:06 PDT-0700
2012-08-30 21:34:06 PDT-0700
2012-08-30 21:34:06
datetime.now(la) creates a datetime with the current time in LA, plus the timezone information for LA.
la.localize(datetime.now()) adds timezone information to the naive datetime, but does no timezone conversion; it just assumes the time was already in this timezone.
datetime.now() creates a naive datetime (without timezone information) with the local time.
As long as you are in LA, you will not see the difference, but if your code ever runs somewhere else, it will probably not do what you wanted.
Apart from that, if you ever need to deal seriously with timezones, it is better to have all your times in UTC, saving yourself a lot of trouble with DST.
This works:
# naive datetime
d = datetime.datetime(2016, 11, 5, 16, 43, 45)
utc = pytz.UTC # UTC timezone
pst = pytz.timezone('America/Los_Angeles') # LA timezone
# Convert to UTC timezone aware datetime
d = utc.localize(d)
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>)
# show as in LA time zone (not converting here)
d.astimezone(pst)
>>> datetime.datetime(2016, 11, 5, 9, 43, 45,
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
# we get Pacific Daylight Time: PDT
# add 1 day to UTC date
d = d + datetime.timedelta(days=1)
>>> datetime.datetime(2016, 11, 6, 16, 43, 45, tzinfo=<UTC>)
d.astimezone(pst) # now cast to LA time zone
>>> datetime.datetime(2016, 11, 6, 8, 43, 45,
tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>)
# Daylight saving is applied -> we get Pacific Standard Time PST
This DOES NOT work:
# naive datetime
d = datetime.datetime(2016, 11, 5, 16, 43, 45)
utc = pytz.UTC # UTC timezone
pst = pytz.timezone('America/Los_Angeles') # LA timezone
# convert to UTC timezone aware datetime
d = utc.localize(d)
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>)
# convert to 'America/Los_Angeles' timezone: DON'T DO THIS
d = d.astimezone(pst)
>>> datetime.datetime(2016, 11, 5, 9, 43, 45,
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
# we are in Pacific Daylight Time PDT
# add 1 day to LA local date: DON'T DO THAT
d = d + datetime.timedelta(days=1)
>>> datetime.datetime(2016, 11, 6, 9, 43, 45,
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
# Daylight Saving is NOT respected, we are still in PDT time, not PST
Conclusion:
datetime.timedelta() DOES NOT account for daylight saving.
Do your time add/subtract in UTC timezone ALWAYS.
Cast to local time only for output / display.
The pytz website says:
Unfortunately using the tzinfo argument of the standard datetime
constructors ‘’does not work’’ with pytz for many timezones.
So you should not use datetime.now(la). I don't know the specifics, but some timezones operate on more exotic rules then we are used to, and python's datetime code can't handle them. By using pytz's code, they should be handled correctly since that's pytz's purpose. It may also have issues for the times that occur twice thanks to jumping times in daylight savings.
As for the second question, that's exactly what the documentation shows, so you should be good.
I'm having some weird issues with pytz's .localize() function. Sometimes it wouldn't make adjustments to the localized datetime:
.localize behaviour:
>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>
>>> d
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421)
>>> tz.localize(d)
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
>>> tz.normalize(tz.localize(d))
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
As you can see, time has not been changed as a result of localize/normalize operations.
However, if .replace is used:
>>> d.replace(tzinfo=tz)
datetime.datetime(2009, 9, 2, 14, 45, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>)
>>> tz.normalize(d.replace(tzinfo=tz))
datetime.datetime(2009, 9, 2, 15, 1, 42, 91421,
tzinfo=<DstTzInfo 'Africa/Abidjan' GMT0:00:00 STD>)
Which seems to make adjustments into datetime.
Question is - which is correct and why other's wrong?
localize just assumes that the naive datetime you pass it is "right" (except for not knowing about the timezone!) and so just sets the timezone, no other adjustments.
You can (and it's advisable...) internally work in UTC (rather than with naive datetimes) and use replace when you need to perform I/O of datetimes in a localized way (normalize will handle DST and the like).
localize is the correct function to use for creating datetime aware objects with an initial fixed datetime value. The resulting datetime aware object will have the original datetime value. A very common usage pattern in my view, and one that perhaps pytz can better document.
replace(tzinfo = ...) is unfortunately named. It is a function that is random in its behaviour. I would advise avoiding the use of this function to set timezones unless you enjoy self-inflicted pain. I have already suffered enough from using this function.
This DstTzInfo class is used for timezones where the offset from UTC changes at certain points in time. For example (as you are probably aware), many locations transition to "daylight savings time" at the beginning of Summer, and then back to "standard time" at the end of Summer. Each DstTzInfo instance only represents one of these timezones, but the "localize" and "normalize" methods help you get the right instance.
For Abidjan, there has only ever been one transition (according to pytz), and that was in 1912:
>>> tz = pytz.timezone('Africa/Abidjan')
>>> tz._utc_transition_times
[datetime.datetime(1, 1, 1, 0, 0), datetime.datetime(1912, 1, 1, 0, 16, 8)]
The tz object we get out of pytz represents the pre-1912 timezone:
>>> tz
<DstTzInfo 'Africa/Abidjan' LMT-1 day, 23:44:00 STD>
Now looking up at your two examples, see that when you call tz.localize(d) you do NOT get this pre-1912 timezone added to your naive datetime object. It assumes that the datetime object you give it represents local time in the correct timezone for that local time, which is the post-1912 timezone.
However in your second example using d.replace(tzinfo=tz), it takes your datetime object to represent the time in the pre-1912 timezone. This is probably not what you meant. Then when you call dt.normalize it converts this to the timezone that is correct for that datetime value, ie the post-1912 timezone.
I realize I'm a little late on this...
but here is what I found to work well.
Work in UTC as Alex stated:
tz = pytz.timezone('Africa/Abidjan')
now = datetime.datetime.utcnow()
Then to localize:
tzoffset = tz.utcoffset(now)
mynow = now+tzoffset
And this method does handle DST perfectly