In Python, how is `datetime.datetime.now()` an "unaware" datetime object? - python

Apparently, I must run pytz.UTC.localize(datetime.datetime.now()) before I compare the current time with other "naive" time objects.
Why is that? Isn't the current time relevant to specific time zones?

>>> datetime.datetime.now().utcoffset() is None
True
The now function does indeed return a naive object. It contains field values for day and time that are populated according to locale settings. That is why a separate utcnow is provided; that always gives you UTC field values (though it is still naive).

Related

checking when a command was last used

I have a discord bot which has a command that requires a cooldown of a week. I considered using something like #commands.cooldown, but the issue is that if the bot goes offline, that cooldown resets itself. So, a member could simply watch for when the bot goes down and try to exploit it after it returns.
Since I can't keep the bot running 24x7x365, the solution I came up with was to record the date a user last used the command in a database, and when they use the command next, to check if seven days have passed.
For example's sake, this is what the command would look like:
#bot.command()
async def datetest(ctx,date):
today = datetime.date.today()
print(today)
someday = datetime.date.strptime(date,"%y-%m-%d")
diff = someday - today
await ctx.send(diff + "days.")
In this example, I'm using "date" as a parameter to just test if the command returns the difference of days.
(in the actual command, rather than take a date argument, it'll get a date stored in the DB)
However, the issue I'm facing is that:
someday = datetime.date.strptime(date,"%y-%m-%d")
doesn't run. What am I doing wrong here?
1: To format a date it's strftime 'f' for 'format' not strptime, 'parse'. strptime is used to turn a string object into a datetime object.
2: If you are working with the object you don't need to also pass that object to the methods of that class. i.e. you don't need to pass date to the object method 'strftime'
This is because, as with all object methods, it's done internally using the implicit 'self' parameter.
3: To get the actual date object you need to call a method of the datetime class which produces that. e.g. datetime.now()
Your code line should simply read something like:
import datetime.datetime as datetime
# or from datetime import datetime
someday = datetime.now().date().strftime("%y-%m-%d")
or
import datetime
someday = datetime.datetime.now().date().strftime("%y-%m-%d")
(Reading your post not sure what your intent is, so adding this for completeness)
Going the other way, getting a value from your database and using that to generate a datetime.
I don't know what database interface you are using so this is a general answer, as for many Python DB interfaces this is not necessary as the date field select() call will return a type for that field that is a datetime object.
# Paraphrasing a DB interface
dt_col = db.select("select max(date) from lastrun_dates;").execute()
# returns ["2022-10-3",]
date = datetime.datetime.strptime(dt_col[0], "%Y-m-%d")
# note the uppercase 'Y' for a 4 digit date
However, don't do this!
I don't recommend saving dates/times in a DB (or anywhere else for that matter) as a string as it has a DB schema, geographical, localle and daylight saving interpretation, which results in many ways of creating future issues. Instead it is recommended to use the UTC datetime from the epoch as an integer or float.
datetime.datetime.utcnow().timestamp()
# or
datetime.datetime.utcnow().toordinal()
Ok, so I figured out the issue after digging deeper into how datetime subtractions work.
One is that I needed to use someday = datetime.datetime.strptime().
Two is that I needed to use %Y instead of %y to match the time format passed into the date parameter (yyyy-mm-dd). Console pointed this out after a few tests.
Third is that I needed to extract the number of days out of the timedelta answer, because in the end, I'm trying to see how many days passed from the time the command was last used.
So, the new code looks like:
someday = datetime.datetime.strptime(date, "%Y-%m-%d")
diff = someday - today
print(diff.days)

python datetime and pandas to_datetime have different output when covering same timestamp

When I try to convert timestamp, I used 2 method and their outcomes are different from each other
Method 1:
pd.to_datetime(1523808011.979,unit='s') # I got Timestamp('2018-04-15 16:00:11.979000')
Method 2:
yyy=dt.datetime.fromtimestamp(1523808011.979).isoformat() # I got '2018-04-16T00:00:11.979000'
Could you please help me to understand why?
Best regards,
YX
The fromtimestamp returns the local time so if you're in a timezone that isn't UTC, then you'll get a different result.
You should use utcfromtimestamp instead.
>>> from datetime import datetime
>>> datetime.utcfromtimestamp(1523808011.979).isoformat()
'2018-04-15T16:00:11.979000'
In repl which internal time is UTC the result is the same: https://repl.it/repls/IndolentBurlywoodAddons
On my local machine (UTC+2) two values differ exactly for two hours. So looks like the explanation is the timezone.

Create timezone-aware ISODate with pymongo

I'm looking for a way to store a timezone-aware datetime object in mongoDB.
I read here: Create an ISODate with pyMongo that storing a datetime object in MongoDB with pymongo will result in a ISODate object being stored (or an object that looks like this when querying from the shell)
Using the datetime.replace(tzinfo=[...]) method, there is a way to make the datetime object aware of its timezone. Unfortunately, when storing such object in mongoDB, the timezone information seems to be lost.
My question is: is there a way to store the timezone in the ISODate object itself, so that it looks like something like this: ISODate("2012-07-14T01:00:00+01:00") when querying the shell and so that the datetime object is still timezone aware when reloaded with pymongo later on?
Thanks a lot for your help!
No, there's not a way to do that. MongoDB's ISODate is just a wrapper around a Date object, which is just a moment in time represented by an integer corresponding to the number of milliseconds elapsed since Jan 1, 1970 UTC. There is no way for it to retain an offset.
You might consider storing it just as a string, perhaps in a secondary field so you can still query for that moment in time while still knowing the local time and offset.
Also, assuming you're using tzdb time zones from either pytz or dateutil, such as "Europe/London", you should recognize that there's no way to determine a time zone from just an offset. So if you really need to reconstruct a full tz aware datetime, then you'll also have to store the time zone identifier in a separate field.
from datetime import datetime
from dateutil.tz import *
def to_utc(date_time):
return date_time.replace(tzinfo=tzlocal()).astimezone(tzoffset(None, 0))

Strange behavior of datetime.replace and pytz [duplicate]

This question already has answers here:
How to make a timezone aware datetime object
(15 answers)
Closed 6 years ago.
I've got a datetime which has no timezone information. I'm now getting the timezone info and would like to add the timezone into the existed datetime instance, how can I do?
d = datetime.datetime.now()
tz = pytz.timezone('Asia/Taipei')
How to add the timezone info tz into datetime a
Use tz.localize(d) to localize the instance. From the documentation:
The first is to use the localize() method provided by the pytz library. This is used to localize a naive datetime (datetime with no timezone information):
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500
If you don't use tz.localize(), but use datetime.replace(), chances are that a historical offset is used instead; tz.localize() will pick the right offset in effect for the given date. The US Eastern timezone DST start and end dates have changed over time, for example.
When you try to localize a datetime value that is ambiguous because it straddles the transition period from summer to winter time or vice-versa, the timezone will be consulted to see if the resulting datetime object should have .dst() return True or False. You can override the default for the timezone with the is_dst keyword argument for .localize():
dt = tz.localize(naive, is_dst=True)
or even switch off the choice altogether by setting is_dst=None. In that case, or in the rare cases there is no default set for a timezone, an ambiguous datetime value would lead to a AmbiguousTimeError exception being raised. The is_dst flag is only consulted for datetime values that are ambiguous and is ignored otherwise.
To go back the other way, turn a timezone-aware object back to a naive object, use .replace(tzinfo=None):
naivedt = awaredt.replace(tzinfo=None)
If you know that your original datetime was "measured" in the time zone you are trying to add to it, you could (but probably shouldn't) use replace rather than localize.
# d = datetime.datetime.now()
# tz = pytz.timezone('Asia/Taipei')
d = d.replace(tzinfo=tz)
I can imagine 2 times when this might make sense (the second one happened to me):
Your server locale is set to the incorrect time zone and you are trying to correct a datetime instance by making it aware of this incorrect timezone (and presumably later localizing it to the "correct" time zone so the values of now() match up to other times you are comparing it to (your watch, perhaps)
You want to "tag" a time instance (NOT a datetime) with a time zone (tzinfo) attribute so that attribute can be used later to form a full datetime instance.

How to add timezone into a naive datetime instance in python [duplicate]

This question already has answers here:
How to make a timezone aware datetime object
(15 answers)
Closed 6 years ago.
I've got a datetime which has no timezone information. I'm now getting the timezone info and would like to add the timezone into the existed datetime instance, how can I do?
d = datetime.datetime.now()
tz = pytz.timezone('Asia/Taipei')
How to add the timezone info tz into datetime a
Use tz.localize(d) to localize the instance. From the documentation:
The first is to use the localize() method provided by the pytz library. This is used to localize a naive datetime (datetime with no timezone information):
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500
If you don't use tz.localize(), but use datetime.replace(), chances are that a historical offset is used instead; tz.localize() will pick the right offset in effect for the given date. The US Eastern timezone DST start and end dates have changed over time, for example.
When you try to localize a datetime value that is ambiguous because it straddles the transition period from summer to winter time or vice-versa, the timezone will be consulted to see if the resulting datetime object should have .dst() return True or False. You can override the default for the timezone with the is_dst keyword argument for .localize():
dt = tz.localize(naive, is_dst=True)
or even switch off the choice altogether by setting is_dst=None. In that case, or in the rare cases there is no default set for a timezone, an ambiguous datetime value would lead to a AmbiguousTimeError exception being raised. The is_dst flag is only consulted for datetime values that are ambiguous and is ignored otherwise.
To go back the other way, turn a timezone-aware object back to a naive object, use .replace(tzinfo=None):
naivedt = awaredt.replace(tzinfo=None)
If you know that your original datetime was "measured" in the time zone you are trying to add to it, you could (but probably shouldn't) use replace rather than localize.
# d = datetime.datetime.now()
# tz = pytz.timezone('Asia/Taipei')
d = d.replace(tzinfo=tz)
I can imagine 2 times when this might make sense (the second one happened to me):
Your server locale is set to the incorrect time zone and you are trying to correct a datetime instance by making it aware of this incorrect timezone (and presumably later localizing it to the "correct" time zone so the values of now() match up to other times you are comparing it to (your watch, perhaps)
You want to "tag" a time instance (NOT a datetime) with a time zone (tzinfo) attribute so that attribute can be used later to form a full datetime instance.

Categories