복붙노트

[PYTHON] 날짜 문자열에서 적절한 strftime 형식을 결정하는 방법?

PYTHON

날짜 문자열에서 적절한 strftime 형식을 결정하는 방법?

dateutil 구문 분석기는 다양한 출처에서 날짜와 시간을 올바르게 추측하는 훌륭한 작업을 수행합니다.

우리는 각 파일이 하나의 날짜 / 시간 형식 만 사용하는 파일을 처리하지만 형식은 파일마다 다릅니다. 프로파일 링은 dateutil.parser.parse에서 많은 시간을 사용하고 있음을 보여줍니다. 파일 당 한 번만 결정하면되므로 매번 형식을 추측하지 못하는 무언가를 구현하면 속도가 빨라질 수 있습니다.

실제로 형식을 미리 알지 못하고 형식을 추론해야합니다. 같은 것 :

from MysteryPackage import date_string_to_format_string
import datetime

# e.g. mystring = '1 Jan 2016'
myformat = None

...

# somewhere in a loop reading from a file or connection:
if myformat is None:
    myformat = date_string_to_format_string(mystring)

# do the usual checks to see if that worked, then:
mydatetime = datetime.strptime(mystring, myformat)

그런 기능이 있습니까?

해결법

  1. ==============================

    1.이것은 까다 롭습니다. 내 접근 방식은 정규 표현식과 새로운 정규 표현식 모듈에서만 지원되는 (? (DEFINE) ...) 구문을 사용합니다. 본질적으로, DEFINE은 서브 루틴을 정합하기 전에 정의하기 때문에 먼저 날짜 추측 함수에 필요한 모든 벽돌을 정의합니다.

    이것은 까다 롭습니다. 내 접근 방식은 정규 표현식과 새로운 정규 표현식 모듈에서만 지원되는 (? (DEFINE) ...) 구문을 사용합니다. 본질적으로, DEFINE은 서브 루틴을 정합하기 전에 정의하기 때문에 먼저 날짜 추측 함수에 필요한 모든 벽돌을 정의합니다.

        (?(DEFINE)
            (?P<year_def>[12]\d{3})
            (?P<year_short_def>\d{2})
            (?P<month_def>January|February|March|April|May|June|
            July|August|September|October|November|December)
            (?P<month_short_def>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
            (?P<day_def>(?:0[1-9]|[1-9]|[12][0-9]|3[01]))
            (?P<weekday_def>(?:Mon|Tue|Wednes|Thurs|Fri|Satur|Sun)day)
            (?P<weekday_short_def>Mon|Tue|Wed|Thu|Fri|Sat|Sun)
            (?P<hms_def>\d{2}:\d{2}:\d{2})
            (?P<hm_def>\d{2}:\d{2})
                (?P<ms_def>\d{5,6})
                (?P<delim_def>([-/., ]+|(?<=\d|^)T))
            )
            # actually match them
            (?P<hms>^(?&hms_def)$)|(?P<year>^(?&year_def)$)|(?P<month>^(?&month_def)$)|(?P<month_short>^(?&month_short_def)$)|(?P<day>^(?&day_def)$)|
            (?P<weekday>^(?&weekday_def)$)|(?P<weekday_short>^(?&weekday_short_def)$)|(?P<hm>^(?&hm_def)$)|(?P<delim>^(?&delim_def)$)|(?P<ms>^(?&ms_def)$)
            """, re.VERBOSE)
    

    이 후, 우리는 가능한 구분 기호를 생각할 필요가있다.

    # delim
    delim = re.compile(r'([-/., ]+|(?<=\d)T)')
    

    형식 매핑 :

    # formats
    formats = {'ms': '%f', 'year': '%Y', 'month': '%B', 'month_dec': '%m', 'day': '%d', 'weekday': '%A', 'hms': '%H:%M:%S', 'weekday_short': '%a', 'month_short': '%b', 'hm': '%H:%M', 'delim': ''}
    

    GuessFormat () 함수는 구분 기호를 사용하여 파트를 분할하고이를 일치 시키려고 시도하고 strftime ()에 해당하는 코드를 출력합니다.

    def GuessFormat(datestring):
    
        # define the bricks
        bricks = re.compile(r"""
                (?(DEFINE)
                    (?P<year_def>[12]\d{3})
                    (?P<year_short_def>\d{2})
                    (?P<month_def>January|February|March|April|May|June|
                    July|August|September|October|November|December)
                    (?P<month_short_def>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
                    (?P<day_def>(?:0[1-9]|[1-9]|[12][0-9]|3[01]))
                    (?P<weekday_def>(?:Mon|Tue|Wednes|Thurs|Fri|Satur|Sun)day)
                    (?P<weekday_short_def>Mon|Tue|Wed|Thu|Fri|Sat|Sun)
                    (?P<hms_def>T?\d{2}:\d{2}:\d{2})
                    (?P<hm_def>T?\d{2}:\d{2})
                    (?P<ms_def>\d{5,6})
                    (?P<delim_def>([-/., ]+|(?<=\d|^)T))
                )
                # actually match them
                (?P<hms>^(?&hms_def)$)|(?P<year>^(?&year_def)$)|(?P<month>^(?&month_def)$)|(?P<month_short>^(?&month_short_def)$)|(?P<day>^(?&day_def)$)|
                (?P<weekday>^(?&weekday_def)$)|(?P<weekday_short>^(?&weekday_short_def)$)|(?P<hm>^(?&hm_def)$)|(?P<delim>^(?&delim_def)$)|(?P<ms>^(?&ms_def)$)
                """, re.VERBOSE)
    
        # delim
        delim = re.compile(r'([-/., ]+|(?<=\d)T)')
    
        # formats
        formats = {'ms': '%f', 'year': '%Y', 'month': '%B', 'month_dec': '%m', 'day': '%d', 'weekday': '%A', 'hms': '%H:%M:%S', 'weekday_short': '%a', 'month_short': '%b', 'hm': '%H:%M', 'delim': ''}
    
        parts = delim.split(datestring)
        out = []
        for index, part in enumerate(parts):
            try:
                brick = dict(filter(lambda x: x[1] is not None, bricks.match(part).groupdict().items()))
                key = next(iter(brick))
    
                # ambiguities
                if key == 'day' and index == 2:
                    key = 'month_dec'
    
                item = part if key == 'delim' else formats[key]
                out.append(item)
            except AttributeError:
                out.append(part)
    
        return "".join(out)
    

    결국 테스트 :

    import regex as re
    
    datestrings = [datetime.now().isoformat(), '2006-11-02', 'Thursday, 10 August 2006 08:42:51', 'August 9, 1995', 'Aug 9, 1995', 'Thu, 01 Jan 1970 00:00:00', '21/11/06 16:30', 
    '06 Jun 2017 20:33:10']
    
    # test
    for dt in datestrings:
        print("Date: {}, Format: {}".format(dt, GuessFormat(dt)))
    

    결과는 다음과 같습니다.

    Date: 2017-06-07T22:02:05.001811, Format: %Y-%m-%dT%H:%M:%S.%f
    Date: 2006-11-02, Format: %Y-%m-%d
    Date: Thursday, 10 August 2006 08:42:51, Format: %A, %m %B %Y %H:%M:%S
    Date: August 9, 1995, Format: %B %m, %Y
    Date: Aug 9, 1995, Format: %b %m, %Y
    Date: Thu, 01 Jan 1970 00:00:00, Format: %a, %m %b %Y %H:%M:%S
    Date: 21/11/06 16:30, Format: %d/%m/%d %H:%M
    Date: 06 Jun 2017 20:33:10, Format: %d %b %Y %H:%M:%S
    
  2. ==============================

    2.저는 기성품 해결책이 없지만 이것은 매우 까다로운 문제입니다. 너무 많은 뇌 시간이 이미 dateutil에 사용 되었기 때문에이를 대체하려고하지 않고 그것을 통합하는 접근법을 제안 할 것입니다 :

    저는 기성품 해결책이 없지만 이것은 매우 까다로운 문제입니다. 너무 많은 뇌 시간이 이미 dateutil에 사용 되었기 때문에이를 대체하려고하지 않고 그것을 통합하는 접근법을 제안 할 것입니다 :

    "각 파일은 하나의 날짜 / 시간 형식 만 사용합니다"라고 말했기 때문에이 방법을 사용해야합니다 (여러 파일의 날짜가 서로 다른 경우 여러 날짜 값을 비교하여 mm / dd 모호성을 해결할 수 있음).

  3. ==============================

    3.자신 만의 파서를 작성할 수 있습니다.

    자신 만의 파서를 작성할 수 있습니다.

    import datetime
    
    class DateFormatFinder:
        def __init__(self):
            self.fmts = []
    
        def add(self,fmt):
            self.fmts.append(fmt)
    
        def find(self, ss):
            for fmt in self.fmts:            
                try:
                    datetime.datetime.strptime(ss, fmt)
                    return fmt
                except:
                    pass
            return None
    

    다음과 같이 사용할 수 있습니다.

    >>> df = DateFormatFinder()
    >>> df.add('%m/%d/%y %H:%M')
    >>> df.add('%m/%d/%y')
    >>> df.add('%H:%M')
    
    >>> df.find("01/02/06 16:30")
    '%m/%d/%y %H:%M'
    >>> df.find("01/02/06")
    '%m/%d/%y'
    >>> df.find("16:30")
    '%H:%M'
    >>> df.find("01/02/06 16:30")
    '%m/%d/%y %H:%M'
    >>> df.find("01/02/2006")
    

    그러나 날짜가 모호 할 수 있고 형식이 문맥없이 결정될 수 없기 때문에 그렇게 간단하지 않습니다.

    >>> datetime.strptime("01/02/06 16:30", "%m/%d/%y %H:%M") # us format
    datetime.datetime(2006, 1, 2, 16, 30)
    >>> datetime.strptime("01/02/06 16:30", "%d/%m/%y %H:%M") # european format
    datetime.datetime(2006, 2, 1, 16, 30)
    
  4. from https://stackoverflow.com/questions/44321601/how-to-determine-appropriate-strftime-format-from-a-date-string by cc-by-sa and MIT license