|
| 1 | +(function (main) { |
| 2 | + 'use strict'; |
| 3 | + |
| 4 | + /** |
| 5 | + * Parse or format dates |
| 6 | + * @class fecha |
| 7 | + */ |
| 8 | + var fecha = {}; |
| 9 | + var token = /d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g; |
| 10 | + var twoDigits = '\\d\\d?'; |
| 11 | + var threeDigits = '\\d{3}'; |
| 12 | + var fourDigits = '\\d{4}'; |
| 13 | + var word = '[^\\s]+'; |
| 14 | + var literal = /\[([^]*?)\]/gm; |
| 15 | + var noop = function () { |
| 16 | + }; |
| 17 | + |
| 18 | + function regexEscape(str) { |
| 19 | + return str.replace( /[|\\{()[^$+*?.-]/g, '\\$&'); |
| 20 | + } |
| 21 | + |
| 22 | + function shorten(arr, sLen) { |
| 23 | + var newArr = []; |
| 24 | + for (var i = 0, len = arr.length; i < len; i++) { |
| 25 | + newArr.push(arr[i].substr(0, sLen)); |
| 26 | + } |
| 27 | + return newArr; |
| 28 | + } |
| 29 | + |
| 30 | + function monthUpdate(arrName) { |
| 31 | + return function (d, v, i18n) { |
| 32 | + var index = i18n[arrName].indexOf(v.charAt(0).toUpperCase() + v.substr(1).toLowerCase()); |
| 33 | + if (~index) { |
| 34 | + d.month = index; |
| 35 | + } |
| 36 | + }; |
| 37 | + } |
| 38 | + |
| 39 | + function pad(val, len) { |
| 40 | + val = String(val); |
| 41 | + len = len || 2; |
| 42 | + while (val.length < len) { |
| 43 | + val = '0' + val; |
| 44 | + } |
| 45 | + return val; |
| 46 | + } |
| 47 | + |
| 48 | + var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; |
| 49 | + var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; |
| 50 | + var monthNamesShort = shorten(monthNames, 3); |
| 51 | + var dayNamesShort = shorten(dayNames, 3); |
| 52 | + fecha.i18n = { |
| 53 | + dayNamesShort: dayNamesShort, |
| 54 | + dayNames: dayNames, |
| 55 | + monthNamesShort: monthNamesShort, |
| 56 | + monthNames: monthNames, |
| 57 | + amPm: ['am', 'pm'], |
| 58 | + DoFn: function DoFn(D) { |
| 59 | + return D + ['th', 'st', 'nd', 'rd'][D % 10 > 3 ? 0 : (D - D % 10 !== 10) * D % 10]; |
| 60 | + } |
| 61 | + }; |
| 62 | + |
| 63 | + var formatFlags = { |
| 64 | + D: function(dateObj) { |
| 65 | + return dateObj.getDate(); |
| 66 | + }, |
| 67 | + DD: function(dateObj) { |
| 68 | + return pad(dateObj.getDate()); |
| 69 | + }, |
| 70 | + Do: function(dateObj, i18n) { |
| 71 | + return i18n.DoFn(dateObj.getDate()); |
| 72 | + }, |
| 73 | + d: function(dateObj) { |
| 74 | + return dateObj.getDay(); |
| 75 | + }, |
| 76 | + dd: function(dateObj) { |
| 77 | + return pad(dateObj.getDay()); |
| 78 | + }, |
| 79 | + ddd: function(dateObj, i18n) { |
| 80 | + return i18n.dayNamesShort[dateObj.getDay()]; |
| 81 | + }, |
| 82 | + dddd: function(dateObj, i18n) { |
| 83 | + return i18n.dayNames[dateObj.getDay()]; |
| 84 | + }, |
| 85 | + M: function(dateObj) { |
| 86 | + return dateObj.getMonth() + 1; |
| 87 | + }, |
| 88 | + MM: function(dateObj) { |
| 89 | + return pad(dateObj.getMonth() + 1); |
| 90 | + }, |
| 91 | + MMM: function(dateObj, i18n) { |
| 92 | + return i18n.monthNamesShort[dateObj.getMonth()]; |
| 93 | + }, |
| 94 | + MMMM: function(dateObj, i18n) { |
| 95 | + return i18n.monthNames[dateObj.getMonth()]; |
| 96 | + }, |
| 97 | + YY: function(dateObj) { |
| 98 | + return String(dateObj.getFullYear()).substr(2); |
| 99 | + }, |
| 100 | + YYYY: function(dateObj) { |
| 101 | + return dateObj.getFullYear(); |
| 102 | + }, |
| 103 | + h: function(dateObj) { |
| 104 | + return dateObj.getHours() % 12 || 12; |
| 105 | + }, |
| 106 | + hh: function(dateObj) { |
| 107 | + return pad(dateObj.getHours() % 12 || 12); |
| 108 | + }, |
| 109 | + H: function(dateObj) { |
| 110 | + return dateObj.getHours(); |
| 111 | + }, |
| 112 | + HH: function(dateObj) { |
| 113 | + return pad(dateObj.getHours()); |
| 114 | + }, |
| 115 | + m: function(dateObj) { |
| 116 | + return dateObj.getMinutes(); |
| 117 | + }, |
| 118 | + mm: function(dateObj) { |
| 119 | + return pad(dateObj.getMinutes()); |
| 120 | + }, |
| 121 | + s: function(dateObj) { |
| 122 | + return dateObj.getSeconds(); |
| 123 | + }, |
| 124 | + ss: function(dateObj) { |
| 125 | + return pad(dateObj.getSeconds()); |
| 126 | + }, |
| 127 | + S: function(dateObj) { |
| 128 | + return Math.round(dateObj.getMilliseconds() / 100); |
| 129 | + }, |
| 130 | + SS: function(dateObj) { |
| 131 | + return pad(Math.round(dateObj.getMilliseconds() / 10), 2); |
| 132 | + }, |
| 133 | + SSS: function(dateObj) { |
| 134 | + return pad(dateObj.getMilliseconds(), 3); |
| 135 | + }, |
| 136 | + a: function(dateObj, i18n) { |
| 137 | + return dateObj.getHours() < 12 ? i18n.amPm[0] : i18n.amPm[1]; |
| 138 | + }, |
| 139 | + A: function(dateObj, i18n) { |
| 140 | + return dateObj.getHours() < 12 ? i18n.amPm[0].toUpperCase() : i18n.amPm[1].toUpperCase(); |
| 141 | + }, |
| 142 | + ZZ: function(dateObj) { |
| 143 | + var o = dateObj.getTimezoneOffset(); |
| 144 | + return (o > 0 ? '-' : '+') + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4); |
| 145 | + } |
| 146 | + }; |
| 147 | + |
| 148 | + var parseFlags = { |
| 149 | + D: [twoDigits, function (d, v) { |
| 150 | + d.day = v; |
| 151 | + }], |
| 152 | + Do: [twoDigits + word, function (d, v) { |
| 153 | + d.day = parseInt(v, 10); |
| 154 | + }], |
| 155 | + M: [twoDigits, function (d, v) { |
| 156 | + d.month = v - 1; |
| 157 | + }], |
| 158 | + YY: [twoDigits, function (d, v) { |
| 159 | + var da = new Date(), cent = +('' + da.getFullYear()).substr(0, 2); |
| 160 | + d.year = '' + (v > 68 ? cent - 1 : cent) + v; |
| 161 | + }], |
| 162 | + h: [twoDigits, function (d, v) { |
| 163 | + d.hour = v; |
| 164 | + }], |
| 165 | + m: [twoDigits, function (d, v) { |
| 166 | + d.minute = v; |
| 167 | + }], |
| 168 | + s: [twoDigits, function (d, v) { |
| 169 | + d.second = v; |
| 170 | + }], |
| 171 | + YYYY: [fourDigits, function (d, v) { |
| 172 | + d.year = v; |
| 173 | + }], |
| 174 | + S: ['\\d', function (d, v) { |
| 175 | + d.millisecond = v * 100; |
| 176 | + }], |
| 177 | + SS: ['\\d{2}', function (d, v) { |
| 178 | + d.millisecond = v * 10; |
| 179 | + }], |
| 180 | + SSS: [threeDigits, function (d, v) { |
| 181 | + d.millisecond = v; |
| 182 | + }], |
| 183 | + d: [twoDigits, noop], |
| 184 | + ddd: [word, noop], |
| 185 | + MMM: [word, monthUpdate('monthNamesShort')], |
| 186 | + MMMM: [word, monthUpdate('monthNames')], |
| 187 | + a: [word, function (d, v, i18n) { |
| 188 | + var val = v.toLowerCase(); |
| 189 | + if (val === i18n.amPm[0]) { |
| 190 | + d.isPm = false; |
| 191 | + } else if (val === i18n.amPm[1]) { |
| 192 | + d.isPm = true; |
| 193 | + } |
| 194 | + }], |
| 195 | + ZZ: ['[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d', function (d, v) { |
| 196 | + var parts = (v + '').match(/([\+\-]|\d\d)/gi), minutes; |
| 197 | + |
| 198 | + if (parts) { |
| 199 | + minutes = +(parts[1] * 60) + parseInt(parts[2], 10); |
| 200 | + d.timezoneOffset = parts[0] === '+' ? minutes : -minutes; |
| 201 | + } |
| 202 | + }] |
| 203 | + }; |
| 204 | + parseFlags.dd = parseFlags.d; |
| 205 | + parseFlags.dddd = parseFlags.ddd; |
| 206 | + parseFlags.DD = parseFlags.D; |
| 207 | + parseFlags.mm = parseFlags.m; |
| 208 | + parseFlags.hh = parseFlags.H = parseFlags.HH = parseFlags.h; |
| 209 | + parseFlags.MM = parseFlags.M; |
| 210 | + parseFlags.ss = parseFlags.s; |
| 211 | + parseFlags.A = parseFlags.a; |
| 212 | + |
| 213 | + |
| 214 | + // Some common format strings |
| 215 | + fecha.masks = { |
| 216 | + default: 'ddd MMM DD YYYY HH:mm:ss', |
| 217 | + shortDate: 'M/D/YY', |
| 218 | + mediumDate: 'MMM D, YYYY', |
| 219 | + longDate: 'MMMM D, YYYY', |
| 220 | + fullDate: 'dddd, MMMM D, YYYY', |
| 221 | + shortTime: 'HH:mm', |
| 222 | + mediumTime: 'HH:mm:ss', |
| 223 | + longTime: 'HH:mm:ss.SSS' |
| 224 | + }; |
| 225 | + |
| 226 | + /*** |
| 227 | + * Format a date |
| 228 | + * @method format |
| 229 | + * @param {Date|number} dateObj |
| 230 | + * @param {string} mask Format of the date, i.e. 'mm-dd-yy' or 'shortDate' |
| 231 | + */ |
| 232 | + fecha.format = function (dateObj, mask, i18nSettings) { |
| 233 | + var i18n = i18nSettings || fecha.i18n; |
| 234 | + |
| 235 | + if (typeof dateObj === 'number') { |
| 236 | + dateObj = new Date(dateObj); |
| 237 | + } |
| 238 | + |
| 239 | + if (Object.prototype.toString.call(dateObj) !== '[object Date]' || isNaN(dateObj.getTime())) { |
| 240 | + throw new Error('Invalid Date in fecha.format'); |
| 241 | + } |
| 242 | + |
| 243 | + mask = fecha.masks[mask] || mask || fecha.masks['default']; |
| 244 | + |
| 245 | + var literals = []; |
| 246 | + |
| 247 | + // Make literals inactive by replacing them with ?? |
| 248 | + mask = mask.replace(literal, function($0, $1) { |
| 249 | + literals.push($1); |
| 250 | + return '??'; |
| 251 | + }); |
| 252 | + // Apply formatting rules |
| 253 | + mask = mask.replace(token, function ($0) { |
| 254 | + return $0 in formatFlags ? formatFlags[$0](dateObj, i18n) : $0.slice(1, $0.length - 1); |
| 255 | + }); |
| 256 | + // Inline literal values back into the formatted value |
| 257 | + return mask.replace(/\?\?/g, function() { |
| 258 | + return literals.shift(); |
| 259 | + }); |
| 260 | + }; |
| 261 | + |
| 262 | + /** |
| 263 | + * Parse a date string into an object, changes - into / |
| 264 | + * @method parse |
| 265 | + * @param {string} dateStr Date string |
| 266 | + * @param {string} format Date parse format |
| 267 | + * @returns {Date|boolean} |
| 268 | + */ |
| 269 | + fecha.parse = function (dateStr, format, i18nSettings) { |
| 270 | + var i18n = i18nSettings || fecha.i18n; |
| 271 | + |
| 272 | + if (typeof format !== 'string') { |
| 273 | + throw new Error('Invalid format in fecha.parse'); |
| 274 | + } |
| 275 | + |
| 276 | + format = fecha.masks[format] || format; |
| 277 | + |
| 278 | + // Avoid regular expression denial of service, fail early for really long strings |
| 279 | + // https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS |
| 280 | + if (dateStr.length > 1000) { |
| 281 | + return null; |
| 282 | + } |
| 283 | + |
| 284 | + var dateInfo = {}; |
| 285 | + var parseInfo = []; |
| 286 | + var newFormat = regexEscape(format).replace(token, function ($0) { |
| 287 | + if (parseFlags[$0]) { |
| 288 | + var info = parseFlags[$0]; |
| 289 | + parseInfo.push(info[1]); |
| 290 | + return '(' + info[0] + ')'; |
| 291 | + } |
| 292 | + |
| 293 | + return $0; |
| 294 | + }); |
| 295 | + var matches = dateStr.match(new RegExp(newFormat, 'i')); |
| 296 | + if (!matches) { |
| 297 | + return null; |
| 298 | + } |
| 299 | + |
| 300 | + for (var i = 1; i < matches.length; i++) { |
| 301 | + parseInfo[i - 1](dateInfo, matches[i], i18n); |
| 302 | + } |
| 303 | + |
| 304 | + var today = new Date(); |
| 305 | + if (dateInfo.isPm === true && dateInfo.hour != null && +dateInfo.hour !== 12) { |
| 306 | + dateInfo.hour = +dateInfo.hour + 12; |
| 307 | + } else if (dateInfo.isPm === false && +dateInfo.hour === 12) { |
| 308 | + dateInfo.hour = 0; |
| 309 | + } |
| 310 | + |
| 311 | + var date; |
| 312 | + if (dateInfo.timezoneOffset != null) { |
| 313 | + dateInfo.minute = +(dateInfo.minute || 0) - +dateInfo.timezoneOffset; |
| 314 | + date = new Date(Date.UTC(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, |
| 315 | + dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0)); |
| 316 | + } else { |
| 317 | + date = new Date(dateInfo.year || today.getFullYear(), dateInfo.month || 0, dateInfo.day || 1, |
| 318 | + dateInfo.hour || 0, dateInfo.minute || 0, dateInfo.second || 0, dateInfo.millisecond || 0); |
| 319 | + } |
| 320 | + return date; |
| 321 | + }; |
| 322 | + |
| 323 | + /* istanbul ignore next */ |
| 324 | + if (typeof module !== 'undefined' && module.exports) { |
| 325 | + module.exports = fecha; |
| 326 | + } else if (typeof define === 'function' && define.amd) { |
| 327 | + define(function () { |
| 328 | + return fecha; |
| 329 | + }); |
| 330 | + } else { |
| 331 | + main.fecha = fecha; |
| 332 | + } |
| 333 | +})(this); |
0 commit comments