/**
 * Date formater
 *
 * @author clonedoppelganger(at)gmail.com
 * @class  Date
 * @param  {Date} target date object
 *         {String} pattern
 *  - Y 年。4桁。
 *  - y 年。2桁。
 *  - m 月。前ゼロあり。
 *  - n 月。前ゼロなし。
 *  - F 月。フルスペル。
 *  - M 月。3文字形式。
 *  - O 月。旧暦日本語。
 *  - d 日。前ゼロあり。
 *  - j 日。前ゼロなし。
 *  - w 曜日。数値。
 *  - l 曜日。フルスペル。
 *  - D 曜日。3文字形式。
 *  - N 曜日。ISO-8601形式の数値。
 *  - J 曜日。日本語。
 *  - g 時。12時間単位。前ゼロなし。
 *  - G 時。24時間単位。前ゼロなし。
 *  - h 時。12時間単位。前ゼロあり。
 *  - H 時。24時間単位。前ゼロあり。
 *  - i 分。前ゼロあり。
 *  - s 秒。前ゼロあり。
 *  - a 午前または午後。（am/pm）
 *  - A 午前または午後。（AM/PM）
 *  - S 英語形式の序数を表すサフィックス。2文字。
 *  - z 年間の通算日。数字。(ゼロから開始)
 *  - t 指定した月の日数。
 *  - L 閏年であるかどうか。
 *  上記の予約パラメータを通常の文字としたい場合には直前に#を付けて下さい。
 * @return {String} formatted date
 */
Date.format = function(d, pattern) {

  if (typeof pattern != "string") return;

  var dYear = d.getFullYear();
  var dMonth = d.getMonth();
  var dDate = d.getDate();
  var dDay = d.getDay();
  var dHours = d.getHours();
  var dMinutes = d.getMinutes();
  var dSeconds = d.getSeconds();

  var preZero = function(value) {
    return (parseInt(value) < 10) ? "0" + value : value
  };
  var from24to12 = function(hours) {
    return (hours > 12) ? hours - 12 : hours;
  }
  var ampm = function() {
    return (dHours < 12) ? "am" : "pm";
  }
  var isoDay = function() {
    return (dDay == 0) ? "7" : dDay;
  }
  var weekFullEn = function() {
    var week = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
    return week[dDay];
  }
  var weekJp = function() {
    var week = ["日","月","火","水","木","金","土"];
    return week[dDay];
  }
  var monthFullEn = function() {
    var month = ["January", "February", "March", "April", "May", "June",
      "July", "August", "September", "October", "November", "December"];
    return month[dMonth];
  }
  var monthOldJp = function() {
    var month = ["睦月", "如月", "弥生", "卯月", "皐月", "水無月",
      "文月", "葉月", "長月", "神無月", "霜月", "師走"];
    return month[dMonth];
  }
  var lastDayOfMonth = function(dateObj) {
    var tmp = new Date(dateObj.getFullYear(), dateObj.getMonth() + 1, 1);
    tmp.setTime(tmp.getTime() - 1);
    return tmp.getDate();
  }
  var isLeapYear = function() {
    var tmp = new Date(dYear, 0, 1);
    var sum = 0;
    for (var i = 0; i < 12; i++) {
      tmp.setMonth(i);
      sum += lastDayOfMonth(tmp);
    }
    return (sum == 365) ? "0" : "1";
  }
  var dateCount = function() {
    var tmp = new Date(dYear, 0, 1);
    var sum = -1;
    for (var i = 0; i < dMonth; i++) {
      tmp.setMonth(i);
      sum += lastDayOfMonth(tmp);
    }
    return sum + dDate;
  }
  var dateSuffix = function() {
    var suffix = [
      "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th",
      "th", "th", "th", "th", "th", "th", "th", "th", "th", "th",
      "st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "st"
    ];
    return suffix[dDate - 1];
  }

  var res = "";
  for (var i = 0, len = pattern.length; i < len; i++) {
    var c = pattern.charAt(i);
    switch (c) {
      case "#": 
        if (i == len - 1) break;
        res += pattern.charAt(++i);
        break;
      case "Y": res += dYear; break;
      case "y": res += dYear.toString().substr(2, 2); break;
      case "m": res += preZero(dMonth + 1); break;
      case "n": res += dMonth + 1; break;
      case "d": res += preZero(dDate); break;
      case "j": res += dDate; break;
      case "w": res += dDay; break;
      case "N": res += isoDay(); break
      case "l": res += weekFullEn(); break;
      case "D": res += weekFullEn().substr(0, 3); break;
      case "J": res += weekJp(); break;
      case "F": res += monthFullEn(); break;
      case "O": res += monthOldJp(); break;
      case "a": res += ampm(); break;
      case "A": res += ampm().toUpperCase(); break;
      case "H": res += preZero(dHours); break;
      case "h": res += preZero(from24to12(dHours)); break;
      case "g": res += from24to12(dHours); break;
      case "G": res += dHours; break;
      case "i": res += preZero(dMinutes); break;
      case "s": res += preZero(dSeconds); break;
      case "M": res += monthFullEn().substr(0, 3); break;
      case "t": res += lastDayOfMonth(d); break;
      case "L": res += isLeapYear(); break;
      case "z": res += dateCount(); break;
      case "S": res += dateSuffix(); break;
      default : res += c; break;
    }
  }
  return res;
}
