diff --git a/gracedb/nltime.py b/gracedb/nltime.py
new file mode 100755
index 0000000000000000000000000000000000000000..b6ce811d085da39bb04f0a51404f52d777da17fe
--- /dev/null
+++ b/gracedb/nltime.py
@@ -0,0 +1,191 @@
+#!/usr/bin/python
+
+# Taken from
+# http://pyparsing.wikispaces.com/UnderDevelopment
+
+from datetime import datetime, timedelta
+from pyparsing import *
+import calendar
+ 
+# string conversion parse actions
+def convertToTimedelta(toks):
+    unit = toks.timeunit.lower().rstrip("s")
+    td = {
+        'month'  : timedelta(30),
+        '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,basestring):
+            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")))
+month, week, day, hour, minute, second = map( plural,
+    "month 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))
+ 
+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:])] ))
+qty = integer | couple | a_qty
+dayName = oneOf( list(calendar.day_name) )
+ 
+dayOffset = (qty("qty") + (month | 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)
+ 
+hourMinuteOrSecond = (hour | minute | second)
+ 
+timespec = Group(int4("miltime") |
+                 integer("HH") + 
+                 Optional(COLON + integer("MM")) + 
+                 Optional(COLON + integer("SS")) + (am | pm)("ampm")
+                 )
+absTimeSpec = ((noon | midnight | now | timespec("timeparts"))("timeOfDay") + 
+                Optional(dayRef)("dayRef"))
+absTimeSpec.setParseAction(convertToAbsTime,calculateTime)
+ 
+relTimeSpec = qty("qty") + hourMinuteOrSecond("timeunit") + \
+                (from_ | before | after)("dir") + \
+                absTimeSpec("absTime") | \
+              qty("qty") + hourMinuteOrSecond("timeunit") + ago("dir") | \
+              in_ + qty("qty") + hourMinuteOrSecond("timeunit")
+relTimeSpec.setParseAction(convertToTimedelta,calculateTime)
+ 
+nlTimeExpression = (absTimeSpec | dayTimeSpec | relTimeSpec)
+ 
+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
+    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
+    2009/12/22 12:13:14""".splitlines()
+     
+    for t in tests:
+        print t, "(relative to %s)" % datetime.now()
+        res = nlTimeExpression.parseString(t)
+        if "calculatedTime" in res:
+            print res.calculatedTime
+        else:
+            print "???"
+        print
+     
diff --git a/gracedb/query.py b/gracedb/query.py
index 793613c5a4002762e610a60a3a544ece825579c0..359702db580e80ba38fd519dc6e002fcf1cce21a 100644
--- a/gracedb/query.py
+++ b/gracedb/query.py
@@ -11,6 +11,10 @@
 
 #import pyparsing as p
 
+# (weak) natural language time parsing.
+from nltime import nlTimeExpression as nltime_
+nltime = nltime_.setParseAction(lambda toks: toks["calculatedTime"])
+
 import models
 from django.db.models import Q
 
@@ -80,6 +84,11 @@ hidRange = hid + Suppress("..") + hid
 hidQ = Optional(Suppress(Keyword("hid:"))) + (hid^hidRange)
 hidQ = hidQ.setParseAction(maybeRange("hid", dbname="id"))
 
+# Created times
+nltimeRange = nltime + Suppress("..") + nltime
+createdQ = Optional(Suppress(Keyword("created:"))) + (nltime^nltimeRange)
+createdQ = createdQ.setParseAction(maybeRange("created"))
+
 
 # Labels
 labelNames = ["DQV", "INJ", "LUMIN_NO", "LUMIN_GO", "SWIFT_NO", "SWIFT_GO"]
@@ -105,7 +114,7 @@ dateQ = (Optional(Suppress(Keyword("date:"))) + dateTime).\
         setParseAction(doDate)
 
 
-q = (gidQ | hidQ | atypeQ | groupQ | gpsQ | labelQ ).setName("query term")
+q = (gidQ | hidQ | atypeQ | groupQ | gpsQ | labelQ | createdQ).setName("query term")
 
 def parseQuery(s):
     d={}