]> ToastFreeware Gitweb - chrisu/seepark.git/blob - web/static/strftime.js
add workaround for not rendered points after reload
[chrisu/seepark.git] / web / static / strftime.js
1 //
2 // strftime
3 // github.com/samsonjs/strftime
4 // @_sjs
5 //
6 // Copyright 2010 - 2016 Sami Samhuri <sami@samhuri.net>
7 //
8 // MIT License
9 // http://sjs.mit-license.org
10 //
11
12 ;(function() {
13
14     var Locales = {
15         de_DE: {
16             days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
17             shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
18             months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
19             shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
20             AM: 'AM',
21             PM: 'PM',
22             am: 'am',
23             pm: 'pm',
24             formats: {
25                 c: '%a %d %b %Y %X %Z',
26                 D: '%d.%m.%Y',
27                 F: '%Y-%m-%d',
28                 R: '%H:%M',
29                 r: '%I:%M:%S %p',
30                 T: '%H:%M:%S',
31                 v: '%e-%b-%Y',
32                 X: '%T',
33                 x: '%D'
34             }
35         },
36
37         en_CA: {
38             days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
39             shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
40             months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
41             shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
42             ordinalSuffixes: [
43                 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
44                 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
45                 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
46                 'st'
47             ],
48             AM: 'AM',
49             PM: 'PM',
50             am: 'am',
51             pm: 'pm',
52             formats: {
53                 c: '%a %d %b %Y %X %Z',
54                 D: '%d/%m/%y',
55                 F: '%Y-%m-%d',
56                 R: '%H:%M',
57                 r: '%I:%M:%S %p',
58                 T: '%H:%M:%S',
59                 v: '%e-%b-%Y',
60                 X: '%r',
61                 x: '%D'
62             }
63         },
64
65         en_US: {
66             days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
67             shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
68             months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
69             shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
70             ordinalSuffixes: [
71                 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
72                 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
73                 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
74                 'st'
75             ],
76             AM: 'AM',
77             PM: 'PM',
78             am: 'am',
79             pm: 'pm',
80             formats: {
81                 c: '%a %d %b %Y %X %Z',
82                 D: '%m/%d/%y',
83                 F: '%Y-%m-%d',
84                 R: '%H:%M',
85                 r: '%I:%M:%S %p',
86                 T: '%H:%M:%S',
87                 v: '%e-%b-%Y',
88                 X: '%r',
89                 x: '%D'
90             }
91         },
92
93         es_MX: {
94             days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
95             shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
96             months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre',' diciembre'],
97             shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
98             AM: 'AM',
99             PM: 'PM',
100             am: 'am',
101             pm: 'pm',
102             formats: {
103                 c: '%a %d %b %Y %X %Z',
104                 D: '%d/%m/%Y',
105                 F: '%Y-%m-%d',
106                 R: '%H:%M',
107                 r: '%I:%M:%S %p',
108                 T: '%H:%M:%S',
109                 v: '%e-%b-%Y',
110                 X: '%T',
111                 x: '%D'
112             }
113         },
114
115         fr_FR: {
116             days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
117             shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
118             months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
119             shortMonths: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
120             AM: 'AM',
121             PM: 'PM',
122             am: 'am',
123             pm: 'pm',
124             formats: {
125                 c: '%a %d %b %Y %X %Z',
126                 D: '%d/%m/%Y',
127                 F: '%Y-%m-%d',
128                 R: '%H:%M',
129                 r: '%I:%M:%S %p',
130                 T: '%H:%M:%S',
131                 v: '%e-%b-%Y',
132                 X: '%T',
133                 x: '%D'
134             }
135         },
136
137         it_IT: {
138             days: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato'],
139             shortDays: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
140             months: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
141             shortMonths: ['pr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'],
142             AM: 'AM',
143             PM: 'PM',
144             am: 'am',
145             pm: 'pm',
146             formats: {
147                 c: '%a %d %b %Y %X %Z',
148                 D: '%d/%m/%Y',
149                 F: '%Y-%m-%d',
150                 R: '%H:%M',
151                 r: '%I:%M:%S %p',
152                 T: '%H:%M:%S',
153                 v: '%e-%b-%Y',
154                 X: '%T',
155                 x: '%D'
156             }
157         },
158
159         nl_NL: {
160             days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
161             shortDays: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
162             months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
163             shortMonths: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
164             AM: 'AM',
165             PM: 'PM',
166             am: 'am',
167             pm: 'pm',
168             formats: {
169                 c: '%a %d %b %Y %X %Z',
170                 D: '%d-%m-%y',
171                 F: '%Y-%m-%d',
172                 R: '%H:%M',
173                 r: '%I:%M:%S %p',
174                 T: '%H:%M:%S',
175                 v: '%e-%b-%Y',
176                 X: '%T',
177                 x: '%D'
178             }
179         },
180
181         pt_BR: {
182             days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
183             shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
184             months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
185             shortMonths: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
186             AM: 'AM',
187             PM: 'PM',
188             am: 'am',
189             pm: 'pm',
190             formats: {
191                 c: '%a %d %b %Y %X %Z',
192                 D: '%d-%m-%Y',
193                 F: '%Y-%m-%d',
194                 R: '%H:%M',
195                 r: '%I:%M:%S %p',
196                 T: '%H:%M:%S',
197                 v: '%e-%b-%Y',
198                 X: '%T',
199                 x: '%D'
200             }
201         },
202
203         ru_RU: {
204             days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
205             shortDays: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
206             months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
207             shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
208             AM: 'AM',
209             PM: 'PM',
210             am: 'am',
211             pm: 'pm',
212             formats: {
213                 c: '%a %d %b %Y %X',
214                 D: '%d.%m.%y',
215                 F: '%Y-%m-%d',
216                 R: '%H:%M',
217                 r: '%I:%M:%S %p',
218                 T: '%H:%M:%S',
219                 v: '%e-%b-%Y',
220                 X: '%T',
221                 x: '%D'
222             }
223         },
224
225         tr_TR: {
226             days: ['Pazar', 'Pazartesi', 'Salı','Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
227             shortDays: ['Paz', 'Pzt', 'Sal', 'Çrş', 'Prş', 'Cum', 'Cts'],
228             months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
229             shortMonths: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
230             AM: 'ÖÖ',
231             PM: 'ÖS',
232             am: 'ÖÖ',
233             pm: 'ÖS',
234             formats: {
235                 c: '%a %d %b %Y %X %Z',
236                 D: '%d-%m-%Y',
237                 F: '%Y-%m-%d',
238                 R: '%H:%M',
239                 r: '%I:%M:%S %p',
240                 T: '%H:%M:%S',
241                 v: '%e-%b-%Y',
242                 X: '%T',
243                 x: '%D'
244             }
245         },
246
247         // By michaeljayt<michaeljayt@gmail.com>
248         // https://github.com/michaeljayt/strftime/commit/bcb4c12743811d51e568175aa7bff3fd2a77cef3
249         zh_CN: {
250             days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
251             shortDays: ['日', '一', '二', '三', '四', '五', '六'],
252             months: ['一月份', '二月份', '三月份', '四月份', '五月份', '六月份', '七月份', '八月份', '九月份', '十月份', '十一月份', '十二月份'],
253             shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
254             AM: '上午',
255             PM: '下午',
256             am: '上午',
257             pm: '下午',
258             formats: {
259                 c: '%a %d %b %Y %X %Z',
260                 D: '%d/%m/%y',
261                 F: '%Y-%m-%d',
262                 R: '%H:%M',
263                 r: '%I:%M:%S %p',
264                 T: '%H:%M:%S',
265                 v: '%e-%b-%Y',
266                 X: '%r',
267                 x: '%D'
268             }
269         }
270     };
271
272     var DefaultLocale = Locales['en_US'],
273         defaultStrftime = new Strftime(DefaultLocale, 0, false),
274         isCommonJS = typeof module !== 'undefined',
275         namespace;
276
277     // CommonJS / Node module
278     if (isCommonJS) {
279         namespace = module.exports = defaultStrftime;
280     }
281     // Browsers and other environments
282     else {
283         // Get the global object. Works in ES3, ES5, and ES5 strict mode.
284         namespace = (function() { return this || (1,eval)('this'); }());
285         namespace.strftime = defaultStrftime;
286     }
287
288     // Polyfill Date.now for old browsers.
289     if (typeof Date.now !== 'function') {
290         Date.now = function() {
291           return +new Date();
292         };
293     }
294
295     function Strftime(locale, customTimezoneOffset, useUtcTimezone) {
296         var _locale = locale || DefaultLocale,
297             _customTimezoneOffset = customTimezoneOffset || 0,
298             _useUtcBasedDate = useUtcTimezone || false,
299
300             // we store unix timestamp value here to not create new Date() each iteration (each millisecond)
301             // Date.now() is 2 times faster than new Date()
302             // while millisecond precise is enough here
303             // this could be very helpful when strftime triggered a lot of times one by one
304             _cachedDateTimestamp = 0,
305             _cachedDate;
306
307         function _strftime(format, date) {
308             var timestamp;
309
310             if (!date) {
311                 var currentTimestamp = Date.now();
312                 if (currentTimestamp > _cachedDateTimestamp) {
313                     _cachedDateTimestamp = currentTimestamp;
314                     _cachedDate = new Date(_cachedDateTimestamp);
315
316                     timestamp = _cachedDateTimestamp;
317
318                     if (_useUtcBasedDate) {
319                         // how to avoid duplication of date instantiation for utc here?
320                         // we tied to getTimezoneOffset of the current date
321                         _cachedDate = new Date(_cachedDateTimestamp + getTimestampToUtcOffsetFor(_cachedDate) + _customTimezoneOffset);
322                     }
323                 }
324                 else {
325                   timestamp = _cachedDateTimestamp;
326                 }
327                 date = _cachedDate;
328             }
329             else {
330                 timestamp = date.getTime();
331
332                 if (_useUtcBasedDate) {
333                     var utcOffset = getTimestampToUtcOffsetFor(date);
334                     date = new Date(timestamp + utcOffset + _customTimezoneOffset);
335                     // If we've crossed a DST boundary with this calculation we need to
336                     // adjust the new date accordingly or it will be off by an hour in UTC.
337                     if (getTimestampToUtcOffsetFor(date) !== utcOffset) {
338                         var newUTCOffset = getTimestampToUtcOffsetFor(date);
339                         date = new Date(timestamp + newUTCOffset + _customTimezoneOffset);
340                     }
341                 }
342             }
343
344             return _processFormat(format, date, _locale, timestamp);
345         }
346
347         function _processFormat(format, date, locale, timestamp) {
348             var resultString = '',
349                 padding = null,
350                 isInScope = false,
351                 length = format.length,
352                 extendedTZ = false;
353
354             for (var i = 0; i < length; i++) {
355
356                 var currentCharCode = format.charCodeAt(i);
357
358                 if (isInScope === true) {
359                     // '-'
360                     if (currentCharCode === 45) {
361                         padding = '';
362                         continue;
363                     }
364                     // '_'
365                     else if (currentCharCode === 95) {
366                         padding = ' ';
367                         continue;
368                     }
369                     // '0'
370                     else if (currentCharCode === 48) {
371                         padding = '0';
372                         continue;
373                     }
374                     // ':'
375                     else if (currentCharCode === 58) {
376                       if (extendedTZ) {
377                           warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime");
378                       }
379                       extendedTZ = true;
380                       continue;
381                     }
382
383                     switch (currentCharCode) {
384
385                         // Examples for new Date(0) in GMT
386
387                         // '%'
388                         // case '%':
389                         case 37:
390                             resultString += '%';
391                             break;
392
393                         // 'Thursday'
394                         // case 'A':
395                         case 65:
396                             resultString += locale.days[date.getDay()];
397                             break;
398
399                         // 'January'
400                         // case 'B':
401                         case 66:
402                             resultString += locale.months[date.getMonth()];
403                             break;
404
405                         // '19'
406                         // case 'C':
407                         case 67:
408                             resultString += padTill2(Math.floor(date.getFullYear() / 100), padding);
409                             break;
410
411                         // '01/01/70'
412                         // case 'D':
413                         case 68:
414                             resultString += _processFormat(locale.formats.D, date, locale, timestamp);
415                             break;
416
417                         // '1970-01-01'
418                         // case 'F':
419                         case 70:
420                             resultString += _processFormat(locale.formats.F, date, locale, timestamp);
421                             break;
422
423                         // '00'
424                         // case 'H':
425                         case 72:
426                             resultString += padTill2(date.getHours(), padding);
427                             break;
428
429                         // '12'
430                         // case 'I':
431                         case 73:
432                             resultString += padTill2(hours12(date.getHours()), padding);
433                             break;
434
435                         // '000'
436                         // case 'L':
437                         case 76:
438                             resultString += padTill3(Math.floor(timestamp % 1000));
439                             break;
440
441                         // '00'
442                         // case 'M':
443                         case 77:
444                             resultString += padTill2(date.getMinutes(), padding);
445                             break;
446
447                         // 'am'
448                         // case 'P':
449                         case 80:
450                             resultString += date.getHours() < 12 ? locale.am : locale.pm;
451                             break;
452
453                         // '00:00'
454                         // case 'R':
455                         case 82:
456                             resultString += _processFormat(locale.formats.R, date, locale, timestamp);
457                             break;
458
459                         // '00'
460                         // case 'S':
461                         case 83:
462                             resultString += padTill2(date.getSeconds(), padding);
463                             break;
464
465                         // '00:00:00'
466                         // case 'T':
467                         case 84:
468                             resultString += _processFormat(locale.formats.T, date, locale, timestamp);
469                             break;
470
471                         // '00'
472                         // case 'U':
473                         case 85:
474                             resultString += padTill2(weekNumber(date, 'sunday'), padding);
475                             break;
476
477                         // '00'
478                         // case 'W':
479                         case 87:
480                             resultString += padTill2(weekNumber(date, 'monday'), padding);
481                             break;
482
483                         // '16:00:00'
484                         // case 'X':
485                         case 88:
486                             resultString += _processFormat(locale.formats.X, date, locale, timestamp);
487                             break;
488
489                         // '1970'
490                         // case 'Y':
491                         case 89:
492                             resultString += date.getFullYear();
493                             break;
494
495                         // 'GMT'
496                         // case 'Z':
497                         case 90:
498                             if (_useUtcBasedDate && _customTimezoneOffset === 0) {
499                                 resultString += "GMT";
500                             }
501                             else {
502                                 // fixme optimize
503                                 var tzString = date.toString().match(/\(([\w\s]+)\)/);
504                                 resultString += tzString && tzString[1] || '';
505                             }
506                             break;
507
508                         // 'Thu'
509                         // case 'a':
510                         case 97:
511                             resultString += locale.shortDays[date.getDay()];
512                             break;
513
514                         // 'Jan'
515                         // case 'b':
516                         case 98:
517                             resultString += locale.shortMonths[date.getMonth()];
518                             break;
519
520                         // ''
521                         // case 'c':
522                         case 99:
523                             resultString += _processFormat(locale.formats.c, date, locale, timestamp);
524                             break;
525
526                         // '01'
527                         // case 'd':
528                         case 100:
529                             resultString += padTill2(date.getDate(), padding);
530                             break;
531
532                         // ' 1'
533                         // case 'e':
534                         case 101:
535                             resultString += padTill2(date.getDate(), padding == null ? ' ' : padding);
536                             break;
537
538                         // 'Jan'
539                         // case 'h':
540                         case 104:
541                             resultString += locale.shortMonths[date.getMonth()];
542                             break;
543
544                         // '000'
545                         // case 'j':
546                         case 106:
547                             var y = new Date(date.getFullYear(), 0, 1);
548                             var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24));
549                             resultString += padTill3(day);
550                             break;
551
552                         // ' 0'
553                         // case 'k':
554                         case 107:
555                             resultString += padTill2(date.getHours(), padding == null ? ' ' : padding);
556                             break;
557
558                         // '12'
559                         // case 'l':
560                         case 108:
561                             resultString += padTill2(hours12(date.getHours()), padding == null ? ' ' : padding);
562                             break;
563
564                         // '01'
565                         // case 'm':
566                         case 109:
567                             resultString += padTill2(date.getMonth() + 1, padding);
568                             break;
569
570                         // '\n'
571                         // case 'n':
572                         case 110:
573                             resultString += '\n';
574                             break;
575
576                         // '1st'
577                         // case 'o':
578                         case 111:
579                             // Try to use an ordinal suffix from the locale, but fall back to using the old
580                             // function for compatibility with old locales that lack them.
581                             var day = date.getDate();
582                             if (locale.ordinalSuffixes) {
583                                 resultString += String(day) + (locale.ordinalSuffixes[day - 1] || ordinal(day));
584                             }
585                             else {
586                                 resultString += String(day) + ordinal(day);
587                             }
588                             break;
589
590                         // 'AM'
591                         // case 'p':
592                         case 112:
593                             resultString += date.getHours() < 12 ? locale.AM : locale.PM;
594                             break;
595
596                         // '12:00:00 AM'
597                         // case 'r':
598                         case 114:
599                             resultString += _processFormat(locale.formats.r, date, locale, timestamp);
600                             break;
601
602                         // '0'
603                         // case 's':
604                         case 115:
605                             resultString += Math.floor(timestamp / 1000);
606                             break;
607
608                         // '\t'
609                         // case 't':
610                         case 116:
611                             resultString += '\t';
612                             break;
613
614                         // '4'
615                         // case 'u':
616                         case 117:
617                             var day = date.getDay();
618                             resultString += day === 0 ? 7 : day;
619                             break; // 1 - 7, Monday is first day of the week
620
621                         // ' 1-Jan-1970'
622                         // case 'v':
623                         case 118:
624                             resultString += _processFormat(locale.formats.v, date, locale, timestamp);
625                             break;
626
627                         // '4'
628                         // case 'w':
629                         case 119:
630                             resultString += date.getDay();
631                             break; // 0 - 6, Sunday is first day of the week
632
633                         // '12/31/69'
634                         // case 'x':
635                         case 120:
636                             resultString += _processFormat(locale.formats.x, date, locale, timestamp);
637                             break;
638
639                         // '70'
640                         // case 'y':
641                         case 121:
642                             resultString += ('' + date.getFullYear()).slice(2);
643                             break;
644
645                         // '+0000'
646                         // case 'z':
647                         case 122:
648                             if (_useUtcBasedDate && _customTimezoneOffset === 0) {
649                                 resultString += extendedTZ ? "+00:00" : "+0000";
650                             }
651                             else {
652                                 var off;
653                                 if (_customTimezoneOffset !== 0) {
654                                     off = _customTimezoneOffset / (60 * 1000);
655                                 }
656                                 else {
657                                     off = -date.getTimezoneOffset();
658                                 }
659                                 var sign = off < 0 ? '-' : '+';
660                                 var sep = extendedTZ ? ':' : '';
661                                 var hours = Math.floor(Math.abs(off / 60));
662                                 var mins = Math.abs(off % 60);
663                                 resultString += sign + padTill2(hours) + sep + padTill2(mins);
664                             }
665                             break;
666
667                         default:
668                             if (isInScope) {
669                                 resultString += '%';
670                             }
671                             resultString += format[i];
672                             break;
673                     }
674
675                     padding = null;
676                     isInScope = false;
677                     continue;
678                 }
679
680                 // '%'
681                 if (currentCharCode === 37) {
682                     isInScope = true;
683                     continue;
684                 }
685
686                 resultString += format[i];
687             }
688
689             return resultString;
690         }
691
692         var strftime = _strftime;
693
694         strftime.localize = function(locale) {
695             return new Strftime(locale || _locale, _customTimezoneOffset, _useUtcBasedDate);
696         };
697
698         strftime.localizeByIdentifier = function(localeIdentifier) {
699             var locale = Locales[localeIdentifier];
700             if (!locale) {
701                 warn('[WARNING] No locale found with identifier "' + localeIdentifier + '".');
702                 return strftime;
703             }
704             return strftime.localize(locale);
705         };
706
707         strftime.timezone = function(timezone) {
708             var customTimezoneOffset = _customTimezoneOffset;
709             var useUtcBasedDate = _useUtcBasedDate;
710
711             var timezoneType = typeof timezone;
712             if (timezoneType === 'number' || timezoneType === 'string') {
713                 useUtcBasedDate = true;
714
715                 // ISO 8601 format timezone string, [-+]HHMM
716                 if (timezoneType === 'string') {
717                     var sign = timezone[0] === '-' ? -1 : 1,
718                         hours = parseInt(timezone.slice(1, 3), 10),
719                         minutes = parseInt(timezone.slice(3, 5), 10);
720
721                     customTimezoneOffset = sign * ((60 * hours) + minutes) * 60 * 1000;
722                     // in minutes: 420
723                 }
724                 else if (timezoneType === 'number') {
725                     customTimezoneOffset = timezone * 60 * 1000;
726                 }
727             }
728
729             return new Strftime(_locale, customTimezoneOffset, useUtcBasedDate);
730         };
731
732         strftime.utc = function() {
733             return new Strftime(_locale, _customTimezoneOffset, true);
734         };
735
736         return strftime;
737     }
738
739     function padTill2(numberToPad, paddingChar) {
740         if (paddingChar === '' || numberToPad > 9) {
741             return numberToPad;
742         }
743         if (paddingChar == null) {
744             paddingChar = '0';
745         }
746         return paddingChar + numberToPad;
747     }
748
749     function padTill3(numberToPad) {
750         if (numberToPad > 99) {
751             return numberToPad;
752         }
753         if (numberToPad > 9) {
754             return '0' + numberToPad;
755         }
756         return '00' + numberToPad;
757     }
758
759     function hours12(hour) {
760         if (hour === 0) {
761             return 12;
762         }
763         else if (hour > 12) {
764             return hour - 12;
765         }
766         return hour;
767     }
768
769     // firstWeekday: 'sunday' or 'monday', default is 'sunday'
770     //
771     // Pilfered & ported from Ruby's strftime implementation.
772     function weekNumber(date, firstWeekday) {
773         firstWeekday = firstWeekday || 'sunday';
774
775         // This works by shifting the weekday back by one day if we
776         // are treating Monday as the first day of the week.
777         var weekday = date.getDay();
778         if (firstWeekday === 'monday') {
779             if (weekday === 0) // Sunday
780                 weekday = 6;
781             else
782                 weekday--;
783         }
784
785         var firstDayOfYearUtc = Date.UTC(date.getFullYear(), 0, 1),
786             dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
787             yday = Math.floor((dateUtc - firstDayOfYearUtc) / 86400000),
788             weekNum = (yday + 7 - weekday) / 7;
789
790         return Math.floor(weekNum);
791     }
792
793     // Get the ordinal suffix for a number: st, nd, rd, or th
794     function ordinal(number) {
795         var i = number % 10;
796         var ii = number % 100;
797
798         if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) {
799             return 'th';
800         }
801         switch (i) {
802             case 1: return 'st';
803             case 2: return 'nd';
804             case 3: return 'rd';
805         }
806     }
807
808     function getTimestampToUtcOffsetFor(date) {
809         return (date.getTimezoneOffset() || 0) * 60000;
810     }
811
812     function warn(message) {
813         if (typeof console !== 'undefined' && typeof console.warn == 'function') {
814             console.warn(message)
815         }
816     }
817
818 }());