1 /**
  2  * @class The built-in Date class.
  3  * @name Date
  4  */
  5 
  6 Date.__parse__ = Date.parse;
  7 
  8 /**
  9  * Parses a date from a string, optionally using the specified formatting. If
 10  * only a single argument is specified (i.e., <tt>format</tt> is not specified),
 11  * this method invokes the native implementation to guarantee
 12  * backwards-compatibility.
 13  *
 14  * <p>The format string is in the same format expected by the <tt>strptime</tt>
 15  * function in C. The following conversion specifications are supported:<ul>
 16  *
 17  * <li>%b - abbreviated month names.</li>
 18  * <li>%B - full month names.</li>
 19  * <li>%h - same as %b.</li>
 20  * <li>%d - day of month [1,31].</li>
 21  * <li>%e - same as %d.</li>
 22  * <li>%H - hour (24-hour clock) [0,23].</li>
 23  * <li>%m - month number [1,12].</li>
 24  * <li>%M - minute [0,59].</li>
 25  * <li>%S - second [0,61].</li>
 26  * <li>%y - year with century [0,99].</li>
 27  * <li>%Y - year including century.</li>
 28  * <li>%% - %.</li>
 29  *
 30  * </ul>The following conversion specifications are <i>unsupported</i> (for now):<ul>
 31  *
 32  * <li>%a - day of week, either abbreviated or full name.</li>
 33  * <li>%A - same as %a.</li>
 34  * <li>%c - locale's appropriate date and time.</li>
 35  * <li>%C - century number.</li>
 36  * <li>%D - same as %m/%d/%y.</li>
 37  * <li>%I - hour (12-hour clock) [1,12].</li>
 38  * <li>%j - day number [1,366].</li>
 39  * <li>%n - any white space.</li>
 40  * <li>%p - locale's equivalent of a.m. or p.m.</li>
 41  * <li>%r - same as %I:%M:%S %p.</li>
 42  * <li>%R - same as %H:%M.</li>
 43  * <li>%t - same as %n.</li>
 44  * <li>%T - same as %H:%M:%S.</li>
 45  * <li>%U - week number [0,53].</li>
 46  * <li>%w - weekday [0,6].</li>
 47  * <li>%W - week number [0,53].</li>
 48  * <li>%x - locale's equivalent to %m/%d/%y.</li>
 49  * <li>%X - locale's equivalent to %I:%M:%S %p.</li>
 50  *
 51  * </ul>
 52  *
 53  * @see <a
 54  * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strptime.html">strptime</a>
 55  * documentation.
 56  * @param {string} s the string to parse as a date.
 57  * @param {string} [format] an optional format string.
 58  * @returns {Date} the parsed date.
 59  */
 60 Date.parse = function(s, format) {
 61   if (arguments.length == 1) {
 62     return Date.__parse__(s);
 63   }
 64 
 65   var year = 1970, month = 0, date = 1, hour = 0, minute = 0, second = 0;
 66   var fields = [function() {}];
 67   format = format.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g, "\\$&");
 68   format = format.replace(/%[a-zA-Z0-9]/g, function(s) {
 69       switch (s) {
 70         // TODO %a: day of week, either abbreviated or full name
 71         // TODO %A: same as %a
 72         case '%b': {
 73           fields.push(function(x) { month = {
 74                 Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7,
 75                 Sep: 8, Oct: 9, Nov: 10, Dec: 11
 76               }[x]; });
 77           return "([A-Za-z]+)";
 78         }
 79         case '%h':
 80         case '%B': {
 81           fields.push(function(x) { month = {
 82                 January: 0, February: 1, March: 2, April: 3, May: 4, June: 5,
 83                 July: 6, August: 7, September: 8, October: 9, November: 10,
 84                 December: 11
 85               }[x]; });
 86           return "([A-Za-z]+)";
 87         }
 88         // TODO %c: locale's appropriate date and time
 89         // TODO %C: century number[0,99]
 90         case '%e':
 91         case '%d': {
 92           fields.push(function(x) { date = x; });
 93           return "([0-9]+)";
 94         }
 95         // TODO %D: same as %m/%d/%y
 96         case '%H': {
 97           fields.push(function(x) { hour = x; });
 98           return "([0-9]+)";
 99         }
100         // TODO %I: hour (12-hour clock) [1,12]
101         // TODO %j: day number [1,366]
102         case '%m': {
103           fields.push(function(x) { month = x - 1; });
104           return "([0-9]+)";
105         }
106         case '%M': {
107           fields.push(function(x) { minute = x; });
108           return "([0-9]+)";
109         }
110         // TODO %n: any white space
111         // TODO %p: locale's equivalent of a.m. or p.m.
112         // TODO %r: %I:%M:%S %p
113         // TODO %R: %H:%M
114         case '%S': {
115           fields.push(function(x) { second = x; });
116           return "([0-9]+)";
117         }
118         // TODO %t: any white space
119         // TODO %T: %H:%M:%S
120         // TODO %U: week number [00,53]
121         // TODO %w: weekday [0,6]
122         // TODO %W: week number [00, 53]
123         // TODO %x: locale date (%m/%d/%y)
124         // TODO %X: locale time (%I:%M:%S %p)
125         case '%y': {
126           fields.push(function(x) {
127               x = Number(x);
128               year = x + (((0 <= x) && (x < 69)) ? 2000
129                   : (((x >= 69) && (x < 100) ? 1900 : 0)));
130             });
131           return "([0-9]+)";
132         }
133         case '%Y': {
134           fields.push(function(x) { year = x; });
135           return "([0-9]+)";
136         }
137         case '%%': {
138           fields.push(function() {});
139           return "%";
140         }
141       }
142       return s;
143     });
144 
145   var match = s.match(format);
146   if (match) match.forEach(function(m, i) { fields[i](m); });
147   return new Date(year, month, date, hour, minute, second);
148 };
149 
150 if (Date.prototype.toLocaleFormat) {
151   Date.prototype.format = Date.prototype.toLocaleFormat;
152 } else {
153 
154 /**
155  * Converts a date to a string using the specified formatting. If the
156  * <tt>Date</tt> object already supports the <tt>toLocaleFormat</tt> method, as
157  * in Firefox, this is simply an alias to the built-in method.
158  *
159  * <p>The format string is in the same format expected by the <tt>strftime</tt>
160  * function in C. The following conversion specifications are supported:<ul>
161  *
162  * <li>%a - abbreviated weekday name.</li>
163  * <li>%A - full weekday name.</li>
164  * <li>%b - abbreviated month names.</li>
165  * <li>%B - full month names.</li>
166  * <li>%c - locale's appropriate date and time.</li>
167  * <li>%C - century number.</li>
168  * <li>%d - day of month [01,31] (zero padded).</li>
169  * <li>%D - same as %m/%d/%y.</li>
170  * <li>%e - day of month [ 1,31] (space padded).</li>
171  * <li>%h - same as %b.</li>
172  * <li>%H - hour (24-hour clock) [00,23] (zero padded).</li>
173  * <li>%I - hour (12-hour clock) [01,12] (zero padded).</li>
174  * <li>%m - month number [01,12] (zero padded).</li>
175  * <li>%M - minute [0,59] (zero padded).</li>
176  * <li>%n - newline character.</li>
177  * <li>%p - locale's equivalent of a.m. or p.m.</li>
178  * <li>%r - same as %I:%M:%S %p.</li>
179  * <li>%R - same as %H:%M.</li>
180  * <li>%S - second [00,61] (zero padded).</li>
181  * <li>%t - tab character.</li>
182  * <li>%T - same as %H:%M:%S.</li>
183  * <li>%x - same as %m/%d/%y.</li>
184  * <li>%X - same as %I:%M:%S %p.</li>
185  * <li>%y - year with century [00,99] (zero padded).</li>
186  * <li>%Y - year including century.</li>
187  * <li>%% - %.</li>
188  *
189  * </ul>The following conversion specifications are <i>unsupported</i> (for now):<ul>
190  *
191  * <li>%j - day number [1,366].</li>
192  * <li>%u - weekday number [1,7].</li>
193  * <li>%U - week number [00,53].</li>
194  * <li>%V - week number [01,53].</li>
195  * <li>%w - weekday number [0,6].</li>
196  * <li>%W - week number [00,53].</li>
197  * <li>%Z - timezone name or abbreviation.</li>
198  *
199  * </ul>
200  *
201  * @see <a
202  * href="http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/toLocaleFormat">Date.toLocaleFormat</a>
203  * documentation.
204  * @see <a
205  * href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">strftime</a>
206  * documentation.
207  * @param {string} format a format string.
208  * @returns {string} the formatted date.
209  */
210 Date.prototype.format = function(format) {
211   function pad(n, p) { return (n < 10) ? (p || "0") + n : n; }
212   var d = this;
213   return format.replace(/%[a-zA-Z0-9]/g, function(s) {
214       switch (s) {
215         case '%a': return [
216             "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
217           ][d.getDay()];
218         case '%A': return [
219             "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
220             "Saturday",
221           ][d.getDay()];
222         case '%h':
223         case '%b': return [
224             "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
225             "Oct", "Nov", "Dec",
226           ][d.getMonth()];
227         case '%B': return [
228             "January", "February", "March", "April", "May", "June", "July",
229             "August", "September", "October", "November", "December",
230           ][d.getMonth()];
231         case '%c': return d.toLocaleString();
232         case '%C': return pad(Math.floor(d.getFullYear() / 100) % 100);
233         case '%d': return pad(d.getDate());
234         case '%x':
235         case '%D': return pad(d.getMonth() + 1)
236                   + "/" + pad(d.getDate())
237                   + "/" + pad(d.getFullYear() % 100);
238         case '%e': return pad(d.getDate(), " ");
239         case '%H': return pad(d.getHours());
240         case '%I': {
241           var h = d.getHours() % 12;
242           return h ? pad(h) : 12;
243         }
244         // TODO %j: day of year as a decimal number [001,366]
245         case '%m': return pad(d.getMonth() + 1);
246         case '%M': return pad(d.getMinutes());
247         case '%n': return "\n";
248         case '%p': return d.getHours() < 12 ? "AM" : "PM";
249         case '%T':
250         case '%X':
251         case '%r': {
252           var h = d.getHours() % 12;
253           return (h ? pad(h) : 12)
254                   + ":" + pad(d.getMinutes())
255                   + ":" + pad(d.getSeconds())
256                   + " " + (d.getHours() < 12 ? "AM" : "PM");
257         }
258         case '%R': return pad(d.getHours()) + ":" + pad(d.getMinutes());
259         case '%S': return pad(d.getSeconds());
260         case '%t': return "\t";
261         case '%u': {
262           var w = d.getDay();
263           return w ? w : 1;
264         }
265         // TODO %U: week number (sunday first day) [00,53]
266         // TODO %V: week number (monday first day) [01,53] ... with weirdness
267         case '%w': return d.getDay();
268         // TODO %W: week number (monday first day) [00,53] ... with weirdness
269         case '%y': return pad(d.getFullYear() % 100);
270         case '%Y': return d.getFullYear();
271         // TODO %Z: timezone name or abbreviation
272         case '%%': return "%";
273       }
274       return s;
275     });
276   };
277 }
278