|
| 1 | +function strtotime (text, now) { |
| 2 | + // Convert string representation of date and time to a timestamp |
| 3 | + // |
| 4 | + // version: 1109.2016 |
| 5 | + // discuss at: http://phpjs.org/functions/strtotime |
| 6 | + // + original by: Caio Ariede (http://caioariede.com) |
| 7 | + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
| 8 | + // + input by: David |
| 9 | + // + improved by: Caio Ariede (http://caioariede.com) |
| 10 | + // + bugfixed by: Wagner B. Soares |
| 11 | + // + bugfixed by: Artur Tchernychev |
| 12 | + // + improved by: A. Matías Quezada (http://amatiasq.com) |
| 13 | + // + improved by: preuter |
| 14 | + // + improved by: Brett Zamir (http://brett-zamir.me) |
| 15 | + // + improved by: Mirko Faber |
| 16 | + // % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones) |
| 17 | + // * example 1: strtotime('+1 day', 1129633200); |
| 18 | + // * returns 1: 1129719600 |
| 19 | + // * example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200); |
| 20 | + // * returns 2: 1130425202 |
| 21 | + // * example 3: strtotime('last month', 1129633200); |
| 22 | + // * returns 3: 1127041200 |
| 23 | + // * example 4: strtotime('2009-05-04 08:30:00 GMT'); |
| 24 | + // * returns 4: 1241425800 |
| 25 | + var parsed, match, year, date, days, ranges, len, times, regex, i; |
| 26 | + |
| 27 | + if (!text) { |
| 28 | + return null; |
| 29 | + } |
1 | 30 |
|
2 |
| -function strtotime(text, now) { |
3 |
| - // Convert string representation of date and time to a timestamp |
4 |
| - // |
5 |
| - // version: 1109.2015 |
6 |
| - // discuss at: http://phpjs.org/functions/strtotime |
7 |
| - // + original by: Caio Ariede (http://caioariede.com) |
8 |
| - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) |
9 |
| - // + input by: David |
10 |
| - // + improved by: Caio Ariede (http://caioariede.com) |
11 |
| - // + bugfixed by: Wagner B. Soares |
12 |
| - // + bugfixed by: Artur Tchernychev |
13 |
| - // + improved by: A. Matías Quezada (http://amatiasq.com) |
14 |
| - // + improved by: preuter |
15 |
| - // + improved by: Brett Zamir (http://brett-zamir.me) |
16 |
| - // % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones) |
17 |
| - // * example 1: strtotime('+1 day', 1129633200); |
18 |
| - // * returns 1: 1129719600 |
19 |
| - // * example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200); |
20 |
| - // * returns 2: 1130425202 |
21 |
| - // * example 3: strtotime('last month', 1129633200); |
22 |
| - // * returns 3: 1127041200 |
23 |
| - // * example 4: strtotime('2009-05-04 08:30:00'); |
24 |
| - // * returns 4: 1241418600 |
25 |
| - var parsed, match, year, date, days, ranges, len, times, regex, i; |
26 |
| - |
27 |
| - if (!text) { |
28 |
| - return null; |
29 |
| - } |
30 |
| - |
31 |
| - // Unecessary spaces |
32 |
| - text = text.replace(/^\s+|\s+$/g, '') |
| 31 | + // Unecessary spaces |
| 32 | + text = text.replace(/^\s+|\s+$/g, '') |
33 | 33 | .replace(/\s{2,}/g, ' ')
|
34 | 34 | .replace(/[\t\r\n]/g, '')
|
35 | 35 | .toLowerCase();
|
36 | 36 |
|
37 |
| - if (text === 'now') { |
38 |
| - return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0; |
39 |
| - } |
40 |
| - |
41 |
| - match = text.match(/^(\d{2,4})-(\d{2})-(\d{2})(?:\s(\d{1,2}):(\d{2})(?::\d{2})?)?(?:\.(\d+)?)?$/); |
42 |
| - if (match) { |
43 |
| - year = match[1] >= 0 && match[1] <= 69 ? + match[1] + 2000 : match[1]; |
44 |
| - return new Date(year, parseInt(match[2], 10) - 1, match[3], |
45 |
| - match[4] || 0, match[5] || 0, match[6] || 0, match[7] || 0) / 1000 | 0; |
46 |
| - } |
47 |
| - |
48 |
| - date = now ? new Date(now * 1000) : new Date(); |
49 |
| - days = { |
50 |
| - 'sun': 0, |
51 |
| - 'mon': 1, |
52 |
| - 'tue': 2, |
53 |
| - 'wed': 3, |
54 |
| - 'thu': 4, |
55 |
| - 'fri': 5, |
56 |
| - 'sat': 6 |
57 |
| - }; |
58 |
| - ranges = { |
59 |
| - 'yea': 'FullYear', |
60 |
| - 'mon': 'Month', |
61 |
| - 'day': 'Date', |
62 |
| - 'hou': 'Hours', |
63 |
| - 'min': 'Minutes', |
64 |
| - 'sec': 'Seconds' |
65 |
| - }; |
66 |
| - |
67 |
| - function lastNext(type, range, modifier) { |
68 |
| - var diff, day = days[range]; |
69 |
| - |
70 |
| - if (typeof day !== 'undefined') { |
71 |
| - diff = day - date.getDay(); |
72 |
| - |
73 |
| - if (diff === 0) { |
74 |
| - diff = 7 * modifier; |
75 |
| - } |
76 |
| - else if (diff > 0 && type === 'last') { |
77 |
| - diff -= 7; |
78 |
| - } |
79 |
| - else if (diff < 0 && type === 'next') { |
80 |
| - diff += 7; |
81 |
| - } |
82 |
| - |
83 |
| - date.setDate(date.getDate() + diff); |
| 37 | + // in contrast to php, js Date.parse function interprets: |
| 38 | + // dates given as yyyy-mm-dd as in timezone: UTC, |
| 39 | + // dates with "." or "-" as MDY instead of DMY |
| 40 | + // dates with two-digit years differently |
| 41 | + // etc...etc... |
| 42 | + // ...therefore we manually parse lots of common date formats |
| 43 | + var match = text.match(/^(\d{1,4})([\-\.\/\:])(\d{1,2})([\-\.\/\:])(\d{1,4})(?:\s(\d{1,2}):(\d{2})?:?(\d{2})?)?(?:\s([A-Z]+)?)?$/); |
| 44 | + if (match && match[2]==match[4]) { |
| 45 | + if (match[1]>1901) { |
| 46 | + switch (match[2]) { |
| 47 | + case '-': { // YYYY-M-D |
| 48 | + if (match[3]>12 | match[5]>31) return(0); |
| 49 | + return new Date(match[1], parseInt(match[3], 10) - 1, match[5], |
| 50 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 51 | + break; |
| 52 | + } |
| 53 | + case '.': { // YYYY.M.D is not parsed by strtotime() |
| 54 | + return(0); |
| 55 | + break; |
| 56 | + } |
| 57 | + case '/': { // YYYY/M/D |
| 58 | + if (match[3]>12 | match[5]>31) return(0); |
| 59 | + return new Date(match[1], parseInt(match[3], 10) - 1, match[5], |
| 60 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 61 | + break; |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + else if (match[5]>1901) { |
| 66 | + switch (match[2]) { |
| 67 | + case '-': { // D-M-YYYY |
| 68 | + if (match[3]>12 | match[1]>31) return(0); |
| 69 | + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], |
| 70 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 71 | + break; |
| 72 | + } |
| 73 | + case '.': { // D.M.YYYY |
| 74 | + if (match[3]>12 | match[1]>31) return(0); |
| 75 | + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], |
| 76 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 77 | + break; |
| 78 | + } |
| 79 | + case '/': { // M/D/YYYY |
| 80 | + if (match[1]>12 | match[3]>31) return(0); |
| 81 | + return new Date(match[5], parseInt(match[1], 10) - 1, match[3], |
| 82 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 83 | + break; |
| 84 | + } |
| 85 | + } |
| 86 | + } |
| 87 | + else { |
| 88 | + switch (match[2]) { |
| 89 | + case '-': { // YY-M-D |
| 90 | + if (match[3]>12 | match[5]>31 | (match[1] < 70 & match[1]>38)) return(0); |
| 91 | + var year = match[1] >= 0 && match[1] <= 38 ? +match[1] + 2000 : match[1]; |
| 92 | + return new Date(year, parseInt(match[3], 10) - 1, match[5], |
| 93 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 94 | + break; |
| 95 | + } |
| 96 | + case '.': { // D.M.YY or H.MM.SS |
| 97 | + if (match[5]>=70) { // D.M.YY |
| 98 | + if (match[3]>12 | match[1]>31) return(0); |
| 99 | + return new Date(match[5], parseInt(match[3], 10) - 1, match[1], |
| 100 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 101 | + } |
| 102 | + else if (match[5]<60 & !(match[6])) { // H.MM.SS |
| 103 | + if (match[1]>23 | match[3]>59) return(0); |
| 104 | + var today = new Date(); |
| 105 | + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), |
| 106 | + match[1] || 0, match[3] || 0, match[5] || 0, match[9] || 0) / 1000; |
| 107 | + } |
| 108 | + else return(0); // invalid format, cannot be parsed |
| 109 | + break; |
| 110 | + } |
| 111 | + case '/': { // M/D/YY |
| 112 | + if (match[1]>12 | match[3]>31 | (match[5] < 70 & match[5]>38)) return(0); |
| 113 | + var year = match[5] >= 0 && match[5] <= 38 ? +match[5] + 2000 : match[5]; |
| 114 | + return new Date(year, parseInt(match[1], 10) - 1, match[3], |
| 115 | + match[6] || 0, match[7] || 0, match[8] || 0, match[9] || 0) / 1000; |
| 116 | + break; |
| 117 | + } |
| 118 | + case ':': { // HH:MM:SS |
| 119 | + if (match[1]>23 | match[3]>59 | match[5]>59) return(0); |
| 120 | + var today = new Date(); |
| 121 | + return new Date(today.getFullYear(), today.getMonth(), today.getDate(), |
| 122 | + match[1] || 0, match[3] || 0, match[5] || 0) / 1000; |
| 123 | + break; |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + |
| 130 | + // other formats and "now" should be parsed by Date.parse() |
| 131 | + if (text === 'now') { |
| 132 | + return now === null || isNaN(now) ? new Date().getTime() / 1000 | 0 : now | 0; |
| 133 | + } |
| 134 | + if (!isNaN(parsed = Date.parse(text))) { |
| 135 | + return parsed / 1000 | 0; |
| 136 | + } |
| 137 | + |
| 138 | + date = now ? new Date(now * 1000) : new Date(); |
| 139 | + days = { |
| 140 | + 'sun': 0, |
| 141 | + 'mon': 1, |
| 142 | + 'tue': 2, |
| 143 | + 'wed': 3, |
| 144 | + 'thu': 4, |
| 145 | + 'fri': 5, |
| 146 | + 'sat': 6 |
| 147 | + }; |
| 148 | + ranges = { |
| 149 | + 'yea': 'FullYear', |
| 150 | + 'mon': 'Month', |
| 151 | + 'day': 'Date', |
| 152 | + 'hou': 'Hours', |
| 153 | + 'min': 'Minutes', |
| 154 | + 'sec': 'Seconds' |
| 155 | + }; |
| 156 | + |
| 157 | + function lastNext(type, range, modifier) { |
| 158 | + var diff, day = days[range]; |
| 159 | + |
| 160 | + if (typeof day !== 'undefined') { |
| 161 | + diff = day - date.getDay(); |
| 162 | + |
| 163 | + if (diff === 0) { |
| 164 | + diff = 7 * modifier; |
| 165 | + } |
| 166 | + else if (diff > 0 && type === 'last') { |
| 167 | + diff -= 7; |
| 168 | + } |
| 169 | + else if (diff < 0 && type === 'next') { |
| 170 | + diff += 7; |
| 171 | + } |
| 172 | + |
| 173 | + date.setDate(date.getDate() + diff); |
| 174 | + } |
84 | 175 | }
|
85 |
| - } |
86 |
| - function process(val) { |
87 |
| - var splt = val.split(' '), // Todo: Reconcile this with regex using \s, taking into account browser issues with split and regexes |
| 176 | + function process(val) { |
| 177 | + var splt = val.split(' '), // Todo: Reconcile this with regex using \s, taking into account browser issues with split and regexes |
88 | 178 | type = splt[0],
|
89 | 179 | range = splt[1].substring(0, 3),
|
90 | 180 | typeIsNumber = /\d+/.test(type),
|
91 | 181 | ago = splt[2] === 'ago',
|
92 | 182 | num = (type === 'last' ? -1 : 1) * (ago ? -1 : 1);
|
93 | 183 |
|
94 |
| - if (typeIsNumber) { |
95 |
| - num *= parseInt(type, 10); |
| 184 | + if (typeIsNumber) { |
| 185 | + num *= parseInt(type, 10); |
| 186 | + } |
| 187 | + |
| 188 | + if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { |
| 189 | + return date['set' + ranges[range]](date['get' + ranges[range]]() + num); |
| 190 | + } |
| 191 | + if (range === 'wee') { |
| 192 | + return date.setDate(date.getDate() + (num * 7)); |
| 193 | + } |
| 194 | + |
| 195 | + if (type === 'next' || type === 'last') { |
| 196 | + lastNext(type, range, num); |
| 197 | + } |
| 198 | + else if (!typeIsNumber) { |
| 199 | + return false; |
| 200 | + } |
| 201 | + return true; |
96 | 202 | }
|
97 | 203 |
|
98 |
| - if (ranges.hasOwnProperty(range) && !splt[1].match(/^mon(day|\.)?$/i)) { |
99 |
| - return date['set' + ranges[range]](date['get' + ranges[range]]() + num); |
100 |
| - } |
101 |
| - if (range === 'wee') { |
102 |
| - return date.setDate(date.getDate() + (num * 7)); |
103 |
| - } |
| 204 | + times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + |
| 205 | + '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + |
| 206 | + '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'; |
| 207 | + regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'; |
104 | 208 |
|
105 |
| - if (type === 'next' || type === 'last') { |
106 |
| - lastNext(type, range, num); |
| 209 | + match = text.match(new RegExp(regex, 'gi')); |
| 210 | + if (!match) { |
| 211 | + return false; |
107 | 212 | }
|
108 |
| - else if (!typeIsNumber) { |
109 |
| - return false; |
110 |
| - } |
111 |
| - return true; |
112 |
| - } |
113 |
| - |
114 |
| - times = '(years?|months?|weeks?|days?|hours?|minutes?|min|seconds?|sec' + |
115 |
| - '|sunday|sun\\.?|monday|mon\\.?|tuesday|tue\\.?|wednesday|wed\\.?' + |
116 |
| - '|thursday|thu\\.?|friday|fri\\.?|saturday|sat\\.?)'; |
117 |
| - regex = '([+-]?\\d+\\s' + times + '|' + '(last|next)\\s' + times + ')(\\sago)?'; |
118 |
| - |
119 |
| - match = text.match(new RegExp(regex, 'gi')); |
120 |
| - if (!match) { |
121 |
| - return false; |
122 |
| - } |
123 |
| - |
124 |
| - for (i = 0, len = match.length; i < len; i++) { |
125 |
| - if (!process(match[i])) { |
126 |
| - return false; |
| 213 | + |
| 214 | + for (i = 0, len = match.length; i < len; i++) { |
| 215 | + if (!process(match[i])) { |
| 216 | + return false; |
| 217 | + } |
127 | 218 | }
|
128 |
| - } |
129 | 219 |
|
130 |
| - // ECMAScript 5 only |
131 |
| - //if (!match.every(process)) |
132 |
| - // return false; |
| 220 | + // ECMAScript 5 only |
| 221 | + // if (!match.every(process)) |
| 222 | + // return false; |
133 | 223 |
|
134 |
| - return (date.getTime() / 1000) | 0; |
| 224 | + return (date.getTime() / 1000); |
135 | 225 | }
|
0 commit comments