【问题标题】:How should I store times with timezones in postgresql using python3?我应该如何使用 python3 在 postgresql 中存储带时区的时间?
【发布时间】:2019-08-21 13:37:13
【问题描述】:

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

  • 5) 如何在不经过日期时间的情况下从字符串中解析时间? (我已经为自己创建了一个函数,但我确信一定存在某种使用日期时间的方法或一些可能涵盖我不知道的情况的强大模块)。

【问题讨论】:

  • 也许您可以将日期存储在纪元时间?
  • 那么时区呢?
  • 纪元时间自动为 UTC。见docs.python.org/3/library/time.html
  • 我现在正在阅读,但是,例如,您如何定义 20:30 和 epoch?我在文档中找不到它。
  • 好吧,我已经从自己的经验中学到了不要做(1)。

标签: python postgresql datetime time pytz


【解决方案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 数据库中?我知道有 time 和 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 timestamptzs。当您查询数据库时,psycopg2 会将timestamptzs 转换回 为您提供时区感知的日期时间。

在内部,PostgreSQL 将所有 timestamptzs 存储在 UTC 中,但它会报告与 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

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

【讨论】:

  • 谢谢。我已经接受了你的回答,但我只是想强调一个事实,即在 (5) 我问的是如何解析时间,而不是日期时间。另外,当我说“不经过日期时间”时,我并不是指模块。我指的是不要使用 datetime.datetime 对象而不是 datetime.time 对象。
  • 顺便说一句,答案很好。我很感激。再次感谢。
猜你喜欢
  • 1970-01-01
  • 2021-08-29
  • 2022-11-08
  • 1970-01-01
  • 2015-06-15
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 2018-01-20
相关资源
最近更新 更多