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

时间:2019-03-31 02:11:44

标签: python postgresql datetime time pytz

我一直在阅读有关在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)如何将datetime.time存储在postgresql数据库中?一世 知道有时间和timetz数据类型。我想我应该储存 时间为UTC。时区重要吗?我应该以某种方式存储它吗?

  • 5)如何在不经过日期时间的情况下从字符串中解析时间? (我已经使自己成为一个函数,但是我确定必须存在某种使用日期时间或某些健壮的模块的方法,这些方法可能涵盖了我没有的情况。)

1 个答案:

答案 0 :(得分: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.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数据库中?我知道有时间和timetz数据类型。

the docs

  

time with time zone类型是由SQL标准定义的,但是该定义具有的属性会导致有用的疑问。

我认为文档暗示了上面显示的问题。不要使用time with time zone。如果要存储时间,请使用PostgreSQL纯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_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
    
  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的值的重要性。 为避免这些错误,您需要在数据库中存储布尔值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中没有时间(无日期)。 但是,您可以通过简单地将 datetimes 存储在UTC中来避免上述问题。

如果您创建的表的数据类型为timestamptz,则 您可以使用诸如psycopg2之类的数据库适配器来存储可识别Python时区的日期时间 作为PostgreSQL timestamptz。查询数据库时,psycopg2会将timestamptz转换为 您知道的时区日期时间。

在内部,PostgreSQL在UTC中存储所有timestamptz,但它报告有关 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

这里有很多要消化的东西,可能还有更多要说的 关于这些问题。将来,您可能需要将帖子拆分为 多个问题。这将降低那些希望 回答一个问题,但不能全部回答,将帮助您更快地获得更多答案。