dateutils 默认为识别部分的最后一次出现,而不是下一次

dateutils default to the last occurence of recognized part, not next

我正在使用 dateutils.parser.parse 来解析可能包含部分信息的日期字符串。如果某些信息不存在,parse 可以采用 default 关键字参数,它将从中填充任何缺失的字段。此默认值默认为 datetime.datetime.today().

对于 dateutil.parser.parse("Thursday") 这样的案例,这意味着它将 return 日期为 下一个 星期四。但是,我需要 return 最后一个 星期四的日期(包括今天,如果今天恰好是星期四的话)。

所以,假设 today == datetime.datetime(2018, 2, 20)(星期二),我想让所有这些 assert 都为真:

from dateutil import parser
from datetime import datetime

def parse(date_str, default=None):
    # this needs to be modified
    return parser.parse(date_str, default=default)

today = datetime(2018, 2, 20)

assert parse("Tuesday", default=today) == today    # True
assert parse("Thursday", default=today) == datetime(2018, 2, 15)    # False
assert parse("Jan 31", default=today) == datetime(2018, 1, 31)    # True
assert parse("December 10", default=today) == datetime(2017, 12, 10)    # False

有没有简单的方法可以做到这一点?使用当前的 parse 函数,只有第一个和第三个 assert 会通过。

这是您修改后的代码 (code.py):

#!/usr/bin/env python3

import sys
from dateutil import parser
from datetime import datetime, timedelta


today = datetime(2018, 2, 20)

data = [
    ("Tuesday", today, today),
    ("Thursday", datetime(2018, 2, 15), today),
    ("Jan 31", datetime(2018, 1, 31), today),
    ("December 10", datetime(2017, 12, 10), today),
]


def parse(date_str, default=None):
    # this needs to be modified
    return parser.parse(date_str, default=default)


def _days_in_year(year):
    try:
        datetime(year, 2, 29)
    except ValueError:
        return 365
    return 366


def parse2(date_str, default=None):
    dt = parser.parse(date_str, default=default)
    if default is not None:
        weekday_strs = [day_str.lower() for day_tuple in parser.parserinfo.WEEKDAYS for day_str in day_tuple]
        if date_str.lower() in weekday_strs:
            if dt.weekday() > default.weekday():
                dt -= timedelta(days=7)
        else:
            if (dt.month > today.month) or ((dt.month == today.month) and (dt.day > today.day)):
                dt -= timedelta(days=_days_in_year(dt.year))
    return dt


def print_stats(parse_func):
    print("\nPrinting stats for \"{:s}\"".format(parse_func.__name__))
    for triple in data:
        d = parse_func(triple[0], default=triple[2])
        print("  [{:s}] [{:s}] [{:s}] [{:s}]".format(triple[0], str(d), str(triple[1]), "True" if d == triple[1] else "False"))


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    print_stats(parse)
    print_stats(parse2)

备注:

  • 我更改了代码的结构 "a bit",对其进行参数化,因此如果需要更改(例如要添加的新示例),更改应该是最小的
    • 我添加了一个函数 (print_stats,而不是 asserts,而是打印结果(而不是提高 AssertError 如果不匹配则退出程序)
      • 接受一个参数 (parse_func),这是一个进行解析的函数(例如 parse
      • 使用一些全局声明的数据(数据)和(以上)函数
    • data - 是一个三元组列表,其中每个三元组包含:
      1. 要转换的文本
      2. 预期 日期时间 ([Python 3.Docs]: datetime Objects) 由转换
      3. 产生
      4. 默认参数传递给解析函数(parse_func)
  • parse2函数(parse的改进版):

    • 接受 2 种类型的日期字符串:
      1. 工作日名称
      2. 月/日(未排序)
    • 进行常规解析,如果转换后的对象在 之后作为 default 参数传递(通过比较确定2 个对象的适当属性),它减去一个句点(看看 [Python 3.Docs]: timedelta Objects):
      1. "Thursday"在"Tuesday"之后,所以它减去一周的天数(7)
      2. "December 10"在"February 20"之后,所以减去一年中的天数*
    • weekday_strs:我最好举例说明一下:

      >>> parser.parserinfo.WEEKDAYS
      [('Mon', 'Monday'), ('Tue', 'Tuesday'), ('Wed', 'Wednesday'), ('Thu', 'Thursday'), ('Fri', 'Friday'), ('Sat', 'Saturday'), ('Sun', 'Sunday')]
      >>> [day_str.lower() for day_tuple in parser.parserinfo.WEEKDAYS for day_str in day_tuple]
      ['mon', 'monday', 'tue', 'tuesday', 'wed', 'wednesday', 'thu', 'thursday', 'fri', 'friday', 'sat', 'saturday', 'sun', 'sunday']
      
      • 变平parser.parserinfo.WEEKDAYS
      • 将字符串转换为小写(为了简化比较)
  • _days_in_year* - 你可能已经猜到了,returns 一年中的天数(不能简单地减去 365,因为 leap 年可能会把事情搞砸):
    >>> dt = datetime(2018, 3, 1)
    >>> dt
    datetime.datetime(2018, 3, 1, 0, 0)
    >>> dt - timedelta(365)
    datetime.datetime(2017, 3, 1, 0, 0)
    >>> dt = datetime(2016, 3, 1)
    >>> dt
    datetime.datetime(2016, 3, 1, 0, 0)
    >>> dt - timedelta(365)
    datetime.datetime(2015, 3, 2, 0, 0)
    

输出:

(py35x64_test) E:\Work\Dev\Whosebug\q048884480>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32


Printing stats for "parse"
  [Tuesday] [2018-02-20 00:00:00] [2018-02-20 00:00:00] [True]
  [Thursday] [2018-02-22 00:00:00] [2018-02-15 00:00:00] [False]
  [Jan 31] [2018-01-31 00:00:00] [2018-01-31 00:00:00] [True]
  [December 10] [2018-12-10 00:00:00] [2017-12-10 00:00:00] [False]

Printing stats for "parse2"
  [Tuesday] [2018-02-20 00:00:00] [2018-02-20 00:00:00] [True]
  [Thursday] [2018-02-15 00:00:00] [2018-02-15 00:00:00] [True]
  [Jan 31] [2018-01-31 00:00:00] [2018-01-31 00:00:00] [True]
  [December 10] [2017-12-10 00:00:00] [2017-12-10 00:00:00] [True]