Advertisement
markuspeloquin

strftime.js

Dec 2nd, 2012
480
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* Copyright (c) 2012, Markus Ivan Peloquin <markus@cs.wisc.edu>
  2.  *
  3.  * Permission to use, copy, modify, and/or distribute this software for any
  4.  * purpose with or without fee is hereby granted, provided that the above
  5.  * copyright notice and this permission notice appear in all copies.
  6.  *
  7.  * THE SOFTWARE IS PROVIDED 'AS IS' AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8.  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9.  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10.  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12.  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  13.  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  14.  
  15. // TODO test the week logic more
  16.  
  17. /*
  18.  * Usage:
  19.  *
  20.  * var d = new Date(0);
  21.  * var format = '%F %T %Z';
  22.  *
  23.  * // module interface
  24.  * Strftime.strftime(format, d);     // 1969-12-31 16:00:00 UTC-8
  25.  * Strftime.strftime_utc(format, d); // 1970-01-01 00:00:00 UTC
  26.  *
  27.  * // OO interface
  28.  * d.strftime(format);               // 1969-12-31 16:00:00 UTC-8
  29.  * d.strftime_utc(format);           // 1970-01-01 00:00:00 UTC
  30.  *
  31.  * The language must be overriden by adding the strings to Strftime.langs.XX
  32.  * and setting Strftime.lang = 'XX'. The locale must be overriden by adding
  33.  * the strings to Strftime.locales.YY and setting Strftime.locale = 'YY'.
  34.  */
  35.  
  36. Strftime = (function(){ // begin Strftime namespace
  37. var Strftime = {
  38.     langs: {},
  39.     locales: {},
  40. };
  41.  
  42. function pad2z(x)
  43. {
  44.     return (x<10 ? '0' : '') + x;
  45. }
  46. function pad2w(x)
  47. {
  48.     return (x<10 ? ' ' : '') + x;
  49. }
  50. function pad3z(x)
  51. {
  52.     return (x<100 ? (x<10 ? '00' : '0') : '') + x;
  53. }
  54. function weekday(lang, x, abbrev)
  55. {
  56.     return lang.weekdays[abbrev?1:0][x];
  57. }
  58. function month(lang, x, abbrev)
  59. {
  60.     return lang.months[abbrev?1:0][x];
  61. }
  62. function short_hour(x)
  63. {
  64.     return !x ? 12 : x>12 ? x-12 : x;
  65. }
  66. // recursion could be used, but...
  67. function insert(fmt, chars)
  68. {
  69.     var chars0 = fmt.split('');
  70.     chars0.reverse();
  71.     chars.push.apply(chars, chars0);
  72. }
  73. // weeknum(real day-of-year, day-of-year of first day of week 1)
  74. function weeknum(days, day0)
  75. {
  76.     days -= day0;
  77.     return days<0 ? 52 : (days/7 |0) + 1;
  78. }
  79. // it's much easier to compute these all at once rather than piecemeal;
  80. // people usually want both cweek and cwyear anyway
  81. function make_week_stats(date)
  82. {
  83.     var start = new Date(date.getTime());
  84.     start.setMonth(0);
  85.     start.setDate(1);
  86.     start.setHours(0);
  87.     start.setMinutes(0);
  88.     start.setSeconds(0);
  89.     start.setMilliseconds(0);
  90.  
  91.     var days = (date.getTime() - start.getTime()) /
  92.         (1000 * 60 * 60 * 24) |0;
  93.     var start_weekday = start.getDay();
  94.  
  95.     // day0_sun =  -sw mod 7,  0<=day0_sun<7
  96.     var day0_sun = (start_weekday<1 ? 0 : 7) - start_weekday;
  97.     // day0_mon = 1-sw mod 7,  0<=day0_mon<7
  98.     var day0_mon = (start_weekday<2 ? 1 : 8) - start_weekday;
  99.     // day0_cw  = 1-sw mod 7, -2<=day0_cw <4
  100.     var day0_cw  = (start_weekday<4 ? 1 : 8) - start_weekday;
  101.  
  102.     var cweek = weeknum(days, day0_cw);
  103.     var cwyear = date.getFullYear() + (
  104.         days<7   && cweek==52 ? -1 :
  105.         days>300 && cweek==1  ?  1 : 0);
  106.  
  107.     return {
  108.         days: days,
  109.         week_sun: weeknum(days, day0_sun),
  110.         week_mon: weeknum(days, day0_mon),
  111.         cweek: cweek,
  112.         cwyear: cwyear,
  113.     };
  114. }
  115.  
  116. function strftime(fmt, date)
  117. {
  118.     var chars = fmt.split('');
  119.     chars.reverse();
  120.     var lang = Strftime.langs[Strftime.lang];
  121.     var locale = Strftime.locales[Strftime.locale];
  122.     var res = '';
  123.     var week_stats = null;
  124.  
  125.     var c;
  126.     var code;
  127.  
  128.     // temporary variables
  129.     var days;
  130.     var h;
  131.     var minutes;
  132.     var sign;
  133.     while ((c = chars.pop()) != null) {
  134.         if (c != '%') {
  135.             res += c;
  136.             continue;
  137.         }
  138.  
  139.         if ((c = chars.pop()) == null)
  140.             throw new Error('Format cannot end with `%\'');
  141.         code = c.charCodeAt(0);
  142.  
  143.         switch (c) {
  144.         case 'g': case 'G':
  145.         case 'j':
  146.         case 'U': case 'V': case 'W':
  147.             week_stats = week_stats || make_week_stats(date);
  148.         }
  149.  
  150.         switch (c) {
  151.         // weekday name
  152.         case 'a': case 'A':
  153.             // abbreviate if 'a'
  154.             res += weekday(lang, date.getDay(), code==0x61);
  155.             break;
  156.         // month name
  157.         case 'b': case 'B':
  158.         case 'h':
  159.             // abbreviate if not 'B'
  160.             res += month(lang, date.getMonth(), code!=0x42);
  161.             break;
  162.         // locale
  163.         case 'c': // datetime
  164.         case 'X': // time
  165.         case 'x': // date
  166.             insert(locale[code==0x63 ? 'datetime' :
  167.                 code==0x78 ? 'date' : 'time'], chars);
  168.             break;
  169.         // century
  170.         case 'C':
  171.             res += pad2z(date.getFullYear() / 100 |0);
  172.             break;
  173.         // date
  174.         case 'd':
  175.         case 'e':
  176.             // pad with zeros for 'd'
  177.             res += (code==0x64 ? pad2z : pad2w)(date.getDate());
  178.             break;
  179.         // MM/DD/YY
  180.         case 'D':
  181.             insert('%m/%d/%y', chars);
  182.             break;
  183.         // YYYY-MM-DD
  184.         case 'F':
  185.             insert('%Y-%m-%d', chars);
  186.             break;
  187.         // ISO8601 year
  188.         case 'g':
  189.             res += pad2z(week_stats.cwyear % 100);
  190.             break;
  191.         // ISO8601 year
  192.         case 'G':
  193.             res += week_stats.cwyear;
  194.             break;
  195.         // hour
  196.         case 'H': // zero-padded
  197.         case 'k': // white-padded
  198.             res += (code==0x48 ? pad2z : pad2w)(date.getHours());
  199.             break;
  200.         // short hour
  201.         case 'I': // zero-padded
  202.         case 'l': // white-padded
  203.             res += (code==0x49 ? pad2z : pad2w)(short_hour(
  204.                 date.getHours()));
  205.             break;
  206.         // day-of-year
  207.         case 'j':
  208.             res += pad3z(week_stats.days);
  209.             break;
  210.         // month
  211.         case 'm':
  212.             res += pad2z(date.getMonth()+1);
  213.             break;
  214.         // minutes
  215.         case 'M':
  216.             res += pad2z(date.getMinutes());
  217.             break;
  218.         // newline
  219.         case 'n':
  220.             res += '\n';
  221.             break;
  222.         // am/pm
  223.         case 'p':
  224.         case 'P':
  225.             res += lang.ampm[date.getHours()>11 ?
  226.                 (code==0x50 ? 3 : 1) : // pm : PM
  227.                 (code==0x50 ? 2 : 0)]; // am : AM
  228.             break;
  229.         // H:MM:SS AMPM
  230.         // e.g. ' 9:00:00 PM'
  231.         case 'r':
  232.             insert('%I:%M:%S %p', chars);
  233.             break;
  234.         // HH:MM
  235.         case 'R':
  236.             insert('%H:%M', chars);
  237.             break;
  238.         // seconds
  239.         case 'S':
  240.             res += pad2z(date.getSeconds());
  241.             break;
  242.         // tab
  243.         case 't':
  244.             res += '\t';
  245.             break;
  246.         // HH:MM:SS
  247.         case 'T':
  248.             insert('%H:%M:%S', chars);
  249.             break;
  250.         // day of week, 1-7, monday=1
  251.         case 'u':
  252.             res += date.getDay()+1;
  253.             break;
  254.         // week num, start sun, 1 has first sun
  255.         case 'U':
  256.             res += pad2z(week_stats.week_sun);
  257.             break;
  258.         // week num, start mon, 1 has jan 4
  259.         case 'V':
  260.             res += pad2z(week_stats.cweek);
  261.             break;
  262.         // day of week, 0-6, monday=1
  263.         case 'w':
  264.             res += date.getDay();
  265.             break;
  266.         // week num, start mon, 1 has first mon
  267.         case 'W':
  268.             res += pad2z(week_stats.week_mon);
  269.             break;
  270.         // not technically correct, but...
  271.         case 'E':
  272.         case 'O':
  273.             insert(locale.datetime, chars);
  274.             break;
  275.         // year w/o century
  276.         case 'y':
  277.             res += pad2z(date.getYear() % 100);
  278.             break;
  279.         // full year
  280.         case 'Y':
  281.             res += date.getFullYear();
  282.             break;
  283.         // utc offset -hhmm +hhmm
  284.         case 'z':
  285.             minutes = date._is_utc ? 0 : date.getTimezoneOffset();
  286.             res += minutes<0 ? '+' : '-';
  287.             minutes = Math.abs(minutes);
  288.             res += pad2z(minutes / 60 |0) + pad2z(minutes % 60);
  289.             break;
  290.         // tz name (lazy implementation)
  291.         case 'Z':
  292.             minutes = date._is_utc ? 0 : date.getTimezoneOffset();
  293.             sign = minutes<0 ? '+' : '-';
  294.             minutes = Math.abs(minutes);
  295.             if (minutes % 60)
  296.                 insert('%z', chars);
  297.             else if (!minutes)
  298.                 res += 'UTC';
  299.             else
  300.                 res += 'UTC' + sign + (minutes / 60 |0);
  301.             break;
  302.         case '%':
  303.             res += '%';
  304.             break;
  305.         default:
  306.             throw new Error('Unrecognized format %' + c);
  307.         }
  308.     }
  309.     return res;
  310. }
  311.  
  312. function utc(date)
  313. {
  314.     date = new Date(date.getTime() + date.getTimezoneOffset() * 60000);
  315.     date._is_utc = true;
  316.     return date;
  317. }
  318.  
  319. function strftime_utc(fmt, date)
  320. {
  321.     return strftime(fmt, utc(date));
  322. }
  323.  
  324. Strftime.strftime = strftime;
  325. Strftime.strftime_utc = strftime_utc;
  326. Date.prototype.strftime = function(fmt) { return strftime(fmt, this) };
  327. Date.prototype.strftime_utc = function(fmt) { return strftime_utc(fmt, this) };
  328.  
  329. return Strftime;
  330. })(); // end Strftime namespace
  331.  
  332. Strftime.langs.en = {
  333.     weekdays: [
  334.         'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '),
  335.         'Sun Mon Tue Wed Thurs Fri Sat'.split(' ')
  336.     ],
  337.     months: [
  338.         'January February March April May June July August September October November December'.split(' '),
  339.         'Jan Feb Mar Apr May June July Aug Sep Oct Nov Dec'.split(' ')
  340.     ],
  341.     ampm: ['AM', 'PM', 'am', 'pm']
  342. };
  343. Strftime.langs.de = {
  344.     weekdays: [
  345.         'Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag'.split(' '),
  346.         'So Mo Di Mi Do Fr Sa'.split(' ')
  347.     ],
  348.     months: [
  349.         'Januar Februar M\0xe4rz April Mai Juni Juli August September Oktober November Dezember'.split(' '),
  350.         'Jan Feb Maart Apr Mei Juni Juli Aug Sept Okt Nov Dec'.split(' ')
  351.     ],
  352.     ampm: ['AM', 'PM', 'am', 'pm']
  353. };
  354.  
  355. // the Unix 'C' locale
  356. Strftime.locales.C = {
  357.     datetime: '%Y-%m-%d %H:%M:%S',
  358.     date: '%Y-%m-%d',
  359.     time: '%H:%M:%S'
  360. };
  361. Strftime.locales.DE = Strftime.locales.C;
  362. Strftime.locales.US = {
  363.     datetime: '%m/%d/%y %H:%M:%S',
  364.     date: '%m/%d/%y',
  365.     time: '%H:%M:%S',
  366. };
  367. Strftime.locales.UK = {
  368.     // ick
  369.     datetime: '%d/%m/%y %H:%M:%S',
  370.     date: '%d/%m/%y',
  371.     time: '%H:%M:%S',
  372. };
  373.  
  374. // setting the default language/locale
  375. Strftime.lang = 'en';
  376. Strftime.locale = 'C';
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement