summaryrefslogtreecommitdiff
path: root/trunk/src/examples/deltaTime.py
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/examples/deltaTime.py')
-rw-r--r--trunk/src/examples/deltaTime.py208
1 files changed, 208 insertions, 0 deletions
diff --git a/trunk/src/examples/deltaTime.py b/trunk/src/examples/deltaTime.py
new file mode 100644
index 0000000..e38da00
--- /dev/null
+++ b/trunk/src/examples/deltaTime.py
@@ -0,0 +1,208 @@
+# deltaTime.py
+#
+# Parser to convert a conversational time reference such as "in a minute" or
+# "noon tomorrow" and convert it to a Python datetime. The returned
+# ParseResults object contains the results name "timeOffset" containing
+# the timedelta, and "calculatedTime" containing the computed time relative
+# to datetime.now().
+#
+# Copyright 2010, by Paul McGuire
+#
+
+from datetime import datetime, timedelta
+from pyparsing import *
+import calendar
+
+__all__ = ["nlTimeExpression"]
+
+# string conversion parse actions
+def convertToTimedelta(toks):
+ unit = toks.timeunit.lower().rstrip("s")
+ td = {
+ 'week' : timedelta(7),
+ 'day' : timedelta(1),
+ 'hour' : timedelta(0,0,0,0,0,1),
+ 'minute' : timedelta(0,0,0,0,1),
+ 'second' : timedelta(0,1),
+ }[unit]
+ if toks.qty:
+ td *= int(toks.qty)
+ if toks.dir:
+ td *= toks.dir
+ toks["timeOffset"] = td
+
+def convertToDay(toks):
+ now = datetime.now()
+ if "wkdayRef" in toks:
+ todaynum = now.weekday()
+ daynames = [n.lower() for n in calendar.day_name]
+ nameddaynum = daynames.index(toks.wkdayRef.day.lower())
+ if toks.wkdayRef.dir > 0:
+ daydiff = (nameddaynum + 7 - todaynum) % 7
+ else:
+ daydiff = -((todaynum + 7 - nameddaynum) % 7)
+ toks["absTime"] = datetime(now.year, now.month, now.day)+timedelta(daydiff)
+ else:
+ name = toks.name.lower()
+ toks["absTime"] = {
+ "now" : now,
+ "today" : datetime(now.year, now.month, now.day),
+ "yesterday" : datetime(now.year, now.month, now.day)+timedelta(-1),
+ "tomorrow" : datetime(now.year, now.month, now.day)+timedelta(+1),
+ }[name]
+
+def convertToAbsTime(toks):
+ now = datetime.now()
+ if "dayRef" in toks:
+ day = toks.dayRef.absTime
+ day = datetime(day.year, day.month, day.day)
+ else:
+ day = datetime(now.year, now.month, now.day)
+ if "timeOfDay" in toks:
+ if isinstance(toks.timeOfDay,str):
+ timeOfDay = {
+ "now" : timedelta(0, (now.hour*60+now.minute)*60+now.second, now.microsecond),
+ "noon" : timedelta(0,0,0,0,0,12),
+ "midnight" : timedelta(),
+ }[toks.timeOfDay]
+ else:
+ hhmmss = toks.timeparts
+ if hhmmss.miltime:
+ hh,mm = hhmmss.miltime
+ ss = 0
+ else:
+ hh,mm,ss = (hhmmss.HH % 12), hhmmss.MM, hhmmss.SS
+ if not mm: mm = 0
+ if not ss: ss = 0
+ if toks.timeOfDay.ampm == 'pm':
+ hh += 12
+ timeOfDay = timedelta(0, (hh*60+mm)*60+ss, 0)
+ else:
+ timeOfDay = timedelta(0, (now.hour*60+now.minute)*60+now.second, now.microsecond)
+ toks["absTime"] = day + timeOfDay
+
+def calculateTime(toks):
+ if toks.absTime:
+ absTime = toks.absTime
+ else:
+ absTime = datetime.now()
+ if toks.timeOffset:
+ absTime += toks.timeOffset
+ toks["calculatedTime"] = absTime
+
+# grammar definitions
+CL = CaselessLiteral
+today, tomorrow, yesterday, noon, midnight, now = map( CL,
+ "today tomorrow yesterday noon midnight now".split())
+plural = lambda s : Combine(CL(s) + Optional(CL("s")))
+week, day, hour, minute, second = map( plural,
+ "week day hour minute second".split())
+am = CL("am")
+pm = CL("pm")
+COLON = Suppress(':')
+
+# are these actually operators?
+in_ = CL("in").setParseAction(replaceWith(1))
+from_ = CL("from").setParseAction(replaceWith(1))
+before = CL("before").setParseAction(replaceWith(-1))
+after = CL("after").setParseAction(replaceWith(1))
+ago = CL("ago").setParseAction(replaceWith(-1))
+next_ = CL("next").setParseAction(replaceWith(1))
+last_ = CL("last").setParseAction(replaceWith(-1))
+at_ = CL("at")
+on_ = CL("on")
+
+couple = (Optional(CL("a")) + CL("couple") + Optional(CL("of"))).setParseAction(replaceWith(2))
+a_qty = CL("a").setParseAction(replaceWith(1))
+integer = Word(nums).setParseAction(lambda t:int(t[0]))
+int4 = Group(Word(nums,exact=4).setParseAction(lambda t: [int(t[0][:2]),int(t[0][2:])] ))
+def fill_timefields(t):
+ t[0]['HH'] = t[0][0]
+ t[0]['MM'] = t[0][1]
+ t[0]['ampm'] = ('am','pm')[t[0].HH >= 12]
+int4.addParseAction(fill_timefields)
+qty = integer | couple | a_qty
+dayName = oneOf( list(calendar.day_name) )
+
+dayOffset = (qty("qty") + (week | day)("timeunit"))
+dayFwdBack = (from_ + now.suppress() | ago)("dir")
+weekdayRef = (Optional(next_ | last_,1)("dir") + dayName("day"))
+dayRef = Optional( (dayOffset + (before | after | from_)("dir") ).setParseAction(convertToTimedelta) ) + \
+ ((yesterday | today | tomorrow)("name")|
+ weekdayRef("wkdayRef")).setParseAction(convertToDay)
+todayRef = (dayOffset + dayFwdBack).setParseAction(convertToTimedelta) | \
+ (in_("dir") + qty("qty") + day("timeunit")).setParseAction(convertToTimedelta)
+
+dayTimeSpec = dayRef | todayRef
+dayTimeSpec.setParseAction(calculateTime)
+
+relativeTimeUnit = (week | day | hour | minute | second)
+
+timespec = Group(ungroup(int4) |
+ integer("HH") +
+ ungroup(Optional(COLON + integer,[0]))("MM") +
+ ungroup(Optional(COLON + integer,[0]))("SS") +
+ (am | pm)("ampm")
+ )
+
+absTimeSpec = ((noon | midnight | now | timespec("timeparts"))("timeOfDay") +
+ Optional(on_) + Optional(dayRef)("dayRef") |
+ dayRef("dayRef") + at_ +
+ (noon | midnight | now | timespec("timeparts"))("timeOfDay"))
+absTimeSpec.setParseAction(convertToAbsTime,calculateTime)
+
+relTimeSpec = qty("qty") + relativeTimeUnit("timeunit") + \
+ (from_ | before | after)("dir") + \
+ Optional(at_) + \
+ absTimeSpec("absTime") | \
+ qty("qty") + relativeTimeUnit("timeunit") + ago("dir") | \
+ in_ + qty("qty") + relativeTimeUnit("timeunit")
+relTimeSpec.setParseAction(convertToTimedelta,calculateTime)
+
+nlTimeExpression = (absTimeSpec + Optional(dayTimeSpec) |
+ dayTimeSpec + Optional(Optional(at_) + absTimeSpec) |
+ relTimeSpec + Optional(absTimeSpec))
+
+if __name__ == "__main__":
+ # test grammar
+ tests = """\
+ today
+ tomorrow
+ yesterday
+ in a couple of days
+ a couple of days from now
+ a couple of days from today
+ in a day
+ 3 days ago
+ 3 days from now
+ a day ago
+ in 2 weeks
+ in 3 days at 5pm
+ now
+ 10 minutes ago
+ 10 minutes from now
+ in 10 minutes
+ in a minute
+ in a couple of minutes
+ 20 seconds ago
+ in 30 seconds
+ 20 seconds before noon
+ 20 seconds before noon tomorrow
+ noon
+ midnight
+ noon tomorrow
+ 6am tomorrow
+ 0800 yesterday
+ 12:15 AM today
+ 3pm 2 days from today
+ a week from today
+ a week from now
+ 3 weeks ago
+ noon next Sunday
+ noon Sunday
+ noon last Sunday
+ 2pm next Sunday
+ next Sunday at 2pm"""
+
+ print("(relative to %s)" % datetime.now())
+ nlTimeExpression.runTests(tests)