I am making a news blog for my project in which I have to display time under the news title as x minutes ago etc.
From the RSS feed of the news, I have the time stamp in the form of a string. For example:
timestamp = 'Tue, 12 Feb 2013 07:43:09 GMT'
I am trying to find the difference between this timestamp and the present time using datetime module in python. But for some reason it gives error:
ValueError: astimezone() cannot be applied to a naive datetime
I'll appreciate if somebody can point me in the right direction. Below is my attempt in Python:
from datetime import datetime
from pytz import timezone
timestamp = 'Tue, 12 Feb 2013 07:43:09 GMT'
t = datetime.strptime(timestamp, '%a, %d %b %Y %I:%M:%S %Z')
now_time = datetime.now(timezone('US/Pacific'))
# converting the timestamp to Pacific time
t_pacific = t.astimezone(timezone('US/Pacific')) # get error here
diff = t_pacific - t
Thanks!
Prakhar
Your example had a few problems (I see you've fixed them now):
First line should be:
from datetime import datetime
It looks as though you're missing a closing parenthesis on the 4th line:
now_time = datetime.now(timezone('US/Pacific')
What is timezone()? Where does that come from?
You don't really need to mess with timezones, I don't think — just use GMT (UTC). How about something more like this:
from datetime import datetime
timestamp = 'Tue, 12 Feb 2013 07:43:09 GMT'
t = datetime.strptime(timestamp, '%a, %d %b %Y %I:%M:%S %Z')
t_now = datetime.utcnow()
diff = t_now - t
The problem here is that t does not have a timezone—that's what the error message means by "naive datetime". From the docs:
There are two kinds of date and time objects: “naive” and “aware”… An aware object has sufficient knowledge of… time zone and daylight savings time information… A naive object does not…
You can verify that it's naive by doing this:
print(t.tzinfo)
The answer will be None.
As the astimezone docs say:
self must be aware (self.tzinfo must not be None, and self.utcoffset() must not return None).
The strptime function always generates a naive datetime.
You can fix this in various ways:
First convert t to a GMT datetime instead of a naive one, and then your conversion to 'US/Pacific' will work.
As the docs say, "If you merely want to attach a time zone object tz to a datetime dt without adjustment of date and time data, use dt.replace(tzinfo=tz)." Since you know the time is in UTC, just replace the empty tz with UTC, and you've got an aware time.
Convert to PST with a different mechanism than astimezone, on which will assume UTC or which allows you to specify the source.
There are various alternatives out there, but you're already using pytz, so see its documentation.
Convert now_time to UTC instead of converting t to PST.
The last one is probably the simplest and best for most use cases. Since you've only got now_time in PST because you explicitly asked for it that way, all you have to do is not do that (or explicitly ask for 'GMT' instead of 'US/Pacific'). Then you can just do your date arithmetic on UTC times.
If you need to display final results in PST, it's still often better to do the arithmetic in UTC, and convert at the end. (For example, you can have two times that are an hour apart, but have the same value in Pacific, because of 1am on DST day being repeated twice; that won't be an issue if you stay in UTC all the time.)
Related
I get datetime object from email message and then I try to compare it with datetime.now().
And then I see this error:
datetime.now() > datetime.strptime('Fri, 31 Jan 2020 09:59:34 +0000 (UTC)', "%a, %d %b %Y %H:%M:%S %z (%Z)"
TypeError: can't compare offset-naive and offset-aware datetimes
How to solve it?
In many cases, you don't want to have to convert any time zone information. To prevent this, just convert the datetime objects to floats on both sides of the comparator. Use the datetime.timestamp() function.
I also suggest you simplify your date parsing with dateutil.parser.parse(). It's easier to read.
In your example, you might compare your datas like this:
compare_date = 'Fri, 31 Jan 2020 09:59:34 +0000 (UTC)'
datetime.now().timestamp() > parse(compare_date).timestamp()
This will happen any time you compare an offset-naive (datetime.now() No Timezone info) to an offset-aware (UTC) time. Python has poor timezone support by default. Even if you used datetime.utcnow() to compare this technically just returns you the UTC time but still has a naive timezone.
My suggestion is to install the pytz package and do:
import pytz
datetime.now().replace(tzinfo=pytz.UTC) > \
datetime.strptime('Fri, 31 Jan 2020 09:59:34 +0000 (UTC)',
"%a, %d %b %Y %H:%M:%S %z (%Z)")
For further reference see: https://docs.python.org/3/library/datetime.html#datetime.datetime.utcnow
As said in the datetime documentation, date and time objects are categorized as “aware” or “naive” depending on whether or not they include time zone information.
To update your datetime.datetime.now() from naive to aware you can use datetime.timezone.utc.
I also recommend you to use dateutil.parser to parse your generic date/time string to a datetime.datetime object.
Here an example of what you could do:
from datetime import datetime, timezone
from dateutil import parser
datetime.now(timezone.utc) > parser.parse('Fri, 31 Jan 2020 09:59:34 +0000 (UTC)')
See the datetime.timezone documentation and the dateutil.parser documentation for more information.
You can do like below
print(datetime.today().timestamp() < object.datetime.timestamp())
or
object.datetime.timestamp()) > datetime.today().timestamp()
naive_dt = datetime.datetime.utcnow()
aware_dt = datetime.datetime.now(datetime.timezone.utc)
You can also (ab)use the isoformat() and fromisoformat() to add your own offset:
aware_dt = datetime.datetime.fromisoformat(datetime.datetime.now().isoformat()+'+20:00')
I've got a datetime as a string (2016-12-31 23:59), from which I know it was defined in UTC. I now want to get the timedelta using Python. The machine I'm doing this on will normally be in the CEST time zone (Europe/Amsterdam), but this might not always be the case and therefore I don't want to hardcode this time zone in the code.
For local timezones this would work great:
date_str = '2016-12-31 23:59'
print datetime.now() - datetime.strptime(date_str, '%Y-%m-%d %H:%M')
this obviously isn't correct though, but changing it to use utcnow() is also not correct:
print datetime.utcnow() - datetime.strptime(date_str, '%Y-%m-%d %H:%M')
This doesn't work correctly because now the datetime resulting from strptime is in the timezone of the machine this code is running on, while the utcnow() is in UTC.
I found this SO answer, but that assumes you know the timezone of the machine the code is running on, which I don't.
Is there a way that I can add the UTC timezone to the datetime object which I get from datetime.strptime() so that I can compare it with utcnow()? All tips are welcome
Your code is actually fine. Your mistake is in this assumption: "the datetime resulting from strptime is in the timezone of the machine this code is running on."
datetime.strptime(date_str, '%Y-%m-%d %H:%M') will produce a naive datetime representing a time that you said was in UTC.
datetime.utcnow() will produce a naive datetime representing the current time in UTC.
So the difference will be accurate.
I have many strings of dates and times (or both), like these:
'Thu Jun 18 19:30:21 2015'
'21:07:52'
I want to convert these times to the proper datetime format while also changing the timezone to UTC. The current timezone is 4 hours behind UTC. Is there a way that I can tell python to add 4 hours while converting the formats? Can it also take care of the date in UTC such that when the hour goes past 24 the date changes and time resets?
I will ultimately be inserting these into a mysql table into fields with the 'datetime' and 'time' data type, but they all need to be in UTC.
I would approach this with time.strptime() to parse the source time string, time.mktime() to convert the resulting time vector into an epoch time (seconds since 1970-01-01 00:00:00), and time.strftime() to format the time as you like.
For the timezone adjustment, you could add 4*3600 to the epoch time value or, more generally, append a timezone string to the source and use %Z to parse it.
Usually what I do if I want to convert time zone is the following
# local interpreted as pst to utc
utc = pytz.utc
pst = pytz.timezone('America/Los_Angeles')
start_time_str = time.strftime('%Y-%m-%d %H:%M:%S', \
time.localtime(start_time))
start_time_datetime = \
datetime.datetime.strptime(start_time_str, "%Y-%m-%d %H:%M:%S")
start_time_datetime = \
start_time_datetime.replace(tzinfo=pst).astimezone(utc)
Now I want to do similar stuff such that I want localtime convert to pst
localtime = datetime.datetime.fromtimestamp(time.mktime(
time.localtime()))
I am not exactly sure how would you achieve this
Any help would be appreciated
Two things:
The "America/Los_Angeles" identifier represents the entire Pacific time zone, including both PST and PDT. It will use the either -8 or -7 as the time zone offset depending on the specific date and time it is used on.
Calling replace(tzinfo=...) is a mistake. Use localize instead. See the pytz documentation. This is discussed in the introduction and in the very first example.
Your code contains unnecessary (calling strptime after strftime) or just wrong (.replace()) conversions.
To create an aware datetime object given "seconds since epoch" start_time (a value returned by time.time()):
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz
tz = pytz.timezone('America/Los_Angeles')
start_time_datetime = datetime.fromtimestamp(start_time, tz)
As #Matt Johnson mentions, 'America/Los_Angeles' timezone id produces time with PST or PDT tzname depending on the date. No conversion is necessary.
The last code example in your question has both unnecessary conversions and may fail in some cases. If the intent is to get the current time in the local timezone as an aware datetime object then you could use tzlocal module:
#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # $ pip install tzlocal
local_timezone = get_localzone() # pytz timezone corresponding to the local time
current_localtime = datetime.now(local_timezone)
First, as a note, local time is either Standard Time or Daylight Savings Time -- it cannot be both simultaneously. I believe what you are really getting at is asking whether there is a way to tell if a time is either a Daylight Savings Time or not. In other words, given a specific day, is a particular time in Daylight Savings Time? Another common use case is converting tabular data pegged at a specific time offset to the current correct time.
Okay, I can think of three ways to do this.
(1) You can convert the datetime into a time object and look at the .tm_isdst property, similar to this answer. You can do a test on it to see if that property equals one and then subtract a timedelta of one hour.
>>> time.localtime()
time.struct_time(tm_year=2019, tm_mon=11, tm_mday=16, tm_hour=12,
tm_min=36, tm_sec=32, tm_wday=5, tm_yday=320, tm_isdst=0)
>>> time.localtime().tm_isdst
0
(2) Second way is to crawl month by month over noon on the fifteenth of each month and check if the offset varies. (Note that some areas do not implement DST.) The greater value indicates a skip ahead for Daylight Savings Time. (For instance, PDT is UTC -7 while PST is UTC -8.) From those values you can calculate the time difference. Then you can test your time by checking the offset to see if it is in Standard Time or Daylight Savings Time. Offest is checked this way:
>>> my_special_pdt_datetime.strftime('%z')
'-0700'
(3) And, maybe the easiest, is to calculate the existing offset by getting offset = datetime.datetime.utcnow() - datetime.datetime.now() and then comparing that to the known offset from pytz:
>>> pytz.timezone('US/Pacific').localize(datetime.datetime(2019,1,1)).strftime('%z')
'-0800'
I came across this exact issue, and I can't figure out how to achieve the solution in my case.
Guido says
The solution is to remove the tzinfo completely from the time after
converting to UTC.
This is what I have tried:
date_time = parser.parse(i.pubDate.text)
news.publication_date = date_time.replace(tzinfo=None).date()
And I get the same error:
NotImplementedError: DatetimeProperty publication_date_time can only support UTC. Please derive a new Property to support alternative timezones.
So it seems I have to convert the date to UTC first. And here my research has failed me.
I came across this solution:
The solution suggested is this:
def date_time_to_utc(date_time):
tz = pytz.timezone('???')
return tz.normalize(tz.localize(date_time)).astimezone(pytz.utc)
But I don't have the timezone. I am scraping the date from a html source. So the timezone could really be from anywhere in the world. Is there no easy and reliable way to convert a date time to UTC?
I could use both dateutil and pytz to achieve this. Many Thanks.
UPDATE
It has been a really long day. I have misread the stack trace. However the question remains valid.
date_time = (datetime}2015-01-13 18:13:26+00:00
news.publication_date_time = date_time
This caused the crash. And it seems by doing this, I pass the unit test:
news.publication_date_time = date_time.replace(tzinfo=None)
Is this the correct way converting a GMT 0 datetime to UTC datetime? Or in fact any timezone to UTC?
Is this the correct way converting a GMT 0 datetime to UTC datetime? Or in fact any timezone to UTC?
If aware datetime object is already in UTC (+0000) then your formula works:
naive_utc = aware_utc.replace(tzinfo=None)
where aware_utc is a timezone-aware datetime object that represents time in UTC.
But if aware datetime object is not in UTC; it fails. You should take into account a (possibly) non-zero UTC offset in the general case:
assert aware.tzinfo is not None and aware.utcoffset() is not None
# local time = utc time + utc offset (by definition)
# -> utc = local - offset
naive_utc = aware.replace(tzinfo=None) - aware.utcoffset()
where aware is a timezone-aware datetime object in an arbitrary timezone.
But I don't have the timezone. I am scraping the date from a html
source. So the timezone could really be from anywhere in the world. Is
there no easy and reliable way to convert a date time to UTC? I could
use both dateutil and pytz to achieve this. Many Thanks.
No. dateutil, pytz won't help you unless the date string itself contains the timezone (or at least its utc offset).
Remember: It is always noon somewhere on Earth i.e., if you collect date/time strings from different places on Earth then you can't compare them unless you attach the corresponding timezones. You can't convert it to UTC, you can't get a valid POSIX timestamp if you don't know the source timezone for the date.
I'm an idiot and it's late here, this time I read the question.
tstmp= date_time.replace(tzinfo=utc).total_seconds()
naive_date = datetime.utcfromtimestamp(tstmp)
First answer will just give you the current naive time
Try this:
dateTime = dateTime.replace(tzinfo=None)
dtUtcAware = pytz.UTC.localize(dateTime)