74 lines
2.2 KiB
Python
74 lines
2.2 KiB
Python
|
# Copyright (c) 2014, Menno Smits
|
||
|
# Released subject to the New BSD License
|
||
|
# Please see http://en.wikipedia.org/wiki/BSD_licenses
|
||
|
|
||
|
from __future__ import unicode_literals
|
||
|
|
||
|
import re
|
||
|
from datetime import datetime
|
||
|
from email.utils import parsedate_tz
|
||
|
|
||
|
from .fixed_offset import FixedOffset
|
||
|
|
||
|
_SHORT_MONTHS = ' Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ')
|
||
|
|
||
|
|
||
|
def parse_to_datetime(timestamp, normalise=True):
|
||
|
"""Convert an IMAP datetime string to a datetime.
|
||
|
|
||
|
If normalise is True (the default), then the returned datetime
|
||
|
will be timezone-naive but adjusted to the local time.
|
||
|
|
||
|
If normalise is False, then the returned datetime will be
|
||
|
unadjusted but will contain timezone information as per the input.
|
||
|
"""
|
||
|
time_tuple = parsedate_tz(_munge(timestamp))
|
||
|
if time_tuple is None:
|
||
|
raise ValueError("couldn't parse datetime %r" % timestamp)
|
||
|
|
||
|
tz_offset_seconds = time_tuple[-1]
|
||
|
tz = None
|
||
|
if tz_offset_seconds is not None:
|
||
|
tz = FixedOffset(tz_offset_seconds / 60)
|
||
|
|
||
|
dt = datetime(*time_tuple[:6], tzinfo=tz)
|
||
|
if normalise and tz:
|
||
|
dt = datetime_to_native(dt)
|
||
|
|
||
|
return dt
|
||
|
|
||
|
|
||
|
def datetime_to_native(dt):
|
||
|
return dt.astimezone(FixedOffset.for_system()).replace(tzinfo=None)
|
||
|
|
||
|
|
||
|
def datetime_to_INTERNALDATE(dt):
|
||
|
"""Convert a datetime instance to a IMAP INTERNALDATE string.
|
||
|
|
||
|
If timezone information is missing the current system
|
||
|
timezone is used.
|
||
|
"""
|
||
|
if not dt.tzinfo:
|
||
|
dt = dt.replace(tzinfo=FixedOffset.for_system())
|
||
|
fmt = '%d-' + _SHORT_MONTHS[dt.month] + '-%Y %H:%M:%S %z'
|
||
|
return dt.strftime(fmt)
|
||
|
|
||
|
|
||
|
# Matches timestamp strings where the time separator is a dot (see
|
||
|
# issue #154). For example: 'Sat, 8 May 2010 16.03.09 +0200'
|
||
|
_rfc822_dotted_time = re.compile("\w+, ?\d{1,2} \w+ \d\d(\d\d)? \d\d?\.\d\d?\.\d\d?.*")
|
||
|
|
||
|
|
||
|
def _munge(s):
|
||
|
s = s.decode('latin-1') # parsedate_tz only works with strings
|
||
|
if _rfc822_dotted_time.match(s):
|
||
|
return s.replace(".", ":")
|
||
|
return s
|
||
|
|
||
|
|
||
|
def format_criteria_date(dt):
|
||
|
"""Format a date or datetime instance for use in IMAP search criteria.
|
||
|
"""
|
||
|
out = '%02d-%s-%d' % (dt.day, _SHORT_MONTHS[dt.month], dt.year)
|
||
|
return out.encode('ascii')
|