Skip to content

Commit

Permalink
Merge pull request #5 from sbuss/sysdate
Browse files Browse the repository at this point in the history
Sysdate parsing
  • Loading branch information
sbuss committed Dec 26, 2012
2 parents e66b4a4 + 7af1d5b commit 3486147
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 51 deletions.
28 changes: 21 additions & 7 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,11 @@ def test_from_local_time(self):

def test_sys_date(self):
#TODO: Make this test pass
"""s = "Mon Dec 10 23:31:50 EST 2012"
ts_pst = datetime(2012, 12, 10, 23, 31, 50)
s = "Mon Dec 10 23:31:50 EST 2012"
ts_pst = datetime(2012, 12, 10, 20, 31, 50)
self.assertEqual(ts_pst, wtftz.convert(s, 'pst'))
ts_utc = datetime(2012, 12, 11, 7, 31, 50)
self.assertEqual(ts_utc, wtftz.convert(s, 'utc'))"""
ts_utc = datetime(2012, 12, 11, 4, 31, 50)
self.assertEqual(ts_utc, wtftz.convert(s, 'utc'))

def test_naive(self):
ts = datetime.utcnow()
Expand Down Expand Up @@ -223,13 +223,19 @@ def test_naive(self):
self.assertFalse(converted.tzinfo is None)
self.assertEqual(converted.tzinfo, pytz.timezone("US/Pacific"))

def test_sysdate_free(self):
s = "Mon Dec 10 23:31:50 EST 2012"
ts_pst = datetime(2012, 12, 10, 20, 31, 50)
query = "%s to pst" % s
self.assertEqual(ts_pst, wtftz.convert_free(query))

def test_sysdate_tz_doesnt_match(self):
# TODO: Make this test pass
"""s = "Mon Dec 10 23:31:50 EST 2012"
target = datetime(2012, 12, 10, 23, 31, 50)
s = "Mon Dec 10 23:31:50 EST 2012"
target = datetime(2012, 12, 10, 20, 31, 50)
query = "{ts} from utc to pst".format(ts=s)
result = wtftz.convert_free(query)
self.assertEqual(result, target)"""
self.assertEqual(result, target)

def _test_extraction(self, query, ts, fromz, toz):
_ts, _fromz, _toz = free_text(query)
Expand Down Expand Up @@ -276,6 +282,14 @@ def test_extraction_no_from(self):
self._test_extraction(
query, self.est_ts_str, None, "US/Pacific")

def test_extraction_no_from_keyword(self):
query_template = "{ts} {fromz} to {toz}"
query = query_template.format(ts=self.est_ts_str,
fromz="EST",
toz="US/Pacific")
self._test_extraction(
query, self.est_ts_str, "EST", "US/Pacific")


class TestTimesWithoutDates(TestCase):
def test_simple_bare_times(self):
Expand Down
2 changes: 1 addition & 1 deletion wtftz/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.2.3"
__version__ = "0.2.4"
31 changes: 25 additions & 6 deletions wtftz/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .timezones import common_timezones
from .parser import free_text
from .parser import _from


def convert(timestamp, to_tz="utc", from_tz="utc", naive=True):
Expand Down Expand Up @@ -33,8 +34,8 @@ def convert(timestamp, to_tz="utc", from_tz="utc", naive=True):
to_tz = "utc"
if not from_tz:
from_tz = "utc"
from_timezone = common_tz_name_to_real_tz(from_tz)
to_timezone = common_tz_name_to_real_tz(to_tz)
from_timezone = common_tz_name_to_real_tz(from_tz) or pytz.UTC
to_timezone = common_tz_name_to_real_tz(to_tz) or pytz.UTC
timestamp = parse_timestamp(timestamp)
if not hasattr(timestamp, 'tzinfo') or timestamp.tzinfo is None:
timestamp = from_timezone.localize(timestamp)
Expand Down Expand Up @@ -67,7 +68,7 @@ def common_tz_name_to_real_tz(name):
Args:
name: The name of the timezone. eg "est" or "US/Eastern"
Returns a tzinfo.
Returns a tzinfo or None, if the given name is unknown.
"""
if isinstance(name, datetime.tzinfo):
return name
Expand All @@ -78,7 +79,7 @@ def common_tz_name_to_real_tz(name):
return pytz.timezone(name)
except Exception:
pass
return pytz.UTC
return None


def parse_timestamp(timestamp):
Expand All @@ -89,6 +90,7 @@ def parse_timestamp(timestamp):
isoformat, and anything python-dateutil can handle.
Returns a timestamp.
"""
orig_timestamp = timestamp
if isinstance(timestamp, datetime.datetime) or \
isinstance(timestamp, datetime.time):
return timestamp
Expand All @@ -104,9 +106,26 @@ def parse_timestamp(timestamp):
pass

timestamp = str(timestamp)
# We might have a weird timestamp string with a timezone
# eg: "Mon Dec 10 23:31:50 EST 2012"
fromz = None
try:
return date_parser.parse(timestamp)
free_ts, free_from = _from(timestamp)
if free_from and common_tz_name_to_real_tz(free_from):
fromz = common_tz_name_to_real_tz(free_from)
timestamp = free_ts
except Exception:
pass

raise ValueError("Cannot parse timestamp {ts}".format(ts=timestamp))
try:
parsed_date = date_parser.parse(timestamp)
if fromz:
if hasattr(parsed_date, 'tzinfo') and parsed_date.tzinfo:
parsed_date.replace(tzinfo=fromz)
else:
parsed_date = fromz.localize(parsed_date)
return parsed_date
except Exception:
pass

raise ValueError("Cannot parse timestamp {ts}".format(ts=orig_timestamp))
100 changes: 63 additions & 37 deletions wtftz/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,88 @@ def free_text(query):
Ex:
>>> free_text("2012-12-23T14:23:03.826437-05:00 to pst")
('2012-12-23T14:23:03.826437-05:00', None, 'pst')
>>> free_text("2012-12-23T14:23:03.826437 from to pst")
('2012-12-23T14:23:03.826437-05:00', 'est', 'pst')
>>> free_text("Mon Dec 10 23:31:50 EST 2012 to UTC")
("Mon Dec 10 23:31:50 2012", "EST", "UTC")
"""
try:
return _from_to(query)
except Exception:
pass
query, toz = _to(query)
query, fromz = _from(query)
return (query, fromz, toz)

try:
return _to(query)
except Exception:
pass

raise MismatchException("%s does not match a known pattern" % query)
def _to(query):
"""Try to parse a `to` timezone from the query.
Args:
query - A string with a time, and a source and destination timezone.
Returns a tuple (query, to_timezone_string) parsed from the query. The
query will be have the `to` timezone removed. If no timezone is found,
return (query, None).
>>> _to("2012-12-23T14:23:03.826437 to pst")
('2012-12-23T14:23:03.826437', 'pst')
"""
pattern = re.compile(r"to ?(.*)")
matches = pattern.findall(query)
if len(matches) > 0:
toz = matches[0]
query = pattern.sub("", query, 1)
return (query.strip(), toz.strip())
else:
raise MismatchException()


def _from_to(query):
"""Try to parse a `from` and `to` timezone from the query.
def _from(query):
"""Extract the `from ...` part of a query.
Args:
query - A string with a time, and a source and destination timezone.
Returns a triplet (timestamp, from_timezone, to_timezone) parsed from
the query.
Returns a tuple (query, from_timezone_string) parsed from the query. The
query will be have the from timezone removed. If no timezone is found,
return (query, None).
>>> _from_to("2012-12-23T14:23:03.826437 from est to pst")
('2012-12-23T14:23:03.826437', 'est', 'pst')
>>> _from("2012-12-23T14:23:03.826437 from est")
('2012-12-23T14:23:03.826437', 'est')
If no `from` keyword is in this query, we will attempt to find a suitable
timezone in the query string.
"""
template = re.compile(r"(.*)from(.*)to(.*)")
matches = template.findall(query)
if len(matches) == 1:
match = matches[0]
ts = match[0].strip()
fromz = match[1].strip()
toz = match[2].strip()
return (ts, fromz, toz)
raise MismatchException()
pattern = re.compile(r"from ([A-Za-z/ ]+)")
matches = pattern.findall(query)
if len(matches) > 0:
fromz = matches[0]
query = pattern.sub("", query, 1)
return (query.strip(), fromz.strip())

return _implicit_from(query)

def _to(query):
"""Try to parse a `to` timezone from the query.

def _implicit_from(query):
"""There *is* a source timezone in the query, but no from keyword.
Args:
query - A string with a time, and a source and destination timezone.
Returns a triplet (timestamp, from_timezone, to_timezone) parsed from
the query.
>>> _to("2012-12-23T14:23:03.826437 to pst")
('2012-12-23T14:23:03.826437', None, 'pst')
>>> _implicit_from("2012-12-23T14:23:03.826437 est")
('2012-12-23T14:23:03.826437', 'est')
>>> _implicit_from("Mon Dec 10 23:31:50 EST 2012")
("Mon Dec 10 23:31:50 2012", "EST")
"""
template = re.compile(r"(.*)to(.*)")
fromz = None
matches = template.findall(query)
if len(matches) == 1:
match = matches[0]
ts = match[0].strip()
toz = match[1].strip()
return (ts, fromz, toz)
raise MismatchException()
pattern = re.compile(r"^([A-Za-z/]+)$")
tokens = query.split(" ")
for count, token in enumerate(tokens[::-1]):
matches = pattern.findall(token)
if len(matches) > 0:
fromz = matches[0]
if len(fromz) <= 1:
continue
query = " ".join(tokens[0:len(tokens) - count - 1] +
tokens[len(tokens) - count:])
return (query.strip(), fromz.strip())
return (query.strip(), None)


class MismatchException(Exception):
Expand Down

0 comments on commit 3486147

Please sign in to comment.