Index: trac-trunk/trac/ticket/api.py
===================================================================
--- trac-trunk/trac/ticket/api.py	(revision 5555)
+++ trac-trunk/trac/ticket/api.py	(working copy)
@@ -265,6 +265,11 @@
             field = {'name': name, 'type': 'text', 'label': name.title()}
             fields.append(field)
 
+        # Date/Time fields
+        for name in ('time', 'changetime'):
+            field = {'name': name, 'type': 'time', 'label': name.title()}
+            fields.append(field)
+
         for field in self.get_custom_fields():
             if field['name'] in [f['name'] for f in fields]:
                 self.log.warning('Duplicate field name "%s" (ignoring)',
Index: trac-trunk/trac/ticket/web_ui.py
===================================================================
--- trac-trunk/trac/ticket/web_ui.py	(revision 5555)
+++ trac-trunk/trac/ticket/web_ui.py	(working copy)
@@ -934,7 +934,7 @@
             field.setdefault('optional', False)
             field.setdefault('options', [])
             field['skip'] = name in ('summary', 'reporter', 'description',
-                                     'status', 'resolution', 'owner')
+                                     'status', 'resolution', 'owner', 'time', 'changetime')
             fields.append(field)
 
         data['author_id'] = author_id
Index: trac-trunk/trac/ticket/query.py
===================================================================
--- trac-trunk/trac/ticket/query.py	(revision 5555)
+++ trac-trunk/trac/ticket/query.py	(working copy)
@@ -64,6 +64,8 @@
             rows.append('description')
         self.fields = TicketSystem(self.env).get_ticket_fields()
         field_names = [f['name'] for f in self.fields]
+        #field_names.append('time')
+        #field_names.append('changetime')
         self.cols = [c for c in cols or [] if c in field_names or c == 'id']
         self.rows = [c for c in rows if c in field_names]
 
@@ -97,7 +99,7 @@
                 raise QuerySyntaxError('Query filter requires field name')
             # from last char of `field`, get the mode of comparison
             mode, neg = '', ''
-            if field[-1] in ('~', '^', '$'):
+            if field[-1] in ('~', '^', '$', '@', '#'):
                 mode = field[-1]
                 field = field[:-1]
             if field[-1] == '!':
@@ -136,18 +138,23 @@
         # Prepare the default list of columns
         cols = ['id']
         cols += [f['name'] for f in self.fields if f['type'] != 'textarea']
+        #cols.append('time')
+        #cols.append('changetime')
         for col in ('reporter', 'keywords', 'cc'):
             if col in cols:
                 cols.remove(col)
                 cols.append(col)
+        import sys
+        sys.stderr.write("columns: %r\n" % (cols, ))
 
         # Semi-intelligently remove columns that are restricted to a single
         # value by a query constraint.
         for col in [k for k in self.constraints.keys()
                     if k != 'id' and k in cols]:
             constraint = self.constraints[col]
+            sys.stderr.write("column %s has constraint %r\n" % (col, constraint))
             if len(constraint) == 1 and constraint[0] \
-                    and not constraint[0][0] in ('!', '~', '^', '$'):
+                    and not constraint[0][0] in ('!', '~', '^', '$', '@', '#'):
                 if col in cols:
                     cols.remove(col)
             if col == 'status' and not 'closed' in constraint \
@@ -156,6 +163,8 @@
         if self.group in cols:
             cols.remove(self.group)
 
+        sys.stderr.write("columns afer removal: %r\n" % (cols, ))
+
         def sort_columns(col1, col2):
             constrained_fields = self.constraints.keys()
             # Ticket ID is always the first column
@@ -169,6 +178,8 @@
                 return col1 in constrained_fields and -1 or 1
             return 0
         cols.sort(sort_columns)
+
+        sys.stderr.write("columns returned: %r\n" % (cols, ))
         return cols
 
     def get_default_columns(self):
@@ -308,9 +319,18 @@
                 name = name + '.value'
             value = value[len(mode) + neg:]
 
+            import sys
+            sys.stderr.write("eli: %s %s %s %s\n" % (name, value, mode, neg))
+
             if mode == '':
                 return ("COALESCE(%s,'')%s=%%s" % (name, neg and '!' or ''),
                         value)
+
+            if mode == '@':
+                return ("%s<=%%s" % name, value)
+            elif mode == '#':
+                return ("%s>=%%s" % name, value)
+                
             if not value:
                 return None
             db = self.env.get_db_cnx()
@@ -334,7 +354,7 @@
             # starts-with, negation, etc.)
             neg = v[0].startswith('!')
             mode = ''
-            if len(v[0]) > neg and v[0][neg] in ('~', '^', '$'):
+            if len(v[0]) > neg and v[0][neg] in ('~', '^', '$', '@', '#'):
                 mode = v[0][neg]
 
             # Special case id ranges
@@ -454,7 +474,7 @@
                 if neg:
                     val = val[1:]
                 mode = ''
-                if val[:1] in ('~', '^', '$'):
+                if val[:1] in ('~', '^', '$', '@', '#'):
                     mode, val = val[:1], val[1:]
                 constraint['mode'] = (neg and '!' or '') + mode
                 constraint['values'].append(val)
@@ -495,6 +515,11 @@
             {'name': "is", 'value': ""},
             {'name': "is not", 'value': "!"}
         ]
+        modes['time'] = [
+            {'name': "before", 'value': "@"},
+            {'name': "after", 'value': "#"},
+            #{'name': "on", 'value': ""},
+        ]
 
         groups = {}
         groupsequence = []
Index: trac-trunk/trac/ticket/templates/query.html
===================================================================
--- trac-trunk/trac/ticket/templates/query.html	(revision 5555)
+++ trac-trunk/trac/ticket/templates/query.html	(working copy)
@@ -105,6 +105,10 @@
                           <input type="text" name="${field_name}" value="$constraint_value" size="42" />
                         </py:when>
 
+                        <py:when test="'time'">
+                          <input type="text" name="${field_name}" value="$constraint_value" size="42" />
+                        </py:when>
+
                       </td>
                       <td class="actions"
                           py:with="rm_idx = multiline and constraint_idx or len(constraint['values']) - 1"><input
Index: trac-trunk/trac/htdocs/js/query.js
===================================================================
--- trac-trunk/trac/htdocs/js/query.js	(revision 5555)
+++ trac-trunk/trac/htdocs/js/query.js	(working copy)
@@ -224,6 +224,11 @@
         element.type = "text";
         element.name = propertyName;
         element.size = 42;
+      } else if (property.type == "time") {
+        var element = document.createElement("input");
+        element.type = "text";
+        element.name = propertyName;
+        element.size = 42;
       }
       td.appendChild(element);
       element.focus();

