获取dateutil.parse中的格式

Dav*_*542 24 python python-3.x python-dateutil

在dateutil中解析日期后,有没有办法获得"格式".例如:

>>> x = parse("2014-01-01 00:12:12")
datetime.datetime(2014, 1, 1, 0, 12, 12)

x.get_original_string_format()
YYYY-MM-DD HH:MM:SS # %Y-%m-%d %H:%M:%S

# Or, passing the date-string directly
get_original_string_format("2014-01-01 00:12:12")
YYYY-MM-DD HH:MM:SS # %Y-%m-%d %H:%M:%S
Run Code Online (Sandbox Code Playgroud)

更新:我想为这个问题添加一个赏金,看看是否有人可以添加一个相应的答案来获取传递的公共日期字符串的字符串格式.它可以使用,dateutil如果你想,但它没有.希望我们能在这里获得一些创造性的解决方案.

ale*_*cxe 11

在dateutil中解析日期后,有没有办法获得"格式"?

不可能dateutil.问题是在解析期间的任何时候都不会将dateutil 格式作为中间结果,因为它分别检测日期时间的独立组件 - 看一下这不太容易阅读源代码.

  • @ David542好,这需要一些时间来研究,这取决于你试图解决的实际问题.根据问题的范围和可能的输入,可能只是迭代常见的日期时间格式,然后逐个尝试.例如,你可以使用[`dateparser`指定`date_formats`](https://dateparser.readthedocs.io/en/latest/#popular-formats)并尝试流行的格式,直到它工作 - 它的格式将工作将是在字符串中找到的日期时间字符串的格式. (2认同)

ben*_*nvc 7

我不知道你可以从dateutil(或我知道的任何其他python时间戳解析器)返回解析格式的方法.

实现自己的时间戳解析方法返回格式以及datetime对象是相当简单的,datetime.strptime()但是对有效的时间戳格式的广泛有用的列表进行有效的操作却不是.

以下示例使用了一个仅包含50多种格式的列表,这些格式是从快速搜索时间戳格式的热门命中之一改编而来的.它甚至不会划分解析的各种格式的表面dateutil.它按顺序测试每种格式,直到找到匹配或耗尽列表中的所有格式(可能比dateutil独立定位各种日期时间部分的效率低得多,如@alecxe的答案中所述).

此外,我还提供了一些示例时间戳格式,其中包括时区名称(而不是偏移量).如果您针对这些特定的日期时间字符串运行下面的示例方法,您可能会发现该方法返回"无法解析格式",即使我已使用该%Z指令包含匹配的格式.有关使用%Z时区名称的挑战的一些解释可以在bugs.python.org的issue 22377中找到(只是为了突出显示实现自己的日期时间解析方法的另一个重要方面).

有了所有这些警告,如果你正在处理一组可管理的潜在格式,那么实现像下面这样简单的东西可能会得到你所需要的.

尝试将日期时间字符串与格式列表进行匹配并返回datetime对象以及匹配格式的示例方法:

from datetime import datetime

def parse_timestamp(datestring, formats):
    for f in formats:
        try:
            d = datetime.strptime(datestring, f)
        except:
            continue
        return (d, f)
    return (datestring, 'Unable to parse format')
Run Code Online (Sandbox Code Playgroud)

时间戳,时区,时间范围和日期格式改编的示例格式和日期时间字符串:

formats = ['%Y-%m-%dT%H:%M:%S*%f%z','%Y %b %d %H:%M:%S.%f %Z','%b %d %H:%M:%S %z %Y','%d/%b/%Y:%H:%M:%S %z','%b %d, %Y %I:%M:%S %p','%b %d %Y %H:%M:%S','%b %d %H:%M:%S %Y','%b %d %H:%M:%S %z','%b %d %H:%M:%S','%Y-%m-%dT%H:%M:%S%z','%Y-%m-%dT%H:%M:%S.%f%z','%Y-%m-%d %H:%M:%S %z','%Y-%m-%d %H:%M:%S%z','%Y-%m-%d %H:%M:%S,%f','%Y/%m/%d*%H:%M:%S','%Y %b %d %H:%M:%S.%f*%Z','%Y %b %d %H:%M:%S.%f','%Y-%m-%d %H:%M:%S,%f%z','%Y-%m-%d %H:%M:%S.%f','%Y-%m-%d %H:%M:%S.%f%z','%Y-%m-%dT%H:%M:%S.%f','%Y-%m-%dT%H:%M:%S','%Y-%m-%dT%H:%M:%S%Z','%Y-%m-%dT%H:%M:%S.%f','%Y-%m-%dT%H:%M:%S','%Y-%m-%d*%H:%M:%S:%f','%Y-%m-%d*%H:%M:%S','%y-%m-%d %H:%M:%S,%f %z','%y-%m-%d %H:%M:%S,%f','%y-%m-%d %H:%M:%S','%y/%m/%d %H:%M:%S','%y%m%d %H:%M:%S','%Y%m%d %H:%M:%S.%f','%m/%d/%y*%H:%M:%S','%m/%d/%Y*%H:%M:%S','%m/%d/%Y*%H:%M:%S*%f','%m/%d/%y %H:%M:%S %z','%m/%d/%Y %H:%M:%S %z','%H:%M:%S','%H:%M:%S.%f','%H:%M:%S,%f','%d/%b %H:%M:%S,%f','%d/%b/%Y:%H:%M:%S','%d/%b/%Y %H:%M:%S','%d-%b-%Y %H:%M:%S','%d-%b-%Y %H:%M:%S.%f','%d %b %Y %H:%M:%S','%d %b %Y %H:%M:%S*%f','%m%d_%H:%M:%S','%m%d_%H:%M:%S.%f','%m/%d/%Y %I:%M:%S %p:%f','%m/%d/%Y %H:%M:%S %p']

datestrings = ['2018-08-20T13:20:10*633+0000','2017 Mar 03 05:12:41.211 PDT','Jan 21 18:20:11 +0000 2017','19/Apr/2017:06:36:15 -0700','Dec 2, 2017 2:39:58 AM','Jun 09 2018 15:28:14','Apr 20 00:00:35 2010','Sep 28 19:00:00 +0000','Mar 16 08:12:04','2017-10-14T22:11:20+0000','2017-07-01T14:59:55.711+0000','2017-08-19 12:17:55 -0400','2017-08-19 12:17:55-0400','2017-06-26 02:31:29,573','2017/04/12*19:37:50','2018 Apr 13 22:08:13.211*PDT','2017 Mar 10 01:44:20.392','2017-03-10 14:30:12,655+0000','2018-02-27 15:35:20.311','2017-03-12 13:11:34.222-0700','2017-07-22T16:28:55.444','2017-09-08T03:13:10','2017-03-12T17:56:22-0700','2017-11-22T10:10:15.455','2017-02-11T18:31:44','2017-10-30*02:47:33:899','2017-07-04*13:23:55','11-02-11 16:47:35,985 +0000','10-06-26 02:31:29,573','10-04-19 12:00:17','06/01/22 04:11:05','150423 11:42:35','20150423 11:42:35.173','08/10/11*13:33:56','11/22/2017*05:13:11','05/09/2017*08:22:14*612','04/23/17 04:34:22 +0000','10/03/2017 07:29:46 -0700','11:42:35','11:42:35.173','11:42:35,173','23/Apr 11:42:35,173','23/Apr/2017:11:42:35','23/Apr/2017 11:42:35','23-Apr-2017 11:42:35','23-Apr-2017 11:42:35.883','23 Apr 2017 11:42:35','23 Apr 2017 10:32:35*311','0423_11:42:35','0423_11:42:35.883','8/5/2011 3:31:18 AM:234','9/28/2011 2:23:15 PM']
Run Code Online (Sandbox Code Playgroud)

用法示例:

print(parse_timestamp(datestrings[0], formats))
# OUTPUT
# (datetime.datetime(2018, 8, 20, 13, 20, 10, 633000, tzinfo=datetime.timezone.utc), '%Y-%m-%dT%H:%M:%S*%f%z')
Run Code Online (Sandbox Code Playgroud)


Dod*_*die 6

我的想法是:

  1. 创建一个对象,其中包含您认为可能在日期模式中的候选说明符列表(添加的越多,从另一端得到的可能性就越多)
  2. 解析日期字符串
  3. 根据日期和您提供的候选列表,为字符串中的每个元素创建可能的说明符列表。
  4. 重新组合它们以产生“可能”的列表。

如果您只得到一个候选者,您可以非常确定它的格式是否正确。但您通常会得到多种可能性(特别是日期、月份、分钟和小时都在 0-10 范围内)。

示例类:

import re
from itertools import product
from dateutil.parser import parse
from collections import defaultdict, Counter

COMMON_SPECIFIERS = [
    '%a', '%A', '%d', '%b', '%B', '%m',
    '%Y', '%H', '%p', '%M', '%S', '%Z',
]


class FormatFinder:
    def __init__(self,
                 valid_specifiers=COMMON_SPECIFIERS,
                 date_element=r'([\w]+)',
                 delimiter_element=r'([\W]+)',
                 ignore_case=False):
        self.specifiers = valid_specifiers
        joined = (r'' + date_element + r"|" + delimiter_element)
        self.pattern = re.compile(joined)
        self.ignore_case = ignore_case

    def find_candidate_patterns(self, date_string):
        date = parse(date_string)
        tokens = self.pattern.findall(date_string)

        candidate_specifiers = defaultdict(list)

        for specifier in self.specifiers:
            token = date.strftime(specifier)
            candidate_specifiers[token].append(specifier)
            if self.ignore_case:
                candidate_specifiers[token.
                                     upper()] = candidate_specifiers[token]
                candidate_specifiers[token.
                                     lower()] = candidate_specifiers[token]

        options_for_each_element = []
        for (token, delimiter) in tokens:
            if token:
                if token not in candidate_specifiers:
                    options_for_each_element.append(
                        [token])  # just use this verbatim?
                else:
                    options_for_each_element.append(
                        candidate_specifiers[token])
            else:
                options_for_each_element.append([delimiter])

        for parts in product(*options_for_each_element):
            counts = Counter(parts)
            max_count = max(counts[specifier] for specifier in self.specifiers)
            if max_count > 1:
                # this is a candidate with the same item used more than once
                continue
            yield "".join(parts)
Run Code Online (Sandbox Code Playgroud)

以及一些示例测试:

def test_it_returns_value_from_question_1():
    s = "2014-01-01 00:12:12"
    candidates = FormatFinder().find_candidate_patterns(s)
    sut = FormatFinder()
    candidates = sut.find_candidate_patterns(s)
    assert "%Y-%m-%d %H:%M:%S" in candidates


def test_it_returns_value_from_question_2():
    s = 'Jan. 04, 2017'
    sut = FormatFinder()
    candidates = sut.find_candidate_patterns(s)
    candidates = list(candidates)
    assert "%b. %d, %Y" in candidates
    assert len(candidates) == 1


def test_it_can_ignore_case():
    # NB: apparently the 'AM/PM' is meant to be capitalised in my locale! 
    # News to me!
    s = "JANUARY 12, 2018 02:12 am"
    sut = FormatFinder(ignore_case=True)
    candidates = sut.find_candidate_patterns(s)
    assert "%B %d, %Y %H:%M %p" in candidates


def test_it_returns_parts_that_have_no_date_component_verbatim():
    # In this string, the 'at' is considered as a 'date' element, 
    # but there is no specifier that produces a candidate for it
    s = "January 12, 2018 at 02:12 AM"
    sut = FormatFinder()
    candidates = sut.find_candidate_patterns(s)
    assert "%B %d, %Y at %H:%M %p" in candidates
Run Code Online (Sandbox Code Playgroud)

为了让它更清楚一些,下面是在 iPython shell 中使用此代码的一些示例:

In [2]: ff = FormatFinder()

In [3]: list(ff.find_candidate_patterns("2014-01-01 00:12:12"))
Out[3]:
['%Y-%d-%m %H:%M:%S',
 '%Y-%d-%m %H:%S:%M',
 '%Y-%m-%d %H:%M:%S',
 '%Y-%m-%d %H:%S:%M']

In [4]: list(ff.find_candidate_patterns("Jan. 04, 2017"))
Out[4]: ['%b. %d, %Y']

In [5]: list(ff.find_candidate_patterns("January 12, 2018 at 02:12 AM"))
Out[5]: ['%B %d, %Y at %H:%M %p', '%B %M, %Y at %H:%d %p']

In [6]: ff_without_case = FormatFinder(ignore_case=True)

In [7]: list(ff_without_case.find_candidate_patterns("JANUARY 12, 2018 02:12 am"))
Out[7]: ['%B %d, %Y %H:%M %p', '%B %M, %Y %H:%d %p']
Run Code Online (Sandbox Code Playgroud)