function handleEvent(obj, event, func) {
    try {
        obj.addEventListener(event, func, false);
    } catch (e) {
        if (typeof eval("obj.on"+event) == "function") {
            var existing = obj['on'+event];
            obj['on'+event] = function () { existing(); func(); };
        } else {
            obj['on'+event] = func;                        
        }
    }
}   

/* PerfectTime */
    function PerfectTime() {
        /* 
            Original implimentation by Why The Lucky Stiff
            <http://whytheluckystiff.net/>, described at:
            
            http://redhanded.hobix.com/inspect/showingPerfectTime.html
            
            Modified to fit in a single, unobtrusive javascript
            class by Mike West <http://mikewest.org/>
            
            I'm not sure what the original license chosen for this
            code was.  I'm assuming it's liberal enough, and this 
            class is released under the same license, whatever that
            turns out to be.
        */
            
        var self = this;
        
        self.defaultFormat = '<nobr>%d %b %Y</nobr> at <nobr>%H:%M</nobr>';
        self.format = (arguments[0])?arguments[0]:self.defaultFormat;
        self.isoRegEx = /(\d{4})(-?(\d{2})(-?(\d{2})(T(\d{2}):?(\d{2})(:?(\d{2})([.]?(\d+))?)?(Z|(([+-])(\d{2}):?(\d{2}))?)?)?)?)?/;
        
        self.parseISO = function (isoString) {
            // Parse ISO 8601 type times (e.g. hCalendar)
            //     based on Paul Sowden's method, tweaked to match up 
            //     with 'real world' hCalendar usage:
            //
            //         http://delete.me.uk/2005/03/iso8601.html
            //

            
            var d       = isoString.match(self.isoRegEx);
            
            var theDate = new Date(d[1], 0, 1);
            
            // <month> - 1:  Because JS months are 0-11
            if (d[ 3]) { theDate.setMonth(  d[ 3] - 1); }
            if (d[ 5]) { theDate.setDate(   d[ 5]); }
            if (d[ 7]) { theDate.setHours(  d[ 7]); }
            if (d[ 8]) { theDate.setMinutes(d[ 8]); }
            if (d[10]) { theDate.setSeconds(d[10]); }
            // Must be between 0 and 999), using Paul Sowden's method: http://delete.me.uk/2005/03/iso8601.html
            if (d[12]) { theDate.setMilliseconds(Number("0." + d[12]) * 1000); }
            var offset = 0;
            if (d[15]) {
                var offset = (Number(d[16])*60 + Number(d[17])) * 60;
                if (d[15] == "+") { offset *= -1; }
            }
            
     
            offset -= theDate.getTimezoneOffset() * 60;
            theDate.setTime(Number(theDate) + (offset * 1000));
         
            return theDate;
        }
        
        self.strftime_funks = {
            zeropad: 
                    function( n ){ return n>9 ? n : '0'+n; },
            a:      function(t) { return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][t.getDay()] },
            A:      function(t) { return ['Sunday','Monday','Tuedsay','Wednesday','Thursday','Friday','Saturday'][t.getDay()] },
            b:      function(t) { return ['Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'][t.getMonth()] },
            B:      function(t) { return ['January','February','March','April','May','June', 'July','August', 'September','October','November','December'][t.getMonth()] },
            c:      function(t) { return t.toString() },
            d:      function(t) { return t.getDate() },
            H:      function(t) { return t.getHours() },
            I:      function(t) { return (t.getHours() + 12) % 12 },
            m:      function(t) { return this.zeropad(t.getMonth()+1) }, // month-1
            M:      function(t) { return this.zeropad(t.getMinutes()) },
            p:      function(t) { return this.H(t) < 12 ? 'am' : 'pm'; },
            S:      function(t) { return this.zeropad(t.getSeconds()) },
            w:      function(t) { return t.getDay() }, // 0..6 == sun..sat
            y:      function(t) { return this.zeropad(this.Y(t) % 100); },
            Y:      function(t) { return t.getFullYear() },
            Z:      function(t) { 
                        if (t.getTimezoneOffset() > 0) {
                            return "-" + this.zeropad(t.getTimezoneOffset()/60) + "00";
                        } else {
                            return "+" + this.zeropad(Math.abs(t.getTimezoneOffset())/60) + "00";
                        }
                    },    
            '%':    function(t) { return '%' }
        }
        self.strftime = function (theDate) {
            var fmt = self.format;
            for (var s in self.strftime_funks) {
                if (s.length == 1) {
                    fmt = fmt.replace('%' + s, self.strftime_funks[s](theDate));
                }
            }
            return fmt;
        }
        
        
        self.instantiate = function () {
            // Spans by old method
            var spans = document.getElementsByTagName('span');
            for (i=0, numSpans=spans.length; i < numSpans; i++) {
                if (spans[i].className.match(/JS_Replace_GMT_To_User_Time/)) {
                    self.processSpan(spans[i]);
                }
            }
            
            // ABBRs by new method
            var abbrs = document.getElementsByTagName('abbr');
            for (i=0, numAbbrs=abbrs.length; i < numAbbrs; i++) {
                if (abbrs[i].className.match(/PerfectTime/)) {
                    self.processAbbr(abbrs[i]);
                }
            }            
        }
        
        self.processSpan = function (theSpan) {
            var GMT_Str = theSpan.className.split(" ");
            //var GMT = parseInt(theSpan.getAttribute('gmt_time')) * 1000;
            var GMT = parseInt(GMT_Str[1]) * 1000;
            var newDate = new Date(GMT);
            
            self.format = '';
            var i = 3;
            if (GMT_Str[2] == 'offset_day')
            {
              var date = new Date();
              var offset = date.getTimezoneOffset() / 60;
              if ( newDate.getHours() + offset < 0 || newDate.getHours() + offset > 24 )
                self.format = self.format = '%a '; 

              i = 4;
            }
            
            self.format += GMT_Str[i-1];
           
            while (i < GMT_Str.length){
              self.format += ' ' + GMT_Str[i];
              i++;
            }
    
            theSpan.innerHTML = self.strftime(newDate);
        }
        
        
        self.processSpanFullDate = function (theSpan) {
            var GMT_Str = theSpan.className.split(" ");
            //var GMT = parseInt(theSpan.getAttribute('gmt_time')) * 1000;
            var GMT = parseInt(GMT_Str[1]) * 1000;
            var newDate = new Date(GMT);
            
            var date = new Date();
            var offset = date.getTimezoneOffset() / 60;
            if ( newDate.getHours() + offset < 0 )
            {
              self.format = self.format = '%a %H:%M %p';
           //   var absNewTime = newDate.getTime() + (1 * 24 * 60 * 60 * 1000);
           //   newDate.setTime(absNewTime);
            }
            else if ( newDate.getHours() + offset > 24 )
            {
              self.format = self.format = '%a %H:%M %p';
             // var absNewTime = newDate.getTime() - (1 * 24 * 60 * 60 * 1000);
             // newDate.setTime(absNewTime);
            }
            else self.format = self.format = '%H:%M %p';
            
            self.format = GMT_Str[2];
            var i = 3;
            while (i < GMT_Str.length){
              self.format += ' ' + GMT_Str[i];
              i++;
            }
           
            theSpan.innerHTML = self.strftime(newDate);
        }
        
        self.processAbbr = function (theAbbr) {
            var ISOtime = theAbbr.getAttribute('title');
            var newDate = self.parseISO(ISOtime);
            theAbbr.innerHTML = self.strftime(newDate);
        }
        
        handleEvent(window, 'load', self.instantiate);
    }
    var time_zone_converter = new PerfectTime('%H:%M');