我应该如何使用python3在postgresql中存储带时区的时间?

问题描述 投票:0回答:1

我一直在阅读有关在Python中处理日期时间以及将它们存储到postgresql中的最佳实践(尽可能使用utc,使用pytz进行转换,避免使用datetime构造函数中的tzinfo参数等)。

但我现在的疑问是,我很惊讶没有找到关于datetime.time对象及其最佳实践的任何信息。

举例来说,想象一下我只想存储一个时间,比如20:30,因为我在那个时间每周安排一些任务,但是一周的这一天可能每周都会改变。并且可能用户已经在他/她的时区输入了时间。在我的情况下,它将成为西班牙时区“欧洲/马德里”的用户。

我的问题是:

  • 1)一旦我将时间作为datetime.time,我该如何将时区信息存储到datetime.time变量中。可以使用吗? datetime.time(h, m, s, ms, tzinfo=pytz_spanish_timezone) ???
  • 2)如果没有前一行代码,我如何正确地定位一个天真的时间? datetime.datetime使用my_datetime.localize(pytz_spanish_timezone)
  • 3)如何将一个datetime.time对象从一个时区转换为另一个对象,假设使用datetime和pytz它将使用 new_tz_datetime = my_datetime.astimezone(pytz_spanish_timezone) 但随着时间的推移,没有类似的方法
  • 4)我应该如何在postgresql数据库中存储datetime.time?我知道有时间和时间数据类型。我想我应该把时间存储为UTC。时区重要吗?我应该以某种方式存储它吗?
  • 5)如何从字符串中解析时间而不经过日期时间? (我已经使自己成为一个函数,但我确信必须存在一些使用datetime或某些强大模块的方法,这可能涵盖我没有的情况)。
python postgresql datetime time pytz
1个回答
1
投票

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.datetimedatetime.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类型。

您可以将timetimezone存储在数据库中,并在您拥有日期后重新构建时区感知日期时间。但请注意,有一些陷阱:

  1. 本地日期时间不明确 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_dstimport 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
  2. 没有本地日期时间 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
  3. 在给定本地日期时间和特定时区的情况下,可以存在无法代表的日期时间。 上面的AmbiguousTimeErrorNonExistentTimeError显示了指定is_dst值的重要性。要避免这些错误,您需要在数据库中存储boolean is_dst以及timetimezone。 您可能认为只需选择一个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 timestamptzs。当您查询数据库时,psycopg2会将timestamptzs转换回时区感知日期时间。

在内部,PostgreSQL以UTC格式存储所有timestamptzs,但它报告了与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

这里有很多要消化的内容,关于这些问题可能还有很多可以说的。将来,您可能希望将帖子分成多个问题。这将降低那些可能希望回答一个问题但不能回答所有问题的人的障碍,并将帮助您更快地获得更多答案。

© www.soinside.com 2019 - 2024. All rights reserved.