首页 文章

如何在python中识别未知的日期时区时区

提问于
浏览
357

What I need to do

我有一个时区不知道的日期时间对象,我需要添加一个时区,以便能够与其他时区感知日期时间对象进行比较 . 我不想将我的整个应用程序转换为时区而不知道这个遗留案例 .

What I've Tried

首先,要证明问题:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes

首先,我试过astimezone:

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>

它实际上是在尝试进行转换 . 替换似乎是一个更好的选择(根据Python: How to get a value of datetime.today() that is "timezone aware"?):

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>>

但正如你所看到的,replace似乎设置了tzinfo,但没有让对象知道 . 我正准备在解析之前回过头来修改输入字符串以获得时区(我正在使用dateutil进行解析,如果这很重要),但这看起来非常糟糕 .

另外,我在python 2.6和python 2.7中都试过这个,结果相同 .

Context

我正在为一些数据文件编写解析器 . 我需要支持旧格式,其中日期字符串没有时区指示符 . 我已经修复了数据源,但我仍然需要支持旧数据格式 . 对于各种商业BS原因,遗留数据的一次转换不是一种选择 . 虽然一般来说,我不喜欢硬编码默认时区的想法,在这种情况下,它似乎是最好的选择 . 我非常有信心地知道所有遗留数据都是UTC格式的,所以我准备接受在这种情况下违约的风险 .

9 回答

  • 35

    我用过dt_aware到dt_unware

    dt_unaware = dt_aware.replace(tzinfo=None)
    

    和dt_unware到dt_aware

    from pytz import timezone
    localtz = timezone('Europe/Lisbon')
    dt_aware = localtz.localize(dt_unware)
    

    但之前的回答也是一个很好的解决方案 .

  • 8

    所有这些示例都使用外部模块,但您只需使用datetime模块即可获得相同的结果,如this SO answer中所示:

    from datetime import datetime
    from datetime import timezone
    
    dt = datetime.now()
    dt.replace(tzinfo=timezone.utc)
    
    print(dt.replace(tzinfo=timezone.utc).isoformat())
    '2017-01-12T22:11:31+00:00'
    

    依赖性较少,没有pytz问题 .

    注意:如果您希望将它与python3和python2一起使用,您也可以使用它来进行时区导入(硬编码为UTC):

    try:
        from datetime import timezone
        utc = timezone.utc
    except ImportError:
        #Hi there python2 user
        class UTC(tzinfo):
            def utcoffset(self, dt):
                return timedelta(0)
            def tzname(self, dt):
                return "UTC"
            def dst(self, dt):
                return timedelta(0)
        utc = UTC()
    
  • 429

    通常,要使天真的日期时间感知,请使用localize method

    import datetime
    import pytz
    
    unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
    aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)
    
    now_aware = pytz.utc.localize(unaware)
    assert aware == now_aware
    

    对于UTC时区,没有必要使用 localize ,因为没有夏令时计算来处理:

    now_aware = unaware.replace(tzinfo=pytz.UTC)
    

    作品 . ( .replace 返回一个新的日期时间;它不会修改 unaware . )

  • 0

    我同意之前的答案,如果你可以在UTC开始,那就没问题了 . 但我认为这也是人们使用具有 datetime that has a non UTC local timezone. 的tz意识值的常见情况 .

    如果您只是按名称去,可能会推断replace()将适用并生成正确的日期时间感知对象 . 不是这种情况 .

    the replace( tzinfo=... ) seems to be random in its behaviour . 因此没用 . 不要用这个!

    localize是正确使用的功能 . 例:

    localdatetime_aware = tz.localize(datetime_nonaware)
    

    或者更完整的例子:

    import pytz
    from datetime import datetime
    pytz.timezone('Australia/Melbourne').localize(datetime.now())
    

    给我一个当前本地时间的时区感知日期时间值:

    datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
    
  • 6

    为了增加当地时区; (使用dateutil)

    from dateutil import tz
    import datetime
    
    dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)
    
    dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())
    
  • 2

    以unutbu的答案形式;我制作了一个实用程序模块来处理这样的事情,语法更直观 . 可以用pip安装 .

    import datetime
    import saturn
    
    unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
    now_aware = saturn.fix_naive(unaware)
    
    now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')
    
  • 62

    我纯粹知道的两种方法

    from datetime import datetime
    import pytz
    
    naive = datetime.now()
    aware = naive.replace(tzinfo=pytz.UTC)
    

    要么

    aware = pytz.UTC.localize(naive)
    

    当然,您可以使用任何时区而不是UTC,

  • 2

    我在Django中使用此语句将不知不觉的时间转换为意识到:

    from django.utils import timezone
    
    dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
    
  • 77

    这编纂了@Sérgio和@ unutbu的answers . 它将"just work"与 pytz.timezone 对象或IANA Time Zone字符串 .

    def make_tz_aware(dt, tz='UTC', is_dst=None):
        """Add timezone information to a datetime object, only if it is naive."""
        tz = dt.tzinfo or tz
        try:
            tz = pytz.timezone(tz)
        except AttributeError:
            pass
        return tz.localize(dt, is_dst=is_dst)
    

    这似乎是 datetime.localize() (或 .inform().awarify() )应该做的,接受tz参数的字符串和时区对象,如果没有指定时区,则默认为UTC .

相关问题