jsdate.js 56 KB

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