have dates in URLs be partial timestamps - fix datetime-timezone-comparison mess
authorTero Marttila <terom@fixme.fi>
Thu, 12 Feb 2009 00:16:52 +0200
changeset 115 751e3fcd11d2
parent 114 d4848d807fd1
child 116 81da986f6ed5
have dates in URLs be partial timestamps - fix datetime-timezone-comparison mess
handlers.py
helpers.py
log_source.py
templates/channel_calendar.tmpl
utils.py
--- a/handlers.py	Wed Feb 11 23:38:05 2009 +0200
+++ b/handlers.py	Thu Feb 12 00:16:52 2009 +0200
@@ -61,6 +61,35 @@
     else :
         raise http.ResponseError("Unrecognized type: %r" % (type, ))
 
+def _render_date (request, channel, date, lines, type, count, page, max) :
+    """
+        Render the given LogLines as a http.Response for channel_date
+    """
+
+    # type?
+    if type :
+        # special type
+        return _render_type(request, channel, lines, type)
+    
+    else :
+        # format HTML
+        lines = request.prefs['formatter'].format_html(lines)
+
+        # render
+        return templates.render_to_response("channel_date",
+            req             = request,
+            prefs           = request.prefs,
+            channel         = channel,
+            date            = date,
+            count           = count,
+            page            = page,
+            max             = max,
+            lines           = lines,
+            
+            # for prev/next date
+            date_next       = channel.source.get_next_date(date),
+            date_prev       = channel.source.get_prev_date(date),
+        )
 
 def index (request) :
     """
@@ -143,31 +172,9 @@
 
     # get correct day's correct page of lines
     page, max, lines = channel.source.get_date_paged(timestamp, count)
-
-    # type?
-    if type :
-        # special type
-        return _render_type(request, channel, lines, type)
     
-    else :
-        # format HTML
-        lines = formatter.format_html(lines)
-
-        # render
-        return templates.render_to_response("channel_date",
-            req             = request,
-            prefs           = request.prefs,
-            channel         = channel,
-            date            = timestamp,
-            page            = page,
-            count           = count,
-            max             = max,
-            lines           = lines,
-            
-            # for prev/next date
-            date_next       = channel.source.get_next_date(timestamp),
-            date_prev       = channel.source.get_prev_date(timestamp),
-        )
+    # render channel_date
+    return _render_date (request, channel, timestamp, lines, type, count, page, max)
 
 @preferences.handler(prefs.timezone)
 def channel_calendar (request, channel, year, month, timezone) :
@@ -193,14 +200,14 @@
         month           = target,
     )
 
-@preferences.handler(prefs.formatter, prefs.timezone, prefs.count)
-def channel_date (request, channel, date, formatter, timezone, count, page=1, type=None) :
+@preferences.handler(prefs.count)
+def channel_date (request, channel, date, count, page=1, type=None) :
     """
         Display all log data for the given date
     """
 
-    # fix date timezone
-    date = timezone.localize(date)
+#    print
+#    print "channel_date: date=%s" % date
 
     # get that day's events, either paged or not
     if page :
@@ -210,30 +217,8 @@
         lines = channel.source.get_date(date)
         max = None
 
-    # type?
-    if type :
-        # special type
-        return _render_type(request, channel, lines, type)
-    
-    else :
-        # lines
-        lines = formatter.format_html(lines)
-
-        # render page
-        return templates.render_to_response("channel_date",
-            req             = request,
-            prefs           = request.prefs,
-            channel         = channel,
-            date            = date,
-            page            = page,
-            count           = count,
-            max             = max,
-            lines           = lines,
-
-            # for prev/next date
-            date_next       = channel.source.get_next_date(date),
-            date_prev       = channel.source.get_prev_date(date),
-        )
+    # render channel_date
+    return _render_date (request, channel, date, lines, type, count, page, max)
 
 @preferences.handler(prefs.formatter, prefs.count)
 def channel_search (request, channel, formatter, count, q=None, page=1, max=1, type=None) :
--- a/helpers.py	Wed Feb 11 23:38:05 2009 +0200
+++ b/helpers.py	Thu Feb 12 00:16:52 2009 +0200
@@ -46,10 +46,10 @@
 
     def build_date (self, month, mday) :
         """
-            Returns a datetime.date for the given (month.year, month.month, mday)
+            Returns a datetime.datetime for the given (month.year, month.month, mday)
         """
 
-        return datetime.date(month.year, month.month, mday)
+        return self.ctx['prefs'][preferences.timezone].localize(datetime.datetime(month.year, month.month, mday))
     
     def now (self) :
         """
@@ -65,13 +65,13 @@
         
         return self.now().date()
 
-    def is_today (self, date) :
+    def is_today (self, dt) :
         """
-            Checks if the given date is today
+            Checks if the given datetime.datetime is today
         """
 
-        # construct current date
-        return date == self.today()
+        # compare with current date
+        return dt.date() == self.today()
     
     def is_this_month (self, month) :
         """
--- a/log_source.py	Wed Feb 11 23:38:05 2009 +0200
+++ b/log_source.py	Thu Feb 12 00:16:52 2009 +0200
@@ -384,18 +384,7 @@
         self.decoder = decoder
         self.filename_fmt = filename_fmt
 
-    def _get_logfile_datetime (self, dt) :
-        """
-            Get the logfile corresponding to the given datetime
-        """
-
-        # convert to target timezone
-        dtz = dt.astimezone(self.tz)
-        
-        # convert to date and use that
-        return self._get_logfile_date(dtz.date())
-
-    def _get_logfile_date (self, d, load=True, mtime=False, ignore_missing=True) :
+    def _get_logfile_date (self, d, load=True, mtime=False, ignore_missing=False) :
         """
             Get the logfile corresponding to the given naive date in our timezone. 
             
@@ -443,6 +432,13 @@
                 reverse the dates are returned in reverse order instead. Note that the meaning of after/until doesn't change
         """
 
+        # convert timestamps to our timezone's dates
+        if after :
+            after = after.astimezone(self.tz).date()
+
+        if until :
+            until = until.astimezone(self.tz).date()
+
         # listdir
         filenames = os.listdir(self.path)
 
@@ -453,7 +449,8 @@
         for filename in filenames :
             try :
                 # parse date
-                date = self.tz.localize(datetime.datetime.strptime(filename, self.filename_fmt))
+                dt = self.tz.localize(datetime.datetime.strptime(filename, self.filename_fmt))
+                date = dt.date()
             
             except :
                 # ignore
@@ -465,11 +462,8 @@
                     continue
                 
                 else :
-#                    print
-#                    print "iter_logfile_dates: after=%s, until=%s, reverse=%s -> %s" % (after, until, reverse, date)
-
                     # yield
-                    yield date
+                    yield dt
             
     def _iter_date_reverse (self, dt=None) :
         """
@@ -517,7 +511,7 @@
             logfile = None
             
             file_count += 1
-            logfile = self._get_logfile_date(day)
+            logfile = self._get_logfile_date(day, ignore_missing=True)
             
             # no logfile there?
             if not logfile :
@@ -586,18 +580,15 @@
             # open that log
             logfile = self._get_logfile_date(d_begin)
             
-            if not logfile :
-                raise Exception("No logfile for date=%r" % (dt, ))
-            
             # return the full data
             return logfile.read_full()
         
         # otherwise, we need to pull two partial logs
         else :
-            # open both of them
+            # open both of them, but it's okay if we don't have the second one
             f_begin = self._get_logfile_date(d_begin)
-            f_end = self._get_logfile_date(d_end)
-            
+            f_end = self._get_logfile_date(d_end, ignore_missing=True)
+
             # chain together the two sources
             return itertools.chain(
                 f_begin.read_from(dtz_begin), 
@@ -634,7 +625,7 @@
             log_date = dt.astimezone(self.tz).date()
             
             # test for it
-            if self._get_logfile_date(log_date, load=False) :
+            if self._get_logfile_date(log_date, load=False, ignore_missing=True) :
                 # valid
                 yield dt.date()
 
@@ -648,7 +639,7 @@
             # compare against dt?
             if dt :
                 # stat
-                mtime = self._get_logfile_date(log_date, load=False, mtime=True)
+                mtime = self._get_logfile_date(log_date, load=False, mtime=True, ignore_missing=True)
                 
                 # not modified?
                 if mtime < dt :
@@ -656,7 +647,7 @@
                     continue
                 
             # open
-            logfile = self._get_logfile_date(log_date, ignore_missing=False)
+            logfile = self._get_logfile_date(log_date)
 
             # yield all lines
             for line in logfile.read_full() :
--- a/templates/channel_calendar.tmpl	Wed Feb 11 23:38:05 2009 +0200
+++ b/templates/channel_calendar.tmpl	Thu Feb 12 00:16:52 2009 +0200
@@ -44,9 +44,9 @@
         ## build date
         <% date = h.build_date(month, day) %>\
         ## render cell
-        <td${' id="today"' if h.is_today(date) else ''}${' class="empty"' if date not in log_dates else ''}>\
+        <td${' id="today"' if h.is_today(date) else ''}${' class="empty"' if date.date() not in log_dates else ''}>\
         ## link to logs for this day?
-        % if date in log_dates :
+        % if date.date() in log_dates :
 <a href="${urls.channel_date.build(req, channel=channel, date=date)}">${day}</a>\
         % else :
 ${day}\
--- a/utils.py	Wed Feb 11 23:38:05 2009 +0200
+++ b/utils.py	Thu Feb 12 00:16:52 2009 +0200
@@ -35,9 +35,12 @@
 
 class URLDateType (URLType) :
     """
-        Handle dates in URLs as naive datetime objects (with indeterminate time info)
+        Handle dates in URLs as shortened UTC datetime timestamps
     """
 
+    # percision = hour
+    SCALE = 60 * 60
+
     def __init__ (self, date_fmt) :
         """
             Format/parse dates using the given format
@@ -45,19 +48,27 @@
 
         self.date_fmt = date_fmt
     
-    def parse (self, date_str) :
+    def parse (self, sts_str) :
         """
-            date_str -> naive datetime.datetime
+            short_timestamp_str -> datetime.datetime
         """
         
-        return datetime.datetime.strptime(date_str, self.date_fmt)
+        # scale -> from_utc + return
+        return from_utc_timestamp(int(sts_str) * self.SCALE)
     
-    def build (self, date) :
+    def build (self, dtz) :
         """
-            datetime.date -> date_str
+            datetime.datetime -> short_timestamp_str
         """
 
-        return date.strftime(self.date_fmt)
+        # force it to be interpreted as UTC
+        dt_utc = dtz.replace(tzinfo=pytz.utc)
+        
+        # scale the UTC timestamp
+        sts = to_utc_timestamp(dt_utc) / self.SCALE
+        
+        # str
+        return str(sts)
 
 class URLTimestampType (URLType) :
     """