/**
 * Format any UTC timestamp on the page to the users local timezone.
 */
var DateFormat = {
  autoParse: function(){
    $$('span.timestamp').each(function(span) {
      var utc = Date.parseUTC(span.getAttribute('title'));
      var rel = span.getAttribute('rel');
      span.set('html', rel == 'words' ? utc.timeAgoInWords() : utc.strftime(rel))
    });
  }
}

var Locale = new Hash({
  monthNames: (typeof Date.CultureInfo == 'undefined' ? 'January February March April May June July August September October November December'.split(' ') : Date.CultureInfo.monthNames),
  dayNames: (typeof Date.CultureInfo == 'undefined' ? 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' ') : Date.CultureInfo.dayNames)
});

String.prototype.pad = function(l, s, t){
  s = s || " ";
  return (l -= this.length) > 0 ? (s = new Array(Math.ceil(l / s.length) + 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2)) + this + s.substr(0, l - t) : this;
};

$extend(Date.prototype, {
  /**
   * Given a formatted string, replace the necessary items and return.
   * Example: Time.now().strftime("%B %d, %Y") => February 11, 2008
   * @param {String} format The formatted string used to format the results
   */
  strftime: function(format) {
    var day = this.getDay(), month = this.getMonth();
    var hours = this.getHours(), minutes = this.getMinutes();
    function pad(num) { return num.toString().pad(2, '0');};
    return format.replace(/\%([aAbBcdeHImMpSwyY])/g, "{$1}").substitute({
      'a': Locale.get('dayNames')[day].substr(0, 3),
      'A': Locale.get('dayNames')[day],
      'b': Locale.get('monthNames')[month].substr(0, 3),
      'B': Locale.get('monthNames')[month],
      'c': this.toString(),
      'd': pad(this.getDate()),
      'H': pad(hours),
      'I': (hours % 12 == 0) ? 12 : pad(hours % 12),
      'm': pad(month + 1),
      'e': month + 1,
      'M': pad(minutes),
      'p': hours >= 12 ? 'PM' : 'AM',
      'S': pad(this.getSeconds()),
      'w': day,
      'y': pad(this.getFullYear() % 100),
      'Y': this.getFullYear().toString()
    });
  },
  
  timeAgoInWords: function() {
    var relative_to = (arguments.length > 0) ? arguments[1] : new Date();
    return Date.distanceOfTimeInWords(this, relative_to, arguments[2]);
  }
});

$extend(Date, {
  /**
   * Common formats passed to strftime
   */
  strftimeFormats: {
    time: "%I:%M %p",
    day:  "%B %d",
    short: '%b %d',
    dayName: "%A"
  },
  
  /**
   * Get an array back with hours, minutes and seconds from now to a future date.
   * @param {Date} to The future time used to get equate the difference
   * @return {Array} [hours, minutes, seconds]
   */
  differenceFromNow: function(to) {
    var seconds = Math.ceil((new Date().getTime() - to.getTime()) / 1000);
    var hours   = Math.floor(seconds / 3600).toPaddedString(2);
    seconds     = Math.floor(seconds % 3600);
    var minutes = Math.floor(seconds / 60).toPaddedString(2);
    seconds = (seconds % 60).toPaddedString(2);
    return [hours, minutes, seconds];
  },
  
  /**
   * Parse a string date and return a UTC date
   * @param {String} value Formatted date string
   * @return Date
   */
  parseUTC: function(value) {
    var localDate = new Date(value);
    var utcSeconds = Date.UTC(localDate.getFullYear(), localDate.getMonth(), localDate.getDate(), localDate.getHours(), localDate.getMinutes(), localDate.getSeconds())
    return new Date(utcSeconds);
  },
  
  /**
   * Return the distance of time in words between two Dates
   * Example: '5 days ago', 'about an hour ago'
   * @param {Date} fromTime The start date to use in the calculation
   * @param {Date} toTime The end date to use in the calculation
   * @param {Boolean} Include the time in the output
   */
  distanceOfTimeInWords: function(fromTime, toTime, includeTime) {
    var delta = parseInt((toTime.getTime() - fromTime.getTime()) / 1000);
    if(delta < 60) {
        return 'less than a minute ago';
    } else if(delta < 120) {
        return 'about a minute ago';
    } else if(delta < (45*60)) {
        return (parseInt(delta / 60)).toString() + ' minutes ago';
    } else if(delta < (120*60)) {
        return 'about an hour ago';
    } else if(delta < (24*60*60)) {
        return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
    } else if(delta < (48*60*60)) {
        return '1 day ago';
    } else {
      var days = (parseInt(delta / 86400)).toString();
      if(days > 5) {
        var fmt  = '%B %d'
        if(toTime.getYear() != fromTime.getYear()) { fmt += ', %Y' }
        if(includeTime) fmt += ' %I:%M %p'
        return fromTime.strftime(fmt);
      } else {
        return days + " days ago"
      }
    }
  }
});

// Time helpers by MARTIN STRÖM
Object.extend(Number.prototype, {
  seconds: function() {
    return this * 1000;
  },
  
  minutes: function() {
    return this * (60).seconds();
  },
  
  hours: function() {
    return this * (60).minutes();
  },
  
  days: function() {
    return this * (24).hours();
  },
  
  weeks: function(args) {
    return this * (7).days();
  },
  
  fortnights: function() {
    return this * (2).weeks();
  },
  
  months: function() {
    return this * (30).days();
  },
  
  years: function() {
    return parseInt(this * (365.25).days())
  },
  
  since: function(time) {
    time = time || new Date();
    if (time instanceof Date) time = time.getTime();
    return this + time;
  },
  
  ago: function(time) {
    time = time || new Date();
    if (time instanceof Date) time = time.getTime();
    return time - this;
  },
  
  toDate: function() {
    var date = new Date();
    date.setTime(this);
    return date;
  }
});

Object.extend(Number.prototype, {
  second:    Number.prototype.seconds,
  minute:    Number.prototype.minutes,
  hour:      Number.prototype.hours,
  day:       Number.prototype.days,
  week:      Number.prototype.weeks,
  fortnight: Number.prototype.fortnights,
  month:     Number.prototype.months,
  year:      Number.prototype.years,
  from_now:  Number.prototype.since,
  fromNow:   Number.prototype.since,
  until:     Number.prototype.ago
})