16

Python のdateutil.parserツールを使用して、サード パーティのフィードから取得した日付を解析しています。解析された日付の欠落要素を埋めるために、デフォルトの日付自体を指定できます。デフォルトは今日です。これは一般的には役に立ちますが、私のユースケースには適切なデフォルトはありません。部分的な日付を、日付をまったく取得していないかのように扱いたいと思います (ほとんどの場合、データが文字化けしていることを意味するため)。私は次の回避策を書きました:

from dateutil import parser
import datetime

def parse_no_default(dt_str):
  dt = parser.parse(dt_str, default=datetime.datetime(1900, 1, 1)).date()
  dt2 = parser.parse(dt_str, default=datetime.datetime(1901, 2, 2)).date()
  if dt == dt2:
    return dt
  else:
    return None

(このスニペットは日付のみを調べます。これは、アプリケーションで気にするのは日付だけですが、同様のロジックを拡張して時間コンポーネントを含めることができます。)

これを行うためのより良い方法があると思っています(願っています)。同じ文字列を 2 回解析して、異なるデフォルト値が設定されているかどうかを確認するのは、控えめに言っても、リソースの無駄遣いのように思えます。

期待される動作の一連のテスト (nosetest ジェネレーターを使用) は次のとおりです。

import nose.tools
import lib.tools.date

def check_parse_no_default(sample, expected):
  actual = lib.tools.date.parse_no_default(sample)
  nose.tools.eq_(actual, expected)

def test_parse_no_default():
  cases = ( 
      ('2011-10-12', datetime.date(2011, 10, 12)),
      ('2011-10', None),
      ('2011', None),
      ('10-12', None),
      ('2011-10-12T11:45:30', datetime.date(2011, 10, 12)),
      ('10-12 11:45', None),
      ('', None),
      )   
  for sample, expected in cases:
    yield check_parse_no_default, sample, expected
4

4 に答える 4

8

ドメインによっては、次のソリューションが機能する場合があります。

DEFAULT_DATE = datetime.datetime(datetime.MINYEAR, 1, 1)

def parse_no_default(dt_str):    
    dt = parser.parse(dt_str, default=DEFAULT_DATE).date()
    if dt != DEFAULT_DATE:
       return dt
    else:
       return None

別のアプローチは、モンキー パッチ パーサー クラスを使用することです (これは非常にハッキーなので、他のオプションがある場合はお勧めしません)。

import dateutil.parser as parser
def parse(self, timestr, default=None,
          ignoretz=False, tzinfos=None,
          **kwargs):
    return self._parse(timestr, **kwargs)
parser.parser.parse = parse

次のように使用できます。

>>> ddd = parser.parser().parse('2011-01-02', None)
>>> ddd
_result(year=2011, month=01, day=02)
>>> ddd = parser.parser().parse('2011', None)
>>> ddd
_result(year=2011)

結果 (ddd) で使用可能なメンバーを確認することで、いつ None を返すかを判断できます。すべてのフィールドが利用可能な場合、ddd を datetime オブジェクトに変換できます。

# ddd might have following fields:
# "year", "month", "day", "weekday",
# "hour", "minute", "second", "microsecond",
# "tzname", "tzoffset"
datetime.datetime(ddd.year, ddd.month, ddd.day)
于 2011-12-08T17:29:54.733 に答える
3

これはおそらく「ハック」ですが、dateutil は、渡されたデフォルトから非常に少数の属性を参照しているように見えます。目的の方法で爆発する「偽の」日時を提供できます。

>>> import datetime
>>> import dateutil.parser
>>> class NoDefaultDate(object):
...     def replace(self, **fields):
...         if any(f not in fields for f in ('year', 'month', 'day')):
...             return None
...         return datetime.datetime(2000, 1, 1).replace(**fields)
>>> def wrap_parse(v):
...     _actual = dateutil.parser.parse(v, default=NoDefaultDate())
...     return _actual.date() if _actual is not None else None
>>> cases = (
...   ('2011-10-12', datetime.date(2011, 10, 12)),
...   ('2011-10', None),
...   ('2011', None),
...   ('10-12', None),
...   ('2011-10-12T11:45:30', datetime.date(2011, 10, 12)),
...   ('10-12 11:45', None),
...   ('', None),
...   )
>>> all(wrap_parse(test) == expected for test, expected in cases)
True
于 2013-08-14T21:29:47.237 に答える
0

私はdateutilでまったく同じ問題に遭遇しました。私はこの関数を書き、後世のために投稿すると考えました。基本的_parseに @ILYA Khlopotov のような基本的な方法を使用すると、次のことが示唆されます。

from dateutil.parser import parser
import datetime
from StringIO import StringIO

_CURRENT_YEAR = datetime.datetime.now().year
def is_good_date(date):
    try:
        parsed_date = parser._parse(parser(), StringIO(date))
    except:
        return None
    if not parsed_date: return None
    if not parsed_date.year: return None
    if parsed_date.year < 1890 or parsed_date.year > _CURRENT_YEAR: return None
    if not parsed_date.month: return None
    if parsed_date.month < 1 or parsed_date.month > 12: return None
    if not parsed_date.day: return None
    if parsed_date.day < 1 or parsed_date.day > 31: return None
    return parsed_date

返されたオブジェクトはdatetimeインスタンスではありませんが、.year.month、および.day属性があり、私のニーズには十分でした。簡単にdatetimeインスタンスに変換できると思います。

于 2013-08-14T20:34:56.043 に答える
0

simple-date がこれを行います (内部的に複数の形式を試しますが、使用するパターンは python の日付パターンを正規表現などのオプション部分で拡張するため、思ったほど多くはありません)。

https://github.com/andrewcooke/simple-dateを参照してください- ただし、python 3.2 以降のみです (申し訳ありません)。

デフォルトで必要なものよりも寛大です:

>>> for date in ('2011-10-12', '2011-10', '2011', '10-12', '2011-10-12T11:45:30', '10-12 11:45', ''):
...   print(date)
...   try: print(SimpleDate(date).naive.datetime)
...   except: print('nope')
... 
2011-10-12
2011-10-12 00:00:00
2011-10
2011-10-01 00:00:00
2011
2011-01-01 00:00:00
10-12
nope
2011-10-12T11:45:30
2011-10-12 11:45:30
10-12 11:45
nope

nope

ただし、独自の形式を指定できます。例えば:

>>> from simpledate import SimpleDateParser, invert
>>> parser = SimpleDateParser(invert('Y-m-d(%T| )?(H:M(:S)?)?'))
>>> for date in ('2011-10-12', '2011-10', '2011', '10-12', '2011-10-12T11:45:30', '10-12 11:45', ''):
...   print(date)
...   try: print(SimpleDate(date, date_parser=parser).naive.datetime)
...   except: print('nope')
... 
2011-10-12
2011-10-12 00:00:00
2011-10
nope
2011
nope
10-12
nope
2011-10-12T11:45:30
2011-10-12 11:45:30
10-12 11:45
nope

nope

ps は、複雑な日付パターンを指定するときに実際の混乱になるinvert()存在を切り替えるだけです。%したがって、ここではリテラルT文字のみにプレフィックスが必要%です (標準の Python 日付形式では、プレフィックスのない唯一の英数字になります)

于 2013-08-14T20:40:03.657 に答える