파이썬에서 -0400 시간대 문자열로 날짜를 구문 분석하는 방법은 무엇입니까?


81

'2009/05/13 19:19:30 -0400'형식의 날짜 문자열이 있습니다. 이전 버전의 Python은 후행 시간대 사양에 대해 strptime에서 % z 형식 태그를 지원했을 수 있지만 2.6.x는이를 제거한 것으로 보입니다.

이 문자열을 datetime 객체로 구문 분석하는 올바른 방법은 무엇입니까?

답변:


117

dateutil에서 구문 분석 기능을 사용할 수 있습니다.

>>> from dateutil.parser import parse
>>> d = parse('2009/05/13 19:19:30 -0400')
>>> d
datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=tzoffset(None, -14400))

이렇게하면 사용할 수있는 datetime 객체를 얻을 수 있습니다.

대답 했듯이 dateutil2.0은 Python 3.0 용으로 작성되었으며 Python 2.x에서는 작동하지 않습니다. Python 2.x의 경우 dateutil1.5를 사용해야합니다.


13
이것은 dateutilPython 에서 나에게 잘 작동합니다 ( 2.1) 2.7.2. Python 3은 필요하지 않습니다. pip에서 설치하는 경우 패키지 이름은 python-dateutil.
BigglesZX

47

%z Python 3.2 이상에서 지원됩니다.

>>> from datetime import datetime
>>> datetime.strptime('2009/05/13 19:19:30 -0400', '%Y/%m/%d %H:%M:%S %z')
datetime.datetime(2009, 5, 13, 19, 19, 30,
                  tzinfo=datetime.timezone(datetime.timedelta(-1, 72000)))

이전 버전 :

from datetime import datetime

date_str = '2009/05/13 19:19:30 -0400'
naive_date_str, _, offset_str = date_str.rpartition(' ')
naive_dt = datetime.strptime(naive_date_str, '%Y/%m/%d %H:%M:%S')
offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:])
if offset_str[0] == "-":
   offset = -offset
dt = naive_dt.replace(tzinfo=FixedOffset(offset))
print(repr(dt))
# -> datetime.datetime(2009, 5, 13, 19, 19, 30, tzinfo=FixedOffset(-240))
print(dt)
# -> 2009-05-13 19:19:30-04:00

경우 FixedOffset에 따라 클래스입니다 워드 프로세서의 코드 예제 :

from datetime import timedelta, tzinfo

class FixedOffset(tzinfo):
    """Fixed offset in minutes: `time = utc_time + utc_offset`."""
    def __init__(self, offset):
        self.__offset = timedelta(minutes=offset)
        hours, minutes = divmod(offset, 60)
        #NOTE: the last part is to remind about deprecated POSIX GMT+h timezones
        #  that have the opposite sign in the name;
        #  the corresponding numeric value is not used e.g., no minutes
        self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours)
    def utcoffset(self, dt=None):
        return self.__offset
    def tzname(self, dt=None):
        return self.__name
    def dst(self, dt=None):
        return timedelta(0)
    def __repr__(self):
        return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)

1
이로 인해 ValueError: 'z' is a bad directive in format '%Y-%m-%d %M:%H:%S.%f %z'제 경우에는 (Python 2.7) 이 발생합니다 .
Jonathan H

@Sheljohn 그것은 Python 2.7에서 작동하지 않아야합니다. 대답의 맨 위를보십시오.
jfs

그건 그렇고, 이것이 Python 2.7 문서에 전혀
62mkv

22

다음은 "%z"Python 2.7 및 이전 문제에 대한 수정입니다.

사용하는 대신:

datetime.strptime(t,'%Y-%m-%dT%H:%M %z')

다음 timedelta과 같이 시간대를 설명하려면를 사용하십시오 .

from datetime import datetime,timedelta
def dt_parse(t):
    ret = datetime.strptime(t[0:16],'%Y-%m-%dT%H:%M')
    if t[18]=='+':
        ret-=timedelta(hours=int(t[19:22]),minutes=int(t[23:]))
    elif t[18]=='-':
        ret+=timedelta(hours=int(t[19:22]),minutes=int(t[23:]))
    return ret

날짜는로 변환되어 GMT시간대에 대한 걱정없이 날짜 산술을 수행 할 수 있습니다.


나는 이것을 좋아하지만 'seconds ='를 'minutes ='로 변경해야합니다.
데이브

1
참고로 문자열에서 시간대를 가져 와서 날짜 시간을 UTC로 변환하려면 여기에 나열된 반대 논리를 사용합니다. 시간대에 +가 있으면 타임 델타를 빼고 그 반대의 경우도 마찬가지입니다.
Sector95

UTC 로의 변환은 잘못되었습니다. +문자가 있으면 타임 델타를 빼야 하며 그 반대의 경우도 마찬가지입니다. 코드를 수정하고 수정했습니다.
tomtastico dec.

7

dateutil을 사용할 때의 문제는 dateutil에 제한된 형식 지정 옵션 ( dayfirst및 전용 yearfirst) 이 있으므로 serialization과 deserialization 모두에 대해 동일한 형식 문자열을 사용할 수 없다는 것 입니다.

내 응용 프로그램에서 형식 문자열을 .INI 파일에 저장하고 각 배포에는 고유 한 형식이있을 수 있습니다. 따라서 나는 dateutil 접근 방식을 정말로 좋아하지 않습니다.

대신 pytz를 사용하는 대체 방법은 다음과 같습니다.

from datetime import datetime, timedelta

from pytz import timezone, utc
from pytz.tzinfo import StaticTzInfo

class OffsetTime(StaticTzInfo):
    def __init__(self, offset):
        """A dumb timezone based on offset such as +0530, -0600, etc.
        """
        hours = int(offset[:3])
        minutes = int(offset[0] + offset[3:])
        self._utcoffset = timedelta(hours=hours, minutes=minutes)

def load_datetime(value, format):
    if format.endswith('%z'):
        format = format[:-2]
        offset = value[-5:]
        value = value[:-5]
        return OffsetTime(offset).localize(datetime.strptime(value, format))

    return datetime.strptime(value, format)

def dump_datetime(value, format):
    return value.strftime(format)

value = '2009/05/13 19:19:30 -0400'
format = '%Y/%m/%d %H:%M:%S %z'

assert dump_datetime(load_datetime(value, format), format) == value
assert datetime(2009, 5, 13, 23, 19, 30, tzinfo=utc) \
    .astimezone(timezone('US/Eastern')) == load_datetime(value, format)

2

오래된 파이썬을위한 라이너 하나. 다음과 같이 +/- 기호에 따라 타임 델타에 1 / -1을 곱할 수 있습니다.

datetime.strptime(s[:19], '%Y-%m-%dT%H:%M:%S') + timedelta(hours=int(s[20:22]), minutes=int(s[23:])) * (-1 if s[19] == '+' else 1)

-10

Linux를 사용하는 경우 외부 date명령을 사용하여 dwim 할 수 있습니다 .

import commands, datetime

def parsedate(text):
  output=commands.getoutput('date -d "%s" +%%s' % text )
  try:
      stamp=eval(output)
  except:
      print output
      raise
  return datetime.datetime.frometimestamp(stamp)

물론 이것은 dateutil보다 이식성이 떨어지지 만 date"어제"또는 "작년"과 같은 입력도 허용 하므로 약간 더 유연합니다. :-)


3
나는 이것을 위해 외부 프로그램을 호출하는 것이 좋지 않다고 생각합니다. 그리고 다음 약점 : eval () : 웹 서버가이 코드를 실행한다면, 서버에서 임의의 코드를 실행할 수 있습니다!
guettli 2011

5
그것은 모두 컨텍스트에 달려 있습니다. 우리가
추구

10
1) 사소한 것에 대한 시스템 호출을 만들고, 2) 쉘 호출에 직접 문자열을 주입하고, 3) eval ()을 호출하고, 4) 예외 포괄 기능이 있기 때문입니다. 기본적으로 이것은 일을하지 않는 방법의 예입니다 .
benjaoming 2014

이 경우 eval은 악하므로 사용해서는 안됩니다. 외부 호출은 시간대가 숫자 오프셋이 아닌 시간대 인식 날짜 문자열에서 유닉스 타임 스탬프를 얻는 가장 쉽고 실용적인 방법 인 것 같습니다.
Leliel 2016

1
음, 다시 말하지만,이 "eval is evil"모토는 실제로 당신의 상황에 달려 있습니다 (OP에 의해 언급되지 않았 음). 내가 직접 사용할 스크립트를 작성할 때 eval을 자유롭게 사용하고 굉장합니다. Python은 글루 스크립트를위한 훌륭한 언어입니다! 물론 위의 일부 답변에서와 같이 복잡한 일반 사례 오버 엔지니어링 솔루션을 롤아웃 한 다음 Java라고 주장하는 유일한 올바른 방법이라고 주장 할 수 있습니다. 그러나 많은 사용 사례에서 빠르고 더러운 솔루션이 똑같이 좋습니다.
Gyom
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.