mirror of
https://github.com/coleifer/peewee.git
synced 2026-05-06 07:56:41 -04:00
Use faster and more robust fromisoformat when possible.
This commit is contained in:
@@ -3192,8 +3192,16 @@ Fields
|
|||||||
|
|
||||||
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
|
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
|
||||||
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
|
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
|
||||||
|
'%Y-%m-%d %H:%M:%S.%f%z' # ...with timezone offset
|
||||||
|
'%Y-%m-%d %H:%M:%S%z' # ...with timezone offset
|
||||||
'%Y-%m-%d' # year-month-day
|
'%Y-%m-%d' # year-month-day
|
||||||
|
|
||||||
|
In addition, any string accepted by
|
||||||
|
:py:meth:`datetime.datetime.fromisoformat` is parsed automatically,
|
||||||
|
including the ``T`` separator and a trailing ``Z`` (UTC). Custom
|
||||||
|
``formats`` are still consulted as a fallback for non-ISO inputs (e.g.
|
||||||
|
``'01/02/2003 01:37 PM'``).
|
||||||
|
|
||||||
SQLite does not have a native datetime data-type, so datetimes are
|
SQLite does not have a native datetime data-type, so datetimes are
|
||||||
stored as strings. This is handled transparently by Peewee, but if you
|
stored as strings. This is handled transparently by Peewee, but if you
|
||||||
have pre-existing data you should ensure it is stored as
|
have pre-existing data you should ensure it is stored as
|
||||||
@@ -3270,6 +3278,9 @@ Fields
|
|||||||
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
|
'%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second
|
||||||
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
|
'%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond
|
||||||
|
|
||||||
|
In addition, any string accepted by
|
||||||
|
:py:meth:`datetime.datetime.fromisoformat` is parsed automatically.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If the incoming value does not match a format, it is returned as-is.
|
If the incoming value does not match a format, it is returned as-is.
|
||||||
|
|
||||||
|
|||||||
@@ -5472,8 +5472,23 @@ def _date_part(date_part):
|
|||||||
return self.model._meta.database.extract_date(date_part, self)
|
return self.model._meta.database.extract_date(date_part, self)
|
||||||
return dec
|
return dec
|
||||||
|
|
||||||
|
# fromisoformat() is C-implemented and ~10x faster than strptime. Available
|
||||||
|
# since 3.7; pre-3.11 is strict about the separator and rejects 'Z'.
|
||||||
|
_fromisoformat = getattr(datetime.datetime, 'fromisoformat', None)
|
||||||
|
|
||||||
|
|
||||||
def format_date_time(value, formats, post_process=None):
|
def format_date_time(value, formats, post_process=None):
|
||||||
post_process = post_process or (lambda x: x)
|
post_process = post_process or (lambda x: x)
|
||||||
|
if _fromisoformat is not None and value:
|
||||||
|
s = value
|
||||||
|
if len(s) > 10 and s[10] == ' ':
|
||||||
|
s = s[:10] + 'T' + s[11:]
|
||||||
|
if s[-1:] == 'Z':
|
||||||
|
s = s[:-1] + '+00:00'
|
||||||
|
try:
|
||||||
|
return post_process(_fromisoformat(s))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
pass
|
||||||
for fmt in formats:
|
for fmt in formats:
|
||||||
try:
|
try:
|
||||||
return post_process(datetime.datetime.strptime(value, fmt))
|
return post_process(datetime.datetime.strptime(value, fmt))
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ from decimal import ROUND_UP
|
|||||||
|
|
||||||
from peewee import NodeList
|
from peewee import NodeList
|
||||||
from peewee import VirtualField
|
from peewee import VirtualField
|
||||||
|
from peewee import format_date_time
|
||||||
from peewee import *
|
from peewee import *
|
||||||
|
|
||||||
from playhouse.hybrid import *
|
from playhouse.hybrid import *
|
||||||
@@ -404,6 +405,38 @@ class TestDateFields(ModelTestCase):
|
|||||||
datetime.datetime(2002, 3, 1, 0, 0, 0),
|
datetime.datetime(2002, 3, 1, 0, 0, 0),
|
||||||
datetime.datetime(2002, 3, 4, 0, 0, 0)])
|
datetime.datetime(2002, 3, 4, 0, 0, 0)])
|
||||||
|
|
||||||
|
def test_date_time_iso_fast_path(self):
|
||||||
|
dm = DateModel.create(date_time='2019-01-02 03:04:05.123456')
|
||||||
|
dm_db = DateModel[dm.id]
|
||||||
|
self.assertEqual(dm_db.date_time,
|
||||||
|
datetime.datetime(2019, 1, 2, 3, 4, 5, 123456))
|
||||||
|
|
||||||
|
dm = DateModel.create(date_time='2019-01-02T03:04:05')
|
||||||
|
dm_db = DateModel[dm.id]
|
||||||
|
self.assertEqual(dm_db.date_time,
|
||||||
|
datetime.datetime(2019, 1, 2, 3, 4, 5))
|
||||||
|
|
||||||
|
val = format_date_time('2019-01-02T03:04:05Z',
|
||||||
|
DateTimeField.formats)
|
||||||
|
self.assertEqual(val,
|
||||||
|
datetime.datetime(2019, 1, 2, 3, 4, 5,
|
||||||
|
tzinfo=datetime.timezone.utc))
|
||||||
|
|
||||||
|
val = format_date_time('2019-01-02', DateField.formats,
|
||||||
|
lambda x: x.date())
|
||||||
|
self.assertEqual(val, datetime.date(2019, 1, 2))
|
||||||
|
|
||||||
|
def test_date_time_format_fallback(self):
|
||||||
|
val = format_date_time('01/02/2003 01:37 PM',
|
||||||
|
['%m/%d/%Y %I:%M %p'])
|
||||||
|
self.assertEqual(val, datetime.datetime(2003, 1, 2, 13, 37))
|
||||||
|
|
||||||
|
val = format_date_time('11:12:13', TimeField.formats,
|
||||||
|
lambda x: x.time())
|
||||||
|
self.assertEqual(val, datetime.time(11, 12, 13))
|
||||||
|
|
||||||
|
self.assertEqual(format_date_time('not a date', []), 'not a date')
|
||||||
|
|
||||||
def test_to_timestamp(self):
|
def test_to_timestamp(self):
|
||||||
dt = datetime.datetime(2019, 1, 2, 3, 4, 5)
|
dt = datetime.datetime(2019, 1, 2, 3, 4, 5)
|
||||||
ts = calendar.timegm(dt.utctimetuple())
|
ts = calendar.timegm(dt.utctimetuple())
|
||||||
|
|||||||
Reference in New Issue
Block a user