>>> import dateutil.parser, dateutil.tz as tz
>>> dateutil.parser.parse('2017-08-09 10:45 am').replace(tzinfo=tz.gettz('America/New_York'))
datetime.datetime(2017, 8, 9, 10, 45, tzinfo=tzfile('/usr/share/zoneinfo/America/New_York'))
Is that really the way that we're supposed to set a default timezone for parsing? I've read the documentation for the parser and examples but I cannot seem to find anything that says, "This is how to set the default timezone for dateutil.parser.parse", or even anything like it.
Because while this works, there are cases where it would do the wrong thing, if the zone were provided. Does that mean we should do this?
>>> d = dateutil.parser.parse('2017-08-09 10:45 am +06:00')
>>> d = d.replace(tzinfo=d.tzinfo or tz.gettz('America/Chicago'))
Because that's clunky, too.
What's the recommended way to set a default timezone when parsing?
There are basically two "correct" ways to do this. You can see that this was brought up as Issue #94 on dateutil's issue tracker, and "set a default time zone" is determined to be out of scope, since this is something that can be easily done with the information returned by the parser anyway (and thus no need to build it in to the parser itself). The two ways are:
Provide a default date that has a time zone. If you don't care what the default date is, you can just specify some date literal and be done with it. If you want the behavior to be basically the same as dateutil's default behavior (replacing missing elements from "today's date at midnight"), you have to have a bit of boilerplate:
from datetime import datetime, time
from dateutil import tz, parser
default_date = datetime.combine(datetime.now(),
time(0, tzinfo=tz.gettz("America/New_York")))
dt = parser.parse(some_dt_str, default=default_date)
Use your second method with .replace:
from dateutil import parser
def my_parser(*args, default_tzinfo=tz.gettz("America/New_York"), **kwargs):
dt = parser.parse(*args, **kwargs)
return dt.replace(tzinfo=dt.tzinfo or default_tzinfo)
This last one is probably slightly cleaner than the first, but has a slight performance decrease if run in a tight loop (since the first one only needs the default date created once), but dateutil's parser is actually quite slow, so an extra date construction is likely the least of your problems if you're running it in a tight loop.
Fleshing out Paul's comment - because a datetime has to be at least a year, month, and day, dateutil already has a default that it uses:
>>> from datetime import datetime
>>> datetime.now()
datetime.datetime(2017, 10, 13, 15, 16, 13, 548750)
>>> dateutil.parser.parse('2017')
datetime.datetime(2017, 10, 13, 0, 0)
Given this, the appropriate choice would be to create a default that contains the timezone and is either just the current date, or whatever date makes sense:
>>> dateutil.parser.parse('2017', default=datetime(2017, 10, 13, tzinfo=tz.gettz('America/New_York')))
Naturally you can store the default as something sensible, like default_datetime or something, then it becomes:
>>> dateutil.parser.parse('2017', default=default_datetime)
Related
Imagine this situation :
I have a file named settings.py that contains
from datetime import datetime
FOO = datetime.now()
Now, in another file, I do from django.conf import settings (the fact that I'm using Django in this specific situation is irrelevant)
In the file I did the import when I use settings.FOO, no matter how much time has passed I will always get the same datetime value, which corresponds to the time when the import happened.
I understand how and why it works that way. My question is : Is there a way to always have an updated value "stored" in settings.FOO without changing the way I access that value ? (as settings.FOO is used in multiple file across multiple subprojects, I aim to keep that syntax)
In other words, I there a way to make my imported variable transparently call a function ?
As mentioned in the comments, there's almost never a good reason for doing it like this, and you would almost always rely on explicit callables for doing what you do, but by explicitly manipulating sys.modules, if all you care about is syntax, it is possible to do what you're trying to do.
settings.py:
from datetime import datetime
import sys
class _Bar:
#property
def FOO(self):
return datetime.now()
sys.modules[__name__] = _Bar()
Usage example:
>>> import settings
>>> settings.FOO
datetime.datetime(2020, 12, 4, 17, 0, 5, 987423)
>>> settings.FOO
datetime.datetime(2020, 12, 4, 17, 0, 7, 285826)
I can create a timezone specific datetime object like this
import datetime
d = datetime.datetime.now().astimezone()
Result is
datetime.datetime(2018, 4, 2, 15, 12, 2, 807451, tzinfo=datetime.timezone(datetime.timedelta(0, 7200), 'CEST'))
It looks like that tzinfo is represented by two values/attributes: A timedelta and a string. But how can I access them?
I would like to do something like this
d.tzinfo.delta
d.tzinfo.name
I need this informations to be able to (de)serialize the datetime to and from JSON.
I don't want to use third-party packages for such solutions.
tzinfo in this case is an instance of the datetime.timezone() class:
The timezone class is a subclass of tzinfo, each instance of which represents a timezone defined by a fixed offset from UTC.
You can use the tzinfo.utcoffset() and tzinfo.utcname() methods to access the delta and name. For timezone() instances the argument each of these take is ignored, but normally you'd pass in the datetime instance they are attached to:
d.tzinfo.utcoffset(d)
d.tzinfo.utcname(d)
You'd usually call these on the datetime.datetime instance, which has the same methods (but which take no arguments) and these will then handle passing in the right argument to the methods on the contained tzinfo attribute.
Demo:
>>> import datetime
>>> d = datetime.datetime.now().astimezone()
>>> d.utcoffset()
datetime.timedelta(seconds=7200)
>>> d.tzname()
'CEST'
>>> d.tzinfo.utcoffset(d)
datetime.timedelta(seconds=7200)
>>> d.tzinfo.utcoffset(d) is d.utcoffset() # they are the same object
True
The datetime.timezone() subclass is just one implementation of a tzinfo time zone, 3rd-party libraries like pytz offer their own, and the utcoffset() and tzname() return values may well vary for timezones with historical information attached.
You're asking about datetime.tzinfo, which is an abstract base class, as documented here.
datetime comes with an implementation of the tzinfo abstract base class called datetime.timezone, which is documented here.
Just below that last link are the docs on timezone.utcoffset and timezone.utcname, which are ways to access the properties you asked about on the tzinfo if it is a timezone. However, this is not the only implementation of that abstract class. If you are using the pytz timezone, for example, then you'll need to read the docs on that instead.
I'm trying to perform a, to me, simple task of generating a current date/time combo at a speficic time zone. All I see is suggestions to use pytz even though datetime includes the tzinfo class to deal with timezones. However, if I try to use tzinfo, it does not work:
>>> from datetime import datetime, tzinfo
>>> d = datetime.now(tzinfo.tzname("EDT"))
TypeError: descriptor 'tzname' requires a 'datetime.tzinfo' object but received a 'str'
The docs say you can use a time zone name like "EDT" or "GMT". What's wrong with this?
The function tzinfo.tzname does the opposite of what you think it does.
It takes a datetime object and returns a string indicating the time zone.
I am writing an app which depends heavily on dates and times. I want to be able to have an injectable concept of now() and today(). I was thinking that I could write my own versions of these two functions which would check some central setting, to which I will refer to as INJECTED_NOW. If INJECTED_NOW is None, the above functions would just return the values of datetime.datetime.now() and datetime.date.today(). However, if INJECTED_NOW has a datetime value, the above functions would use it to get now() and today().
I am wondering how I could store INJECTED_NOW so that it is mutable. I would like to be able to set it at the beginning of a test case and modify it before another test case. Similarly, I would like to be able to set it from the request, perhaps using middleware.
Does this approach make sense, and if so, how should I store INJECTED_NOW? I would like to avoid a DB access. Is there an alternate way of addressing this problem?
There's a recently released library called FreezeGun that lets specify datetimes like you describe:
http://stevepulec.com/freezegun/
Here is a way to do it using mock, for more information about mock see the docs
# this should be the code your are testing
import datetime
def one_minute_ago():
return (datetime.datetime.now() - datetime.timedelta(seconds=60)).time()
# this would be in your tests file
import mock
import sys
import unittest
class SomeTestcase(unittest.TestCase):
def test_one_minute_ago(self):
real_datetime = datetime.datetime
fake_now = datetime.datetime(2012, 12, 21, 11, 13, 13)
with mock.patch('datetime.datetime', spec=datetime.datetime) as datetime_mock:
datetime_mock.now.return_value = fake_now
self.assertEqual(one_minute_ago(), datetime.time(11, 12, 13))
if __name__ == '__main__':
sys.exit(unittest.main())
To test it just copy the code to a file and run it with Python.
Is there a way I can obtain a datetime aware object out of a string in Python using only the standard library modules ?
I know that I can use dateutil.parser.parse, but unfortunately that's not a good enough reason to add it as a dependency to my project. I already have the mx.DateTime module as a dependency, buuttttt:
>>> dateutil.parser.parse('2011-10-24T06:51:47-07:00')
datetime.datetime(2011, 10, 24, 6, 51, 47, tzinfo=tzoffset(None, -25200))
>>> mx.DateTime.ISO.ParseDateTimeUTC('2011-10-24T06:51:47-07:00')
<mx.DateTime.DateTime object for '2011-10-24 13:51:47.00' at 29c7e48>
the ParseDateTimeUTC fails to detect the offset, even though in its documentation says that:
Returns a DateTime instance in UTC reflecting the given ISO
date. A time part is optional and must be delimited from the
date by a space or 'T'. Timezones are honored.
mx.DateTime.ISO.ParseDateTimeUTC is doing the right thing - it is applying the specified timezone to adjust the time to UTC. The resulting UTC time doesn't have a timezone because it isn't a local time anymore.
The standard Python library doesn't contain any concrete timezone classes according to the documentation:
tzinfo is an abstract base clase, meaning that this class should not
be instantiated directly. You need to derive a concrete subclass, and
(at least) supply implementations of the standard tzinfo methods
needed by the datetime methods you use. The datetime module does not
supply any concrete subclasses of tzinfo.
I've always been surprised that they didn't at least include a class for UTC, or a generic implementation like dateutil's tzoffset.