formatters/date.js

/**
 * @fileoverview Date format library.
 *
 * @see http://google.github.io/styleguide/javascriptguide.xml
 * @see http://developers.google.com/closure/compiler/docs/js-for-compiler
 * @module glize/formatters/date
 * @requires glize/locale
 */

import {getMonthName, getMonthByName} from '../locale/index.js';

/**
 * Static number formats that do not require parsing of the input string.
 * @enum {!Object<string, function(!Date): number>}
 * @inner
 */
const NUMBER_FORMAT = {
  /**
   * @param {!Date} date The date to be formatted.
   * @return {number} Returns formatted date as number.
   */
  'YYYYMMdd': (date) => {
      return 1e4 * date.getFullYear() +
             1e2 * (date.getMonth() + 1) + date.getDate();
  },
  /**
   * @param {!Date} date The date to be formatted.
   * @return {number} Returns formatted date as number.
   */
  'YYYYMMddhhmm': (date) => {
      return 1e4 * NUMBER_FORMAT['YYYYMMdd'](date) +
             1e2 * date.getHours() + date.getMinutes();
  },
  /**
   * @param {!Date} date The date to be formatted.
   * @return {number} Returns formatted date as number.
   */
  'YYYYMMddhhmmss': (date) => {
      return 1e2 * NUMBER_FORMAT['YYYYMMddhhmm'](date) + date.getSeconds();
  }
};

/**
 * Formats given <code>date</code> according to given <code>format</code>.
 * @param {!Date} date The date to be formatted.
 * @param {string} format The date format.
 * @return {string} Returns formatted date as string.
 * @method
 * @example
 * formatDate(new Date(), 'YYYY-MM-dd');
 * formatDate(new Date(), 'YYYY-MM-dd hh:mm');
 * formatDate(new Date(), 'dd MMM, YYYY');
 */
export const formatDate = (date, format) => {
  const fn = NUMBER_FORMAT[format];
  const pad = (num) => ('0' + num).slice(-2);

  if (fn) {
    format = '' + fn(date);
  } else {
    const month = getMonthName(date);
    const tokens = [
      'YYYY', date.getFullYear(),
      'YY', (date.getFullYear() + '').substr(2),
      'MMMM', month,
      'MMM', month.substr(0, 3),
      'MM', pad(date.getMonth() + 1),
      'dd', pad(date.getDate()),
      'd', date.getDate(),
      'hh', pad(date.getHours()),
      'mm', pad(date.getMinutes()),
      'ss', pad(date.getSeconds())
    ];

    const length = tokens.length;
    for (let i = 0; i < length;) {
      format = format.replace(tokens[i++], tokens[i++]);
    }
  }

  return format;
};

/**
 * Parses given date <code>str</code> according to given <code>format</code>.
 * @param {string} str The date string to be parsed.
 * @param {string} format The date format.
 * @return {?Date} The parsed date.
 * @method
 * @example
 * parseDate('2013-01-31', 'YYYY-MM-dd');
 * parseDate('2013-01-31 20:30', 'YYYY-MM-dd hh:mm');
 * parseDate('03 Jan, 2013', 'dd MMM, YYYY');
 */
export const parseDate = (str, format) => {
  const re = /[\/\s,\.\:\-]/;
  const dateParts = str.split(re);
  const formatParts = format.split(re);
  const map = {};

  for (let i = 0; i < Math.min(dateParts.length, formatParts.length); ++i) {
    const key = formatParts[i];
    if (key) map[key] = dateParts[i];
  }

  const year = +(map['YYYY'] || '20' + map['YY']);
  let month = +(map['MM'] || 0) - 1;
  if (map['MMMM'] || map['MMM']) {
    month = getMonthByName(map['MMMM'] || map['MMM']);
  }

  return month >= 0 && +map['dd'] < 32 ?
      new Date(
        year, month, +map['dd'], 
        +map['hh'] || 0, +map['mm'] || 0, +map['ss'] || 0) :
      null;
};