1 2 /** 3 * @fileOverview Date parsing and formatting operations without extending the Date built-in object. 4 * @author Chris Leonello 5 * @version #VERSION# 6 */ 7 8 (function() { 9 10 /** 11 * @description 12 * <p>Object with extended date parsing and formatting capabilities. 13 * This library borrows many concepts and ideas from the Date Instance 14 * Methods by Ken Snyder along with some parts of Ken's actual code.</p> 15 * 16 * <p>jsDate is fundamentally a new implementation, however. I takes a 17 * different approach by not extending the built-in Date Object, handling 18 * date parsing in a more robust manner, allowing for multiple formatting 19 * syntaxes and multiple, expandable localization.<p> 20 * 21 * <p>Ken's origianl Date Instance Methods can be found at: 22 * 23 * http://kendsnyder.com/sandbox/date/ 24 * 25 * @class 26 * @name jsDate 27 * @param {String | Number | Array | Date Object | Options Object} arguments Optional arguments, either a parsable date/time string, 28 * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], 29 * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional. 30 */ 31 32 var jsDate = function () { 33 34 this.syntax = jsDate.config.syntax; 35 this._type = "jsDate"; 36 this.utcOffset = new Date().getTimezoneOffset * 60000; 37 this.proxy = new Date(); 38 this.options = {}; 39 this.locale = jsDate.regional.getLocale(); 40 this.formatString = ''; 41 42 switch ( arguments.length ) { 43 case 0: 44 break; 45 case 1: 46 // other objects either won't have a _type property or, 47 // if they do, it shouldn't be set to "jsDate", so 48 // assume it is an options argument. 49 if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { 50 var opts = this.options = arguments[0]; 51 this.syntax= opts.syntax; 52 this.proxy = jsDate.createDate(opts.date); 53 } 54 else { 55 this.proxy = jsDate.createDate(arguments[0]); 56 } 57 break; 58 default: 59 var a = []; 60 for ( var i=0; i<arguments.length; i++ ) { 61 a.push(arguments[i]); 62 } 63 this.proxy = new Date( this.utcOffset ); 64 this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); 65 if ( a.slice(3).length ) { 66 this.proxy.setHours.apply( this.proxy, a.slice(3) ); 67 } 68 break; 69 } 70 }; 71 72 /** 73 * @namespace Configuration options that will be used as defaults for all instances on the page. 74 * @property {String} defaultLocale The default locale to use [en]. 75 * @property {String} syntax The default syntax to use [perl]. 76 */ 77 jsDate.config = { 78 defaultLocale: 'en', 79 syntax: 'perl' 80 }; 81 82 /** 83 * Add an arbitrary amount to the currently stored date 84 * 85 * @param {Number} number 86 * @param {String} unit 87 * @returns {jsDate} 88 */ 89 90 jsDate.prototype.add = function(number, unit) { 91 var factor = multipliers[unit] || multipliers.day; 92 if (typeof factor == 'number') { 93 this.proxy.setTime(this.proxy.getTime() + (factor * number)); 94 } else { 95 factor.add(this, number); 96 } 97 return this; 98 }; 99 100 /** 101 * Create a new jqplot.date object with the same date 102 * 103 * @returns {jsDate} 104 */ 105 106 jsDate.prototype.clone = function() { 107 return new jsDate(this.proxy.getTime()); 108 }; 109 110 /** 111 * Find the difference between this jsDate and another date. 112 * 113 * @param {String| Number| Array| jsDate Object| Date Object} dateObj 114 * @param {String} unit 115 * @param {Boolean} allowDecimal 116 * @returns {Number} Number of units difference between dates. 117 */ 118 119 jsDate.prototype.diff = function(dateObj, unit, allowDecimal) { 120 // ensure we have a Date object 121 dateObj = new jsDate(dateObj); 122 if (dateObj === null) { 123 return null; 124 } 125 // get the multiplying factor integer or factor function 126 var factor = multipliers[unit] || multipliers.day; 127 if (typeof factor == 'number') { 128 // multiply 129 var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor; 130 } else { 131 // run function 132 var unitDiff = factor.diff(this.proxy, dateObj.proxy); 133 } 134 // if decimals are not allowed, round toward zero 135 return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff)); 136 }; 137 138 /** 139 * Get the abbreviated name of the current week day 140 * 141 * @returns {String} 142 */ 143 144 jsDate.prototype.getAbbrDayName = function() { 145 return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()]; 146 }; 147 148 /** 149 * Get the abbreviated name of the current month 150 * 151 * @returns {String} 152 */ 153 154 jsDate.prototype.getAbbrMonthName = function() { 155 return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()]; 156 }; 157 158 /** 159 * Get UPPER CASE AM or PM for the current time 160 * 161 * @returns {String} 162 */ 163 164 jsDate.prototype.getAMPM = function() { 165 return this.proxy.getHours() >= 12 ? 'PM' : 'AM'; 166 }; 167 168 /** 169 * Get lower case am or pm for the current time 170 * 171 * @returns {String} 172 */ 173 174 jsDate.prototype.getAmPm = function() { 175 return this.proxy.getHours() >= 12 ? 'pm' : 'am'; 176 }; 177 178 /** 179 * Get the century (19 for 20th Century) 180 * 181 * @returns {Integer} Century (19 for 20th century). 182 */ 183 jsDate.prototype.getCentury = function() { 184 return parseInt(this.proxy.getFullYear()/100, 10); 185 }; 186 187 /** 188 * Implements Date functionality 189 */ 190 jsDate.prototype.getDate = function() { 191 return this.proxy.getDate(); 192 }; 193 194 /** 195 * Implements Date functionality 196 */ 197 jsDate.prototype.getDay = function() { 198 return this.proxy.getDay(); 199 }; 200 201 /** 202 * Get the Day of week 1 (Monday) thru 7 (Sunday) 203 * 204 * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday) 205 */ 206 jsDate.prototype.getDayOfWeek = function() { 207 var dow = this.proxy.getDay(); 208 return dow===0?7:dow; 209 }; 210 211 /** 212 * Get the day of the year 213 * 214 * @returns {Integer} 1 - 366, day of the year 215 */ 216 jsDate.prototype.getDayOfYear = function() { 217 var d = this.proxy; 218 var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT'); 219 ms += d.getTimezoneOffset()*60000; 220 d = null; 221 return parseInt(ms/60000/60/24, 10)+1; 222 }; 223 224 /** 225 * Get the name of the current week day 226 * 227 * @returns {String} 228 */ 229 230 jsDate.prototype.getDayName = function() { 231 return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()]; 232 }; 233 234 /** 235 * Get the week number of the given year, starting with the first Sunday as the first week 236 * @returns {Integer} Week number (13 for the 13th full week of the year). 237 */ 238 jsDate.prototype.getFullWeekOfYear = function() { 239 var d = this.proxy; 240 var doy = this.getDayOfYear(); 241 var rdow = 6-d.getDay(); 242 var woy = parseInt((doy+rdow)/7, 10); 243 return woy; 244 }; 245 246 /** 247 * Implements Date functionality 248 */ 249 jsDate.prototype.getFullYear = function() { 250 return this.proxy.getFullYear(); 251 }; 252 253 /** 254 * Get the GMT offset in hours and minutes (e.g. +06:30) 255 * 256 * @returns {String} 257 */ 258 259 jsDate.prototype.getGmtOffset = function() { 260 // divide the minutes offset by 60 261 var hours = this.proxy.getTimezoneOffset() / 60; 262 // decide if we are ahead of or behind GMT 263 var prefix = hours < 0 ? '+' : '-'; 264 // remove the negative sign if any 265 hours = Math.abs(hours); 266 // add the +/- to the padded number of hours to : to the padded minutes 267 return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2); 268 }; 269 270 /** 271 * Implements Date functionality 272 */ 273 jsDate.prototype.getHours = function() { 274 return this.proxy.getHours(); 275 }; 276 277 /** 278 * Get the current hour on a 12-hour scheme 279 * 280 * @returns {Integer} 281 */ 282 283 jsDate.prototype.getHours12 = function() { 284 var hours = this.proxy.getHours(); 285 return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours); 286 }; 287 288 289 jsDate.prototype.getIsoWeek = function() { 290 var d = this.proxy; 291 var woy = d.getWeekOfYear(); 292 var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); 293 // First week is 01 and not 00 as in the case of %U and %W, 294 // so we add 1 to the final result except if day 1 of the year 295 // is a Monday (then %W returns 01). 296 // We also need to subtract 1 if the day 1 of the year is 297 // Friday-Sunday, so the resulting equation becomes: 298 var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); 299 if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) 300 { 301 idow = 1; 302 } 303 else if(idow === 0) 304 { 305 d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31')); 306 idow = d.getIsoWeek(); 307 } 308 d = null; 309 return idow; 310 }; 311 312 /** 313 * Implements Date functionality 314 */ 315 jsDate.prototype.getMilliseconds = function() { 316 return this.proxy.getMilliseconds(); 317 }; 318 319 /** 320 * Implements Date functionality 321 */ 322 jsDate.prototype.getMinutes = function() { 323 return this.proxy.getMinutes(); 324 }; 325 326 /** 327 * Implements Date functionality 328 */ 329 jsDate.prototype.getMonth = function() { 330 return this.proxy.getMonth(); 331 }; 332 333 /** 334 * Get the name of the current month 335 * 336 * @returns {String} 337 */ 338 339 jsDate.prototype.getMonthName = function() { 340 return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()]; 341 }; 342 343 /** 344 * Get the number of the current month, 1-12 345 * 346 * @returns {Integer} 347 */ 348 349 jsDate.prototype.getMonthNumber = function() { 350 return this.proxy.getMonth() + 1; 351 }; 352 353 /** 354 * Implements Date functionality 355 */ 356 jsDate.prototype.getSeconds = function() { 357 return this.proxy.getSeconds(); 358 }; 359 360 /** 361 * Return a proper two-digit year integer 362 * 363 * @returns {Integer} 364 */ 365 366 jsDate.prototype.getShortYear = function() { 367 return this.proxy.getYear() % 100; 368 }; 369 370 /** 371 * Implements Date functionality 372 */ 373 jsDate.prototype.getTime = function() { 374 return this.proxy.getTime(); 375 }; 376 377 /** 378 * Get the timezone abbreviation 379 * 380 * @returns {String} Abbreviation for the timezone 381 */ 382 jsDate.prototype.getTimezoneAbbr = function() { 383 return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); 384 }; 385 386 /** 387 * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time) 388 * 389 * @returns {String} 390 */ 391 jsDate.prototype.getTimezoneName = function() { 392 var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString()); 393 return match[1] || match[2] || 'GMT' + this.getGmtOffset(); 394 }; 395 396 /** 397 * Implements Date functionality 398 */ 399 jsDate.prototype.getTimezoneOffset = function() { 400 return this.proxy.getTimezoneOffset(); 401 }; 402 403 404 /** 405 * Get the week number of the given year, starting with the first Monday as the first week 406 * @returns {Integer} Week number (13 for the 13th week of the year). 407 */ 408 jsDate.prototype.getWeekOfYear = function() { 409 var doy = this.getDayOfYear(); 410 var rdow = 7 - this.getDayOfWeek(); 411 var woy = parseInt((doy+rdow)/7, 10); 412 return woy; 413 }; 414 415 /** 416 * Get the current date as a Unix timestamp 417 * 418 * @returns {Integer} 419 */ 420 421 jsDate.prototype.getUnix = function() { 422 return Math.round(this.proxy.getTime() / 1000, 0); 423 }; 424 425 /** 426 * Implements Date functionality 427 */ 428 jsDate.prototype.getYear = function() { 429 return this.proxy.getYear(); 430 }; 431 432 /** 433 * Return a date one day ahead (or any other unit) 434 * 435 * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond 436 * @returns {jsDate} 437 */ 438 439 jsDate.prototype.next = function(unit) { 440 unit = unit || 'day' 441 return this.clone().add(1, unit); 442 }; 443 444 /** 445 * Set the jsDate instance to a new date. 446 * 447 * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, 448 * either a parsable date/time string, 449 * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], 450 * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional. 451 */ 452 jsDate.prototype.set = function() { 453 switch ( arguments.length ) { 454 case 0: 455 this.proxy = new Date(); 456 case 1: 457 // other objects either won't have a _type property or, 458 // if they do, it shouldn't be set to "jsDate", so 459 // assume it is an options argument. 460 if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { 461 var opts = this.options = arguments[0]; 462 this.syntax= opts.syntax; 463 this.proxy = jsDate.createDate(opts.date); 464 } 465 else { 466 this.proxy = jsDate.createDate(arguments[0]); 467 } 468 break; 469 default: 470 var a = []; 471 for ( var i=0; i<arguments.length; i++ ) { 472 a.push(arguments[i]); 473 } 474 this.proxy = new Date( this.utcOffset ); 475 this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); 476 if ( a.slice(3).length ) { 477 this.proxy.setHours.apply( this.proxy, a.slice(3) ); 478 } 479 break; 480 } 481 }; 482 483 /** 484 * Sets the day of the month for a specified date according to local time. 485 * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. 486 */ 487 jsDate.prototype.setDate = function(n) { 488 this.proxy.setDate(n); 489 }; 490 491 /** 492 * Sets the full year for a specified date according to local time. 493 * @param {Integer} yearValue The numeric value of the year, for example, 1995. 494 * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December. 495 * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue. 496 */ 497 jsDate.prototype.setFullYear = function() { 498 this.proxy.setFullYear.apply(this.proxy, arguments); 499 }; 500 501 /** 502 * Sets the hours for a specified date according to local time. 503 * 504 * @param {Integer} hoursValue An integer between 0 and 23, representing the hour. 505 * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes. 506 * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. 507 * If you specify the secondsValue parameter, you must also specify the minutesValue. 508 * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. 509 * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. 510 */ 511 jsDate.prototype.setHours = function() { 512 this.proxy.setHours.apply(this.proxy, arguments); 513 }; 514 515 /** 516 * Implements Date functionality 517 */ 518 jsDate.prototype.setMilliseconds = function(n) { 519 this.proxy.setMilliseconds(n); 520 }; 521 522 /** 523 * Implements Date functionality 524 */ 525 jsDate.prototype.setMinutes = function() { 526 this.proxy.setMinutes.apply(this.proxy, arguments); 527 }; 528 529 /** 530 * Implements Date functionality 531 */ 532 jsDate.prototype.setMonth = function() { 533 this.proxy.setMonth.apply(this.proxy, arguments); 534 }; 535 536 /** 537 * Implements Date functionality 538 */ 539 jsDate.prototype.setSeconds = function() { 540 this.proxy.setSeconds.apply(this.proxy, arguments); 541 }; 542 543 /** 544 * Implements Date functionality 545 */ 546 jsDate.prototype.setTime = function(n) { 547 this.proxy.setTime(n); 548 }; 549 550 /** 551 * Provide a formatted string representation of this date. 552 * 553 * @param {String} formatString A format string. 554 * See: {@link jsDate.formats}. 555 * @returns {String} Date String. 556 */ 557 558 jsDate.prototype.strftime = function(formatString) { 559 formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString']; 560 return jsDate.strftime(this, formatString, this.syntax); 561 }; 562 563 /** 564 * Return a String representation of this jsDate object. 565 * @returns {String} Date string. 566 */ 567 568 jsDate.prototype.toString = function() { 569 return this.proxy.toString(); 570 }; 571 572 /** 573 * Convert the current date to an 8-digit integer (%Y%m%d) 574 * 575 * @returns {Integer} 576 */ 577 578 jsDate.prototype.toYmdInt = function() { 579 return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate(); 580 }; 581 582 /** 583 * @namespace Holds localizations for month/day names. 584 * <p>jsDate attempts to detect locale when loaded and defaults to 'en'. 585 * If a localization is detected which is not available, jsDate defaults to 'en'. 586 * Additional localizations can be added after jsDate loads. After adding a localization, 587 * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p> 588 * 589 * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p> 590 * <pre class="code"> 591 * jsDate.regional['en'] = { 592 * monthNames : 'January February March April May June July August September October November December'.split(' '), 593 * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), 594 * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), 595 * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ') 596 * }; 597 * </pre> 598 * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the 599 * new localizations.</p> 600 */ 601 602 jsDate.regional = { 603 'en': { 604 monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], 605 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 606 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 607 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], 608 formatString: '%Y-%m-%d %H:%M:%S' 609 }, 610 611 'fr': { 612 monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'], 613 monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'], 614 dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], 615 dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], 616 formatString: '%Y-%m-%d %H:%M:%S' 617 }, 618 619 'de': { 620 monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'], 621 monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'], 622 dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], 623 dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], 624 formatString: '%Y-%m-%d %H:%M:%S' 625 }, 626 627 'es': { 628 monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], 629 monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'], 630 dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], 631 dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], 632 formatString: '%Y-%m-%d %H:%M:%S' 633 }, 634 635 'ru': { 636 monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'], 637 monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'], 638 dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'], 639 dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'], 640 formatString: '%Y-%m-%d %H:%M:%S' 641 }, 642 643 'ar': { 644 monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], 645 monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], 646 dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'], 647 dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'], 648 formatString: '%Y-%m-%d %H:%M:%S' 649 }, 650 651 'pt': { 652 monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], 653 monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], 654 dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], 655 dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], 656 formatString: '%Y-%m-%d %H:%M:%S' 657 }, 658 659 'pt-BR': { 660 monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], 661 monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], 662 dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], 663 dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], 664 formatString: '%Y-%m-%d %H:%M:%S' 665 } 666 667 668 }; 669 670 // Set english variants to 'en' 671 jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en']; 672 673 /** 674 * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en' 675 * if it cannot figure out a locale of if the locale does not have a localization defined. 676 * @returns {String} locale 677 */ 678 679 jsDate.regional.getLocale = function () { 680 var l = jsDate.config.defaultLocale; 681 682 if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) { 683 l = document.getElementsByTagName('html')[0].lang; 684 if (!jsDate.regional.hasOwnProperty(l)) { 685 l = jsDate.config.defaultLocale; 686 } 687 } 688 689 return l; 690 }; 691 692 // ms in day 693 var day = 24 * 60 * 60 * 1000; 694 695 // padd a number with zeros 696 var addZeros = function(num, digits) { 697 num = String(num); 698 var i = digits - num.length; 699 var s = String(Math.pow(10, i)).slice(1); 700 return s.concat(num); 701 }; 702 703 // representations used for calculating differences between dates. 704 // This borrows heavily from Ken Snyder's work. 705 var multipliers = { 706 millisecond: 1, 707 second: 1000, 708 minute: 60 * 1000, 709 hour: 60 * 60 * 1000, 710 day: day, 711 week: 7 * day, 712 month: { 713 // add a number of months 714 add: function(d, number) { 715 // add any years needed (increments of 12) 716 multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12)); 717 // ensure that we properly wrap betwen December and January 718 var prevMonth = d.getMonth() + (number % 12); 719 if (prevMonth == 12) { 720 prevMonth = 0; 721 d.setYear(d.getFullYear() + 1); 722 } else if (prevMonth == -1) { 723 prevMonth = 11; 724 d.setYear(d.getFullYear() - 1); 725 } 726 d.setMonth(prevMonth); 727 }, 728 // get the number of months between two Date objects (decimal to the nearest day) 729 diff: function(d1, d2) { 730 // get the number of years 731 var diffYears = d1.getFullYear() - d2.getFullYear(); 732 // get the number of remaining months 733 var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12); 734 // get the number of remaining days 735 var diffDays = d1.getDate() - d2.getDate(); 736 // return the month difference with the days difference as a decimal 737 return diffMonths + (diffDays / 30); 738 } 739 }, 740 year: { 741 // add a number of years 742 add: function(d, number) { 743 d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number)); 744 }, 745 // get the number of years between two Date objects (decimal to the nearest day) 746 diff: function(d1, d2) { 747 return multipliers.month.diff(d1, d2) / 12; 748 } 749 } 750 }; 751 // 752 // Alias each multiplier with an 's' to allow 'year' and 'years' for example. 753 // This comes from Ken Snyders work. 754 // 755 for (var unit in multipliers) { 756 if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :| 757 multipliers[unit + 's'] = multipliers[unit]; 758 } 759 } 760 761 // 762 // take a jsDate instance and a format code and return the formatted value. 763 // This is a somewhat modified version of Ken Snyder's method. 764 // 765 var format = function(d, code, syntax) { 766 // if shorcut codes are used, recursively expand those. 767 if (jsDate.formats[syntax]["shortcuts"][code]) { 768 return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax); 769 } else { 770 // get the format code function and addZeros() argument 771 var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.'); 772 var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : ''; 773 if (getter[1]) { 774 nbr = addZeros(nbr, getter[1]); 775 } 776 return nbr; 777 } 778 }; 779 780 /** 781 * @static 782 * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes. 783 * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p> 784 * <pre class="code"> 785 * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S'); 786 * </pre> 787 * @param {String | Number | Array | jsDate Object | Date Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object. 788 * @param {String} formatString String with embedded date formatting codes. 789 * See: {@link jsDate.formats}. 790 * @param {String} syntax Optional syntax to use [default perl]. 791 * @param {String} locale Optional locale to use. 792 * @returns {String} Formatted representation of the date. 793 */ 794 // 795 // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods. 796 // 797 jsDate.strftime = function(d, formatString, syntax, locale) { 798 var syn = 'perl'; 799 var loc = jsDate.regional.getLocale(); 800 801 // check if syntax and locale are available or reversed 802 if (syntax && jsDate.formats.hasOwnProperty(syntax)) { 803 syn = syntax; 804 } 805 else if (syntax && jsDate.regional.hasOwnProperty(syntax)) { 806 loc = syntax; 807 } 808 809 if (locale && jsDate.formats.hasOwnProperty(locale)) { 810 syn = locale; 811 } 812 else if (locale && jsDate.regional.hasOwnProperty(locale)) { 813 loc = locale; 814 } 815 816 if (get_type(d) != "[object Object]" || d._type != "jsDate") { 817 d = new jsDate(d); 818 d.locale = loc; 819 } 820 if (!formatString) { 821 formatString = d.formatString || jsDate.regional[loc]['formatString']; 822 } 823 // default the format string to year-month-day 824 var source = formatString || '%Y-%m-%d', 825 result = '', 826 match; 827 // replace each format code 828 while (source.length > 0) { 829 if (match = source.match(jsDate.formats[syn].codes.matcher)) { 830 result += source.slice(0, match.index); 831 result += (match[1] || '') + format(d, match[2], syn); 832 source = source.slice(match.index + match[0].length); 833 } else { 834 result += source; 835 source = ''; 836 } 837 } 838 return result; 839 }; 840 841 /** 842 * @namespace 843 * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes 844 * and shortcuts are defined by default. Additional codes and shortcuts can be 845 * added like: 846 * 847 * <pre class="code"> 848 * jsDate.formats["perl"] = { 849 * "codes": { 850 * matcher: /someregex/, 851 * Y: "fullYear", // name of "get" method without the "get", 852 * ..., // more codes 853 * }, 854 * "shortcuts": { 855 * F: '%Y-%m-%d', 856 * ..., // more shortcuts 857 * } 858 * }; 859 * </pre> 860 * 861 * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via: 862 * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code> 863 */ 864 865 jsDate.formats = { 866 ISO:'%Y-%m-%dT%H:%M:%S.%N%G', 867 SQL:'%Y-%m-%d %H:%M:%S' 868 }; 869 870 /** 871 * Perl format codes and shortcuts for strftime. 872 * 873 * A hash (object) of codes where each code must be an array where the first member is 874 * the name of a Date.prototype or jsDate.prototype function to call 875 * and optionally a second member indicating the number to pass to addZeros() 876 * 877 * <p>The following format codes are defined:</p> 878 * 879 * <pre class="code"> 880 * Code Result Description 881 * == Years == 882 * %Y 2008 Four-digit year 883 * %y 08 Two-digit year 884 * 885 * == Months == 886 * %m 09 Two-digit month 887 * %#m 9 One or two-digit month 888 * %B September Full month name 889 * %b Sep Abbreviated month name 890 * 891 * == Days == 892 * %d 05 Two-digit day of month 893 * %#d 5 One or two-digit day of month 894 * %e 5 One or two-digit day of month 895 * %A Sunday Full name of the day of the week 896 * %a Sun Abbreviated name of the day of the week 897 * %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) 898 * 899 * == Hours == 900 * %H 23 Hours in 24-hour format (two digits) 901 * %#H 3 Hours in 24-hour integer format (one or two digits) 902 * %I 11 Hours in 12-hour format (two digits) 903 * %#I 3 Hours in 12-hour integer format (one or two digits) 904 * %p PM AM or PM 905 * 906 * == Minutes == 907 * %M 09 Minutes (two digits) 908 * %#M 9 Minutes (one or two digits) 909 * 910 * == Seconds == 911 * %S 02 Seconds (two digits) 912 * %#S 2 Seconds (one or two digits) 913 * %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) 914 * 915 * == Milliseconds == 916 * %N 008 Milliseconds (three digits) 917 * %#N 8 Milliseconds (one to three digits) 918 * 919 * == Timezone == 920 * %O 360 difference in minutes between local time and GMT 921 * %Z Mountain Standard Time Name of timezone as reported by browser 922 * %G 06:00 Hours and minutes between GMT 923 * 924 * == Shortcuts == 925 * %F 2008-03-26 %Y-%m-%d 926 * %T 05:06:30 %H:%M:%S 927 * %X 05:06:30 %H:%M:%S 928 * %x 03/26/08 %m/%d/%y 929 * %D 03/26/08 %m/%d/%y 930 * %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y 931 * %v 3-Sep-2008 %e-%b-%Y 932 * %R 15:31 %H:%M 933 * %r 03:31:00 PM %I:%M:%S %p 934 * 935 * == Characters == 936 * %n \n Newline 937 * %t \t Tab 938 * %% % Percent Symbol 939 * </pre> 940 * 941 * <p>Formatting shortcuts that will be translated into their longer version. 942 * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p> 943 * 944 * <p>Format codes and format shortcuts can be redefined after the jsDate 945 * module is imported.</p> 946 * 947 * <p>Note that if you redefine the whole hash (object), you must supply a "matcher" 948 * regex for the parser. The default matcher is:</p> 949 * 950 * <code>/()%(#?(%|[a-z]))/i</code> 951 * 952 * <p>which corresponds to the Perl syntax used by default.</p> 953 * 954 * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p> 955 */ 956 957 jsDate.formats.perl = { 958 codes: { 959 // 960 // 2-part regex matcher for format codes 961 // 962 // first match must be the character before the code (to account for escaping) 963 // second match must be the format code character(s) 964 // 965 matcher: /()%(#?(%|[a-z]))/i, 966 // year 967 Y: 'FullYear', 968 y: 'ShortYear.2', 969 // month 970 m: 'MonthNumber.2', 971 '#m': 'MonthNumber', 972 B: 'MonthName', 973 b: 'AbbrMonthName', 974 // day 975 d: 'Date.2', 976 '#d': 'Date', 977 e: 'Date', 978 A: 'DayName', 979 a: 'AbbrDayName', 980 w: 'Day', 981 // hours 982 H: 'Hours.2', 983 '#H': 'Hours', 984 I: 'Hours12.2', 985 '#I': 'Hours12', 986 p: 'AMPM', 987 // minutes 988 M: 'Minutes.2', 989 '#M': 'Minutes', 990 // seconds 991 S: 'Seconds.2', 992 '#S': 'Seconds', 993 s: 'Unix', 994 // milliseconds 995 N: 'Milliseconds.3', 996 '#N': 'Milliseconds', 997 // timezone 998 O: 'TimezoneOffset', 999 Z: 'TimezoneName', 1000 G: 'GmtOffset' 1001 }, 1002 1003 shortcuts: { 1004 // date 1005 F: '%Y-%m-%d', 1006 // time 1007 T: '%H:%M:%S', 1008 X: '%H:%M:%S', 1009 // local format date 1010 x: '%m/%d/%y', 1011 D: '%m/%d/%y', 1012 // local format extended 1013 '#c': '%a %b %e %H:%M:%S %Y', 1014 // local format short 1015 v: '%e-%b-%Y', 1016 R: '%H:%M', 1017 r: '%I:%M:%S %p', 1018 // tab and newline 1019 t: '\t', 1020 n: '\n', 1021 '%': '%' 1022 } 1023 }; 1024 1025 /** 1026 * PHP format codes and shortcuts for strftime. 1027 * 1028 * A hash (object) of codes where each code must be an array where the first member is 1029 * the name of a Date.prototype or jsDate.prototype function to call 1030 * and optionally a second member indicating the number to pass to addZeros() 1031 * 1032 * <p>The following format codes are defined:</p> 1033 * 1034 * <pre class="code"> 1035 * Code Result Description 1036 * === Days === 1037 * %a Sun through Sat An abbreviated textual representation of the day 1038 * %A Sunday - Saturday A full textual representation of the day 1039 * %d 01 to 31 Two-digit day of the month (with leading zeros) 1040 * %e 1 to 31 Day of the month, with a space preceding single digits. 1041 * %j 001 to 366 Day of the year, 3 digits with leading zeros 1042 * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week 1043 * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week 1044 * 1045 * === Week === 1046 * %U 13 Full Week number, starting with the first Sunday as the first week 1047 * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year 1048 * with at least 4 weekdays, with Monday being the start of the week 1049 * %W 46 A numeric representation of the week of the year, 1050 * starting with the first Monday as the first week 1051 * === Month === 1052 * %b Jan through Dec Abbreviated month name, based on the locale 1053 * %B January - December Full month name, based on the locale 1054 * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b) 1055 * %m 01 - 12 (Jan - Dec) Two digit representation of the month 1056 * 1057 * === Year === 1058 * %C 19 Two digit century (year/100, truncated to an integer) 1059 * %y 09 for 2009 Two digit year 1060 * %Y 2038 Four digit year 1061 * 1062 * === Time === 1063 * %H 00 through 23 Two digit representation of the hour in 24-hour format 1064 * %I 01 through 12 Two digit representation of the hour in 12-hour format 1065 * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits 1066 * %M 00 through 59 Two digit representation of the minute 1067 * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time 1068 * %P am/pm lower-case 'am' or 'pm' based on the given time 1069 * %r 09:34:17 PM Same as %I:%M:%S %p 1070 * %R 00:35 Same as %H:%M 1071 * %S 00 through 59 Two digit representation of the second 1072 * %T 21:34:17 Same as %H:%M:%S 1073 * %X 03:59:16 Preferred time representation based on locale, without the date 1074 * %z -0500 or EST Either the time zone offset from UTC or the abbreviation 1075 * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z 1076 * 1077 * === Time and Date === 1078 * %D 02/05/09 Same as %m/%d/%y 1079 * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps) 1080 * %s 305815200 Unix Epoch Time timestamp (same as the time() function) 1081 * %x 02/05/09 Preferred date representation, without the time 1082 * 1083 * === Miscellaneous === 1084 * %n --- A newline character (\n) 1085 * %t --- A Tab character (\t) 1086 * %% --- A literal percentage character (%) 1087 * </pre> 1088 */ 1089 1090 jsDate.formats.php = { 1091 codes: { 1092 // 1093 // 2-part regex matcher for format codes 1094 // 1095 // first match must be the character before the code (to account for escaping) 1096 // second match must be the format code character(s) 1097 // 1098 matcher: /()%((%|[a-z]))/i, 1099 // day 1100 a: 'AbbrDayName', 1101 A: 'DayName', 1102 d: 'Date.2', 1103 e: 'Date', 1104 j: 'DayOfYear.3', 1105 u: 'DayOfWeek', 1106 w: 'Day', 1107 // week 1108 U: 'FullWeekOfYear.2', 1109 V: 'IsoWeek.2', 1110 W: 'WeekOfYear.2', 1111 // month 1112 b: 'AbbrMonthName', 1113 B: 'MonthName', 1114 m: 'MonthNumber.2', 1115 h: 'AbbrMonthName', 1116 // year 1117 C: 'Century.2', 1118 y: 'ShortYear.2', 1119 Y: 'FullYear', 1120 // time 1121 H: 'Hours.2', 1122 I: 'Hours12.2', 1123 l: 'Hours12', 1124 p: 'AMPM', 1125 P: 'AmPm', 1126 M: 'Minutes.2', 1127 S: 'Seconds.2', 1128 s: 'Unix', 1129 O: 'TimezoneOffset', 1130 z: 'GmtOffset', 1131 Z: 'TimezoneAbbr', 1132 }, 1133 1134 shortcuts: { 1135 D: '%m/%d/%y', 1136 F: '%Y-%m-%d', 1137 T: '%H:%M:%S', 1138 X: '%H:%M:%S', 1139 x: '%m/%d/%y', 1140 R: '%H:%M', 1141 r: '%I:%M:%S %p', 1142 t: '\t', 1143 n: '\n', 1144 '%': '%' 1145 } 1146 }; 1147 // 1148 // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods. 1149 // I use his idea of a set of parsers which can be regular expressions or functions, 1150 // iterating through those, and then seeing if Date.parse() will create a date. 1151 // The parser expressions and functions are a little different and some bugs have been 1152 // worked out. Also, a lot of "pre-parsing" is done to fix implementation 1153 // variations of Date.parse() between browsers. 1154 // 1155 jsDate.createDate = function(date) { 1156 // if passing in multiple arguments, try Date constructor 1157 if (date == null) { 1158 return new Date(); 1159 } 1160 // If the passed value is already a date object, return it 1161 if (date instanceof Date) { 1162 return date; 1163 } 1164 // if (typeof date == 'number') return new Date(date * 1000); 1165 // If the passed value is an integer, interpret it as a javascript timestamp 1166 if (typeof date == 'number') { 1167 return new Date(date); 1168 } 1169 1170 // Before passing strings into Date.parse(), have to normalize them for certain conditions. 1171 // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent. 1172 // 1173 // For example: 1174 // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century. 1175 // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse. 1176 // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime. 1177 1178 // remove leading and trailing spaces 1179 var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1'); 1180 1181 // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn] 1182 parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3"); 1183 1184 ///////// 1185 // Need to check for '15-Dec-09' also. 1186 // FF will not parse, but Chrome will. 1187 // Chrome will set date to 2009 as well. 1188 ///////// 1189 1190 // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010' 1191 parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3"); 1192 1193 // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to 1900. 1194 var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i); 1195 if (match && match.length > 3) { 1196 var m3 = parseFloat(match[3]); 1197 var ny = 1900 + m3; 1198 ny = String(ny); 1199 1200 // now replace 2 digit year with 4 digit year 1201 parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny); 1202 1203 } 1204 1205 // Check for '1/19/70 8:14PM' 1206 // where starts with mm/dd/yy or yy/mm/dd and have something after 1207 // Check if 1st postiion is greater than 12, assume it is year. 1208 // Assme all 2 digit years are 1900's. 1209 // Finally, change them into US style mm/dd/yyyy representations. 1210 match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/); 1211 1212 function h1(parsable, match) { 1213 var m1 = parseFloat(match[1]); 1214 var m2 = parseFloat(match[2]); 1215 var m3 = parseFloat(match[3]); 1216 var cent = 1900; 1217 var ny, nd, nm, str; 1218 1219 if (m1 > 31) { // first number is a year 1220 nd = m3; 1221 nm = m2; 1222 ny = cent + m1; 1223 } 1224 1225 else { // last number is the year 1226 nd = m2; 1227 nm = m1; 1228 ny = cent + m3; 1229 } 1230 1231 str = nm+'/'+nd+'/'+ny; 1232 1233 // now replace 2 digit year with 4 digit year 1234 return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str); 1235 1236 } 1237 1238 if (match && match.length > 3) { 1239 parsable = h1(parsable, match); 1240 } 1241 1242 // Now check for '1/19/70' with nothing after and do as above 1243 var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/); 1244 1245 if (match && match.length > 3) { 1246 parsable = h1(parsable, match); 1247 } 1248 1249 1250 var i = 0; 1251 var length = jsDate.matchers.length; 1252 var pattern; 1253 var current = parsable; 1254 while (i < length) { 1255 ms = Date.parse(current); 1256 if (!isNaN(ms)) { 1257 return new Date(ms); 1258 } 1259 pattern = jsDate.matchers[i]; 1260 if (typeof pattern == 'function') { 1261 obj = pattern.call(jsDate, current); 1262 if (obj instanceof Date) { 1263 return obj; 1264 } 1265 } else { 1266 current = parsable.replace(pattern[0], pattern[1]); 1267 } 1268 i++; 1269 } 1270 return NaN; 1271 }; 1272 1273 // sort of shortcut to create a jsDate date. 1274 jsDate.create = function() { 1275 return new jsDate(arguments); 1276 }; 1277 1278 /** 1279 * @static 1280 * Handy static utility function to return the number of days in a given month. 1281 * @param {Integer} year Year 1282 * @param {Integer} month Month (1-12) 1283 * @returns {Integer} Number of days in the month. 1284 */ 1285 // 1286 // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods. 1287 // 1288 jsDate.daysInMonth = function(year, month) { 1289 if (month == 2) { 1290 return new Date(year, 1, 29).getDate() == 29 ? 29 : 28; 1291 } 1292 return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month]; 1293 }; 1294 1295 1296 // 1297 // An Array of regular expressions or functions that will attempt to match the date string. 1298 // Functions are called with scope of a jsDate instance. 1299 // 1300 jsDate.matchers = [ 1301 // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date). 1302 [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], 1303 // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date). 1304 [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], 1305 // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part. 1306 function(str) { 1307 var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i); 1308 // opt. date hour opt. minute opt. second opt. msec opt. am or pm 1309 if (match) { 1310 if (match[1]) { 1311 var d = this.createDate(match[1]); 1312 if (isNaN(d)) { 1313 return; 1314 } 1315 } else { 1316 var d = new Date(); 1317 d.setMilliseconds(0); 1318 } 1319 var hour = parseFloat(match[2]); 1320 if (match[6]) { 1321 hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12); 1322 } 1323 d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000); 1324 return d; 1325 } 1326 else { 1327 return str; 1328 } 1329 }, 1330 // Handle ISO timestamp with time zone. 1331 function(str) { 1332 var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i); 1333 if (match) { 1334 if (match[1]) { 1335 var d = this.createDate(match[1]); 1336 if (isNaN(d)) { 1337 return; 1338 } 1339 } else { 1340 var d = new Date(); 1341 d.setMilliseconds(0); 1342 } 1343 var hour = parseFloat(match[2]); 1344 d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000); 1345 return d; 1346 } 1347 else { 1348 return str; 1349 } 1350 }, 1351 // Try to match ambiguous strings like 12/8/22. 1352 // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's). 1353 // This may be redundant with pre processing of date already performed. 1354 function(str) { 1355 var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/); 1356 if (match) { 1357 var d = new Date(); 1358 var cent = 1900; 1359 var m1 = parseFloat(match[1]); 1360 var m3 = parseFloat(match[3]); 1361 var ny, nd, nm; 1362 if (m1 > 31) { // first number is a year 1363 nd = m3; 1364 ny = cent + m1; 1365 } 1366 1367 else { // last number is the year 1368 nd = m1; 1369 ny = cent + m3; 1370 } 1371 1372 var nm = inArray(match[2], jsDate.regional[this.locale]["monthNamesShort"]); 1373 1374 if (nm == -1) { 1375 nm = inArray(match[2], jsDate.regional[this.locale]["monthNames"]); 1376 } 1377 1378 d.setFullYear(ny, nm, nd); 1379 d.setHours(0,0,0,0); 1380 return d; 1381 } 1382 1383 else { 1384 return str; 1385 } 1386 } 1387 ]; 1388 1389 // 1390 // I think John Reisig published this method on his blog, ejohn. 1391 // 1392 function inArray( elem, array ) { 1393 if ( array.indexOf ) { 1394 return array.indexOf( elem ); 1395 } 1396 1397 for ( var i = 0, length = array.length; i < length; i++ ) { 1398 if ( array[ i ] === elem ) { 1399 return i; 1400 } 1401 } 1402 1403 return -1; 1404 } 1405 1406 // 1407 // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method. 1408 // 1409 function get_type(thing){ 1410 if(thing===null)return "[object Null]"; // special case 1411 return Object.prototype.toString.call(thing); 1412 } 1413 1414 1415 // Make sure window is defined for headless operations like 1416 // jsdoc-toolkit and some unit testing frameworks. 1417 window = window || {}; 1418 1419 if (window.jQuery !== undefined) { 1420 window.jQuery.jsDate = jsDate; 1421 } 1422 1423 else { 1424 window.jsDate = jsDate; 1425 } 1426 1427 })();