PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/files/phpjs/0.1/datetime/strptime.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 355 lines | 248 code | 19 blank | 88 comment | 23 complexity | b9d8b1ff314b3e9345316abe6135bd11 MD5 | raw file
  1. function strptime (dateStr, format) {
  2. // http://kevin.vanzonneveld.net
  3. // + original by: Brett Zamir (http://brett-zamir.me)
  4. // + based on: strftime
  5. // - depends on: setlocale
  6. // - depends on: array_map
  7. // * example 1: strptime('20091112222135', '%Y%m%d%H%M%S'); // Return value will depend on date and locale
  8. // * returns 1: {tm_sec: 35, tm_min: 21, tm_hour: 22, tm_mday: 12, tm_mon: 10, tm_year: 109, tm_wday: 4, tm_yday: 315, unparsed: ''}
  9. // * example 1: strptime('2009extra', '%Y');
  10. // * returns 1: {tm_sec:0, tm_min:0, tm_hour:0, tm_mday:0, tm_mon:0, tm_year:109, tm_wday:3, tm_yday: -1, unparsed: 'extra'}
  11. // tm_isdst is in other docs; why not PHP?
  12. // Needs more thorough testing and examples
  13. var retObj = {
  14. tm_sec: 0,
  15. tm_min: 0,
  16. tm_hour: 0,
  17. tm_mday: 0,
  18. tm_mon: 0,
  19. tm_year: 0,
  20. tm_wday: 0,
  21. tm_yday: 0,
  22. unparsed: ''
  23. },
  24. that = this,
  25. amPmOffset = 0,
  26. prevHour = false,
  27. _date = function () {
  28. var o = retObj;
  29. // We set date to at least 1 to ensure year or month doesn't go backwards
  30. return _reset(new Date(Date.UTC(o.tm_year + 1900, o.tm_mon, o.tm_mday || 1, o.tm_hour, o.tm_min, o.tm_sec)), o.tm_mday);
  31. },
  32. _reset = function (dateObj, realMday) {
  33. // realMday is to allow for a value of 0 in return results (but without
  34. // messing up the Date() object)
  35. var o = retObj;
  36. var d = dateObj;
  37. o.tm_sec = d.getUTCSeconds();
  38. o.tm_min = d.getUTCMinutes();
  39. o.tm_hour = d.getUTCHours();
  40. o.tm_mday = realMday === 0 ? realMday : d.getUTCDate();
  41. o.tm_mon = d.getUTCMonth();
  42. o.tm_year = d.getUTCFullYear() - 1900;
  43. o.tm_wday = realMday === 0 ? (d.getUTCDay() > 0 ? d.getUTCDay() - 1 : 6) : d.getUTCDay();
  44. var jan1 = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
  45. o.tm_yday = Math.ceil((d - jan1) / (1000 * 60 * 60 * 24));
  46. };
  47. // BEGIN STATIC
  48. var _NWS = /\S/,
  49. _WS = /\s/;
  50. var _aggregates = {
  51. c: 'locale',
  52. D: '%m/%d/%y',
  53. F: '%y-%m-%d',
  54. r: 'locale',
  55. R: '%H:%M',
  56. T: '%H:%M:%S',
  57. x: 'locale',
  58. X: 'locale'
  59. };
  60. /* Fix: Locale alternatives are supported though not documented in PHP; see http://linux.die.net/man/3/strptime
  61. Ec
  62. EC
  63. Ex
  64. EX
  65. Ey
  66. EY
  67. Od or Oe
  68. OH
  69. OI
  70. Om
  71. OM
  72. OS
  73. OU
  74. Ow
  75. OW
  76. Oy
  77. */
  78. var _preg_quote = function (str) {
  79. return (str + '').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!<>\|\:])/g, '\\$1');
  80. };
  81. // END STATIC
  82. // BEGIN REDUNDANT
  83. this.php_js = this.php_js || {};
  84. this.setlocale('LC_ALL', 0); // ensure setup of localization variables takes place
  85. // END REDUNDANT
  86. var phpjs = this.php_js;
  87. var locale = phpjs.localeCategories.LC_TIME;
  88. var locales = phpjs.locales;
  89. var lc_time = locales[locale].LC_TIME;
  90. // First replace aggregates (run in a loop because an agg may be made up of other aggs)
  91. while (format.match(/%[cDFhnrRtTxX]/)) {
  92. format = format.replace(/%([cDFhnrRtTxX])/g, function (m0, m1) {
  93. var f = _aggregates[m1];
  94. return (f === 'locale' ? lc_time[m1] : f);
  95. });
  96. }
  97. var _addNext = function (j, regex, cb) {
  98. if (typeof regex === 'string') {
  99. regex = new RegExp('^' + regex, 'i');
  100. }
  101. var check = dateStr.slice(j);
  102. var match = regex.exec(check);
  103. // Even if the callback returns null after assigning to the return object, the object won't be saved anyways
  104. var testNull = match ? cb.apply(null, match) : null;
  105. if (testNull === null) {
  106. throw 'No match in string';
  107. }
  108. return j + match[0].length;
  109. };
  110. var _addLocalized = function (j, formatChar, category) {
  111. return _addNext(j, that.array_map(
  112. _preg_quote, lc_time[formatChar]).join('|'), // Could make each parenthesized instead and pass index to callback
  113. function (m) {
  114. var match = lc_time[formatChar].search(new RegExp('^' + _preg_quote(m) + '$', 'i'));
  115. if (match) {
  116. retObj[category] = match[0];
  117. }
  118. });
  119. };
  120. // BEGIN PROCESSING CHARACTERS
  121. for (var i = 0, j = 0; i < format.length; i++) {
  122. if (format.charAt(i) === '%') {
  123. var literalPos = ['%', 'n', 't'].indexOf(format.charAt(i + 1));
  124. if (literalPos !== -1) {
  125. if (['%', '\n', '\t'].indexOf(dateStr.charAt(j)) === literalPos) { // a matched literal
  126. ++i, ++j; // skip beyond
  127. continue;
  128. }
  129. // Format indicated a percent literal, but not actually present
  130. return false;
  131. }
  132. var formatChar = format.charAt(i + 1);
  133. try {
  134. switch (formatChar) {
  135. case 'a':
  136. // Fall-through // Sun-Sat
  137. case 'A':
  138. // Sunday-Saturday
  139. j = _addLocalized(j, formatChar, 'tm_wday'); // Changes nothing else
  140. break;
  141. case 'h':
  142. // Fall-through (alias of 'b');
  143. case 'b':
  144. // Jan-Dec
  145. j = _addLocalized(j, 'b', 'tm_mon');
  146. _date(); // Also changes wday, yday
  147. break;
  148. case 'B':
  149. // January-December
  150. j = _addLocalized(j, formatChar, 'tm_mon');
  151. _date(); // Also changes wday, yday
  152. break;
  153. case 'C':
  154. // 0+; century (19 for 20th)
  155. j = _addNext(j, /^\d?\d/, // PHP docs say two-digit, but accepts one-digit (two-digit max)
  156. function (d) {
  157. var year = (parseInt(d, 10) - 19) * 100;
  158. retObj.tm_year = year;
  159. _date();
  160. if (!retObj.tm_yday) {
  161. retObj.tm_yday = -1;
  162. }
  163. // Also changes wday; and sets yday to -1 (always?)
  164. });
  165. break;
  166. case 'd':
  167. // Fall-through 01-31 day
  168. case 'e':
  169. // 1-31 day
  170. j = _addNext(j, formatChar === 'd' ? /^(0[1-9]|[1-2]\d|3[0-1])/ : /^([1-2]\d|3[0-1]|[1-9])/, function (d) {
  171. var dayMonth = parseInt(d, 10);
  172. retObj.tm_mday = dayMonth;
  173. _date(); // Also changes w_day, y_day
  174. });
  175. break;
  176. case 'g':
  177. // No apparent effect; 2-digit year (see 'V')
  178. break;
  179. case 'G':
  180. // No apparent effect; 4-digit year (see 'V')'
  181. break;
  182. case 'H':
  183. // 00-23 hours
  184. j = _addNext(j, /^([0-1]\d|2[0-3])/, function (d) {
  185. var hour = parseInt(d, 10);
  186. retObj.tm_hour = hour;
  187. // Changes nothing else
  188. });
  189. break;
  190. case 'l':
  191. // Fall-through of lower-case 'L'; 1-12 hours
  192. case 'I':
  193. // 01-12 hours
  194. j = _addNext(j, formatChar === 'l' ? /^([1-9]|1[0-2])/ : /^(0[1-9]|1[0-2])/, function (d) {
  195. var hour = parseInt(d, 10) - 1 + amPmOffset;
  196. retObj.tm_hour = hour;
  197. prevHour = true; // Used for coordinating with am-pm
  198. // Changes nothing else, but affected by prior 'p/P'
  199. });
  200. break;
  201. case 'j':
  202. // 001-366 day of year
  203. j = _addNext(j, /^(00[1-9]|0[1-9]\d|[1-2]\d\d|3[0-6][0-6])/, function (d) {
  204. var dayYear = parseInt(d, 10) - 1;
  205. retObj.tm_yday = dayYear;
  206. // Changes nothing else (oddly, since if based on a given year, could calculate other fields)
  207. });
  208. break;
  209. case 'm':
  210. // 01-12 month
  211. j = _addNext(j, /^(0[1-9]|1[0-2])/, function (d) {
  212. var month = parseInt(d, 10) - 1;
  213. retObj.tm_mon = month;
  214. _date(); // Also sets wday and yday
  215. });
  216. break;
  217. case 'M':
  218. // 00-59 minutes
  219. j = _addNext(j, /^[0-5]\d/, function (d) {
  220. var minute = parseInt(d, 10);
  221. retObj.tm_min = minute;
  222. // Changes nothing else
  223. });
  224. break;
  225. case 'P':
  226. // Seems not to work; AM-PM
  227. return false; // Could make fall-through instead since supposed to be a synonym despite PHP docs
  228. case 'p':
  229. // am-pm
  230. j = _addNext(j, /^(am|pm)/i, function (d) {
  231. // No effect on 'H' since already 24 hours but
  232. // works before or after setting of l/I hour
  233. amPmOffset = (/a/).test(d) ? 0 : 12;
  234. if (prevHour) {
  235. retObj.tm_hour += amPmOffset;
  236. }
  237. });
  238. break;
  239. case 's':
  240. // Unix timestamp (in seconds)
  241. j = _addNext(j, /^\d+/, function (d) {
  242. var timestamp = parseInt(d, 10);
  243. var date = new Date(Date.UTC(timestamp * 1000));
  244. _reset(date);
  245. // Affects all fields, but can't be negative (and initial + not allowed)
  246. });
  247. break;
  248. case 'S':
  249. // 00-59 seconds
  250. j = _addNext(j, /^[0-5]\d/, // strptime also accepts 60-61 for some reason
  251. function (d) {
  252. var second = parseInt(d, 10);
  253. retObj.tm_sec = second;
  254. // Changes nothing else
  255. });
  256. break;
  257. case 'u':
  258. // Fall-through; 1 (Monday)-7(Sunday)
  259. case 'w':
  260. // 0 (Sunday)-6(Saturday)
  261. j = _addNext(j, /^\d/, function (d) {
  262. retObj.tm_wday = d - (formatChar === 'u');
  263. // Changes nothing else apparently
  264. });
  265. break;
  266. case 'U':
  267. // Fall-through (week of year, from 1st Sunday)
  268. case 'V':
  269. // Fall-through (ISO-8601:1988 week number; from first 4-weekday week, starting with Monday)
  270. case 'W':
  271. // Apparently ignored (week of year, from 1st Monday)
  272. break;
  273. case 'y':
  274. // 69 (or higher) for 1969+, 68 (or lower) for 2068-
  275. j = _addNext(j, /^\d?\d/, // PHP docs say two-digit, but accepts one-digit (two-digit max)
  276. function (d) {
  277. d = parseInt(d, 10);
  278. var year = d >= 69 ? d : d + 100;
  279. retObj.tm_year = year;
  280. _date();
  281. if (!retObj.tm_yday) {
  282. retObj.tm_yday = -1;
  283. }
  284. // Also changes wday; and sets yday to -1 (always?)
  285. });
  286. break;
  287. case 'Y':
  288. // 2010 (4-digit year)
  289. j = _addNext(j, /^\d{1,4}/, // PHP docs say four-digit, but accepts one-digit (four-digit max)
  290. function (d) {
  291. var year = (parseInt(d, 10)) - 1900;
  292. retObj.tm_year = year;
  293. _date();
  294. if (!retObj.tm_yday) {
  295. retObj.tm_yday = -1;
  296. }
  297. // Also changes wday; and sets yday to -1 (always?)
  298. });
  299. break;
  300. case 'z':
  301. // Timezone; on my system, strftime gives -0800, but strptime seems not to alter hour setting
  302. break;
  303. case 'Z':
  304. // Timezone; on my system, strftime gives PST, but strptime treats text as unparsed
  305. break;
  306. default:
  307. throw 'Unrecognized formatting character in strptime()';
  308. break;
  309. }
  310. } catch (e) {
  311. if (e === 'No match in string') { // Allow us to exit
  312. return false; // There was supposed to be a matching format but there wasn't
  313. }
  314. }++i; // Calculate skipping beyond initial percent too
  315. } else if (format.charAt(i) !== dateStr.charAt(j)) {
  316. // If extra whitespace at beginning or end of either, or between formats, no problem
  317. // (just a problem when between % and format specifier)
  318. // If the string has white-space, it is ok to ignore
  319. if (dateStr.charAt(j).search(_WS) !== -1) {
  320. j++;
  321. i--; // Let the next iteration try again with the same format character
  322. } else if (format.charAt(i).search(_NWS) !== -1) { // Any extra formatting characters besides white-space causes
  323. // problems (do check after WS though, as may just be WS in string before next character)
  324. return false;
  325. } else { // Extra WS in format
  326. // Adjust strings when encounter non-matching whitespace, so they align in future checks above
  327. // Will check on next iteration (against same (non-WS) string character)
  328. }
  329. } else {
  330. j++;
  331. }
  332. }
  333. // POST-PROCESSING
  334. retObj.unparsed = dateStr.slice(j); // Will also get extra whitespace; empty string if none
  335. return retObj;
  336. }