我一直在阅读有关在Python中处理日期时间以及将它们存储到postgresql中的最佳实践(尽可能使用utc,使用pytz进行转换,避免使用datetime构造函数中的tzinfo参数等)。
但我现在的疑问是,我很惊讶没有找到关于datetime.time对象及其最佳实践的任何信息。
举例来说,想象一下我只想存储一个时间,比如20:30,因为我在那个时间每周安排一些任务,但是一周的这一天可能每周都会改变。并且可能用户已经在他/她的时区输入了时间。在我的情况下,它将成为西班牙时区“欧洲/马德里”的用户。
我的问题是:
datetime.time(h, m, s, ms, tzinfo=pytz_spanish_timezone)
???my_datetime.localize(pytz_spanish_timezone)
new_tz_datetime = my_datetime.astimezone(pytz_spanish_timezone)
但随着时间的推移,没有类似的方法2)[H]我是否正确地定位了一个天真的时间?
datetime.datetime
使用my_datetime.localize(pytz_spanish_timezone)
实际上,它是另一种方式。 localize
是一个pytz时区方法,而不是datetime
方法:
import pytz
madrid = pytz.timezone('Europe/Madrid')
aware_datetime = madrid.localize(naive_datetime)
你需要一个datetime.datetime
。 datetime.time
对象没有等价物。请参阅下面的原因。
3)如何将一个
datetime.time
对象从时区转换为另一个?
考虑以下情况:我们知道时间是20:30,时区是Europe/Madrid
,我们希望将其转换为UTC。根据日期是否在夏令时(CEST)内(CET),结果会有所不同:例如,
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
utc = pytz.utc
CET_date = madrid.localize(DT.datetime(2019, 3, 30, 20, 30, 0), is_dst=None)
# the most recent transition occurred at `2019-03-31 02:00:00+01:00 CEST`
CEST_date = madrid.localize(DT.datetime(2019, 3, 31, 20, 30, 0), is_dst=None)
print(CET_date.astimezone(utc))
print(CEST_date.astimezone(utc))
# 2019-03-30 19:30:00+00:00
# 2019-03-31 18:30:00+00:00
请注意,当日期在CET时,时间20:30被“转换”为19:30,但是当日期在CEST时,时间将转换为18:30。在没有事先知道日期的情况下,您的问题没有(简单)答案。
4a)我应该如何将
datetime.time
存储在postgresql数据库中?我知道有时间和时间数据类型。
对于the docs:
类型
time with time zone
由SQL标准定义,但该定义显示的属性导致可疑的有用性。
我认为文档暗指上面显示的问题。不要使用time with
time zone
。如果要存储时间,请使用PostgreSQL plain time
类型。
您可以将time
和timezone
存储在数据库中,并在您拥有日期后重新构建时区感知日期时间。但请注意,有一些陷阱:
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
date = madrid.localize(DT.datetime(2019, 10, 27, 2, 0, 0), is_dst=None)
引发pytz.exceptions.AmbiguousTimeError: 2019-10-27 02:00:00
。为避免AmbiguousTimeError
,必须明确指定is_dst
:
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
date = madrid.localize(DT.datetime(2019, 10, 27, 2, 0, 0), is_dst=False)
print(date)
date = madrid.localize(DT.datetime(2019, 10, 27, 2, 0, 0), is_dst=True)
print(date)
# 2019-10-27 02:00:00+01:00
# 2019-10-27 02:00:00+02:00
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
madrid.localize(DT.datetime(2019, 3, 31, 2, 0, 0), is_dst=None)
引发pytz.exceptions.NonExistentTimeError: 2019-03-31 02:00:00
您可以通过指定天真本地时间是否指向DST(夏令时)期间的时间来避免NonExistentTimeError:
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
date = madrid.normalize(madrid.localize(DT.datetime(2019, 3, 31, 2, 0, 0), is_dst=False))
print(date)
date = madrid.normalize(madrid.localize(DT.datetime(2019, 3, 31, 2, 0, 0), is_dst=True))
print(date)
# 2019-03-31 03:00:00+02:00
# 2019-03-31 01:00:00+01:00
AmbiguousTimeError
和NonExistentTimeError
显示了指定is_dst
值的重要性。要避免这些错误,您需要在数据库中存储boolean is_dst
以及time
和timezone
。
您可能认为只需选择一个is_dst
值就可以避免这个问题。但你错了。这是一个特殊的例子(取自the pytz docs),显示如果你总是选择is_dst = False
(或is_dst = True
),可以有UTC日期时间,只有一个天真的本地时间和时区,无法表达!
import datetime as DT
import pytz
warsaw = pytz.timezone('Europe/Warsaw')
utc = pytz.utc
date1 = warsaw.localize(DT.datetime(1915, 8, 4, 23, 35, 59), is_dst=False).astimezone(utc)
date2 = warsaw.localize(DT.datetime(1915, 8, 4, 23, 36, 0), is_dst=False).astimezone(utc)
print('Datetimes between {} and {} can not be expressed if we assume is_dist=False.'.format(date1, date2))
date3 = warsaw.localize(DT.datetime(1915, 8, 4, 23, 59, 59), is_dst=True).astimezone(utc)
date4 = warsaw.localize(DT.datetime(1915, 8, 5, 0, 0, 0), is_dst=True).astimezone(utc)
print('Datetimes between {} and {} can not be expressed if we assume is_dist=True.'.format(date1, date2))
版画
Datetimes between 1915-08-04 22:11:59+00:00 and 1915-08-04 22:36:00+00:00 can not be expressed if we assume is_dist=False.
Datetimes between 1915-08-04 22:11:59+00:00 and 1915-08-04 22:36:00+00:00 can not be expressed if we assume is_dist=True.
4b)我想我应该把时间存储为UTC。时区重要吗?我应该以某种方式存储它吗?
由于上面显示的原因,UTC中没有时间(没有日期)。但是,您可以通过简化以UTC格式存储日期时间来避免上述问题。
如果使用timestamptz
数据类型的列创建表,则可以使用数据库适配器(如psycopg2
)将Python时区感知日期时间存储为PostgreSQL timestamptz
s。当您查询数据库时,psycopg2
会将timestamptz
s转换回时区感知日期时间。
在内部,PostgreSQL以UTC格式存储所有timestamptz
s,但它报告了与PostgreSQL用户的时区设置有关的值。在Python方面,给定时区感知日期时间,您可以使用其astimezone
方法将其转换为您喜欢的任何时区。
您不需要单独存储时区,除非您想要针对不同的时区报告不同的日期时间。
5)如何从字符串中解析时间而不经过日期时间?
您可以使用regex来解析时间字符串:
import re
import datetime as DT
atime = DT.time(*map(int, re.search(r'(\d{,2}):(\d{,2}):(\d{,2})', 'blueberry jam at 13:32:02').groups()))
print(repr(atime))
# datetime.time(13, 32, 2)
上面,正则表达式模式\d
匹配一个数字。 \d{1,2}
匹配1或2位数。
或者,第三方dateutil package可以解析各种格式的时间字符串:
import dateutil.parser as DP
print(DP.parse("13:32:02").time())
# 13:32:02
print(DP.parse("blueberry jam at 13:32:02", fuzzy=True).time())
# 13:32:02
print(DP.parse("30 minutes 12 hours").time())
# 12:30:00
print(DP.parse("2:30pm").time())
# 14:30:00
这里有很多要消化的内容,关于这些问题可能还有很多可以说的。将来,您可能希望将帖子分成多个问题。这将降低那些可能希望回答一个问题但不能回答所有问题的人的障碍,并将帮助您更快地获得更多答案。