UNPKG

dateable

Version:
348 lines (290 loc) 6.47 kB
/** * Set default language */ var lang = require('./lang/en-us'); /** * Format date * * @param {Date} date * @param {String} format * @return {String} * @api public */ function dateable (date, format) { var date = toObject(date); var re = /Y{2,4}|[Md]{1,4}|[DHhms]{1,2}|[Aa]|"[^"]*"|'[^']*'/g; format = formats[format] || format; return format.replace(re, function (part) { switch (part) { case 'YYYY': return pad(date.Y, 3); case 'YY': return ('' + date.Y).slice(-2); case 'MMMM': case 'MMM': return lang[part][date.M]; case 'MM': return pad(date.M + 1); case 'M': return date.M + 1; case 'DD': return pad(date.D); case 'D': return date.D; case 'dddd': case 'ddd': return lang[part][date.d]; case 'A': return date.A; case 'a': return date.a case 'H': return date.H case 'HH': return pad(date.H); case 'hh': return pad(date.h); case 'h': return date.h case 'mm': return pad(date.m); case 'm': return date.m case 'ss': return pad(date.s); case 's': return date.s; default: return part.slice(1, -1); } }); }; /** * Alias formatter */ dateable.format = dateable; /** * Units */ var units = dateable.units = { years : 31536000000 , months : 2592000000 , weeks : 604800000 , days : 86400000 , hours : 3600000 , minutes : 60000 , seconds : 1000 }; /** * Stored formats */ var formats = dateable.formats = {}; /** * Set language * * @param {String|Object} name * @api public */ dateable.language = function (name) { // object if (typeof name != 'string') { lang = name; return; } // string try { lang = require('./lang/' + name); } catch (e) { throw new Error('language does not exist'); } }; /** * Parse a date string * * @param {String} string * @param {String} format * @return {Date} * @api public */ dateable.parse = function (string, format) { var re = /Y{2,4}|[Md]{1,4}|[DHhms]{1,2}|[Aa]/g; var offset = 0; var parts = {}; var token; format = formats[format] || format; // Strip the string from the escaped parts of the format format = format.replace(/"[^"]*"|'[^']*'/g, function (str) { string = string.replace(str.slice(1, -1), ''); return ''; }); var stringLength = string.length; while (token = re.exec(format)) { var index = token.index + offset var tokenLength = token[0].length var part = string.substr(index, tokenLength); index += tokenLength - 1; // Remove characters that are not part of the format // e.g, MMMM > May part = part.replace(/\W+.*/, function (str) { index -= str.length; return ''; }); // Looks ahead for characters beyond the // specified format, e.g, D > 9 while (++index < stringLength) { if (!(/\d|\w/).test(string[index])) break; part += string[index]; } offset += part.length - tokenLength; if (/[Md]{3,4}/.test(token[0])) { part = lang[token[0]].indexOf(part); } else if (token[0][0] === 'M') { part--; } parts[token[0][0]] = part; } return toDate(parts); }; /** * Relative time * * @param {Date} date * @param {String} [unit] * @return {String} * @api public */ dateable.when = function (date, unit) { var now = new Date(); var diff = date.valueOf() - now.valueOf(); var time = 'present'; unit = unit || determineUnit(diff); diff = Math.round(diff / units[unit]) if (diff !== 0) { time = diff < 0 ? 'past' : 'future'; } diff = Math.abs(diff); return format(lang.time[time], pluralize(diff, unit)); }; /** * Return difference between two dates * * @param {Date} start * @param {Date} end * @param {String} [unit] * @return {String} * @api public */ dateable.diff = function (start, end, unit) { var diff = start.valueOf() - end.valueOf() var unit = unit || determineUnit(diff) diff = Math.abs(Math.round(diff / units[unit])) return pluralize(diff, unit); }; /** * Pluralize unit * * @param {Number} value * @param {String} unit * @return {String} * @api private */ function pluralize (value, unit) { var form = lang.units[unit][value > 1 ? 1 : 0]; return format(form, value); }; /** * Find the best matching unit for an amount of time (ms) * * @param {Number} ms * @return {String} * @api private */ function determineUnit (ms) { ms = Math.abs(ms); for (var unit in units) { if (ms > units[unit]) break; } return unit; } /** * Pad a number with leading zeros * * @param {Number} number * @param {Number} length * @return {String} * @api private */ function pad (number, length) { return number < Math.pow(10, length || 1) ? '0' + number : '' + number; } /** * Format string * * @param {String} str * @return {String} * @api private */ function format (str) { var args = Array.prototype.slice.call(arguments, 1); return str.replace(/(%[djs])/g, function (match) { return args.length ? args.shift() : match; }); } /** * Convert an object to a date * * @param {Object} object * @return {Number} length * @api private */ function toDate (obj) { var date = new Date(0); var abbr = obj.a || obj.A; // Handle AM/PM if (!obj.h && obj.H && abbr) { abbr = abbr.toLowerCase(); if (abbr == 'pm' && obj.H < 12) { obj.h = obj.H + 12; } } // Handle years if (obj.Y && obj.Y.length == 2) { if (parseInt(obj.Y, 10) > 50) { obj.Y = '19' + obj.Y; } else { obj.Y = '20' + obj.Y; } } date.setFullYear(obj.Y || 0); date.setMonth(obj.M || 0); date.setDate(obj.D || 0); date.setHours(obj.h || 0); date.setMinutes(obj.m || 0); date.setSeconds(obj.s || 0); return date; } /** * Convert a date to an object * * @param {Object} date * @return {Object} * @api private */ function toObject (date) { var obj = { Y: date.getFullYear(), M: date.getMonth(), D: date.getDate(), d: date.getDay(), h: date.getHours(), m: date.getMinutes(), s: date.getSeconds() }; obj.H = obj.h - (obj.h > 12 ? 12 : 0); obj.a = obj.h >= 12 ? 'pm' : 'am'; obj.A = obj.a.toUpperCase(); return obj; } module.exports = dateable;