JavaScript strtotime
Convert string representation of date and time to a timestamp
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110111 112 113 114 115116 117 118 119 120121 122 123 124 125126 127 128 129 130131 132 133 134 135136 137 138 139 140141 142 143 144 145146 147 148 149 150151 152 153 154 155156 157 158 159 160161 162 163 164 165166 167 168 169 170171 172 173 174 175176 177 178 179 180181 182 183 184 185186 187 188 189 190191 192 193 | function strtotime (str, now) { // Convert string representation of date and time to a timestamp // // version: 1008.1718 // discuss at: http://phpjs.org/functions/strtotime // + original by: Caio Ariede (http://caioariede.com) // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + input by: David // + improved by: Caio Ariede (http://caioariede.com) // + improved by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Wagner B. Soares // + bugfixed by: Artur Tchernychev // % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones) // * example 1: strtotime('+1 day', 1129633200); // * returns 1: 1129719600 // * example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200); // * returns 2: 1130425202 // * example 3: strtotime('last month', 1129633200); // * returns 3: 1127041200 // * example 4: strtotime('2009-05-04 08:30:00'); // * returns 4: 1241418600 var i, match, s, strTmp = '', parse = ''; strTmp = str; strTmp = strTmp.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces strTmp = strTmp.replace(/[\t\r\n]/g, ''); // unecessary chars if (strTmp == 'now') { return (new Date()).getTime()/1000; // Return seconds, not milli-seconds } else if (!isNaN(parse = Date.parse(strTmp))) { return (parse/1000); } else if (now) { now = new Date(now*1000); // Accept PHP-style seconds } else { now = new Date(); } strTmp = strTmp.toLowerCase(); var __is = { day: { 'sun': 0, 'mon': 1, 'tue': 2, 'wed': 3, 'thu': 4, 'fri': 5, 'sat': 6 }, mon: { 'jan': 0, 'feb': 1, 'mar': 2, 'apr': 3, 'may': 4, 'jun': 5, 'jul': 6, 'aug': 7, 'sep': 8, 'oct': 9, 'nov': 10, 'dec': 11 } }; var process = function (m) { var ago = (m[2] && m[2] == 'ago'); var num = (num = m[0] == 'last' ? -1 : 1) * (ago ? -1 : 1); switch (m[0]) { case 'last': case 'next': switch (m[1].substring(0, 3)) { case 'yea': now.setFullYear(now.getFullYear() + num); break; case 'mon': now.setMonth(now.getMonth() + num); break; case 'wee': now.setDate(now.getDate() + (num * 7)); break; case 'day': now.setDate(now.getDate() + num); break; case 'hou': now.setHours(now.getHours() + num); break; case 'min': now.setMinutes(now.getMinutes() + num); break; case 'sec': now.setSeconds(now.getSeconds() + num); break; default: var day; if (typeof (day = __is.day[m[1].substring(0, 3)]) != 'undefined') { var diff = day - now.getDay(); if (diff == 0) { diff = 7 * num; } else if (diff > 0) { if (m[0] == 'last') {diff -= 7;} } else { if (m[0] == 'next') {diff += 7;} } now.setDate(now.getDate() + diff); } } break; default: if (/\d+/.test(m[0])) { num *= parseInt(m[0], 10); switch (m[1].substring(0, 3)) { case 'yea': now.setFullYear(now.getFullYear() + num); break; case 'mon': now.setMonth(now.getMonth() + num); break; case 'wee': now.setDate(now.getDate() + (num * 7)); break; case 'day': now.setDate(now.getDate() + num); break; case 'hou': now.setHours(now.getHours() + num); break; case 'min': now.setMinutes(now.getMinutes() + num); break; case 'sec': now.setSeconds(now.getSeconds() + num); break; } } else { return false; } break; } return true; }; match = strTmp.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/); if (match != null) { if (!match[2]) { match[2] = '00:00:00'; } else if (!match[3]) { match[2] += ':00'; } s = match[1].split(/-/g); for (i in __is.mon) { if (__is.mon[i] == s[1] - 1) { s[1] = i; } } s[0] = parseInt(s[0], 10); s[0] = (s[0] >= 0 && s[0] <= 69) ? '20'+(s[0] < 10 ? '0'+s[0] : s[0]+'') : (s[0] >= 70 && s[0] <= 99) ? '19'+s[0] : s[0]+''; return parseInt(this.strtotime(s[2] + ' ' + s[1] + ' ' + s[0] + ' ' + match[2])+(match[4] ? match[4]/1000 : ''), 10); } var regex = '([+-]?\\d+\\s'+ '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?'+ '|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday'+ '|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)'+ '|(last|next)\\s'+ '(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?'+ '|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday'+ '|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))'+ '(\\sago)?'; match = strTmp.match(new RegExp(regex, 'gi')); // Brett: seems should be case insensitive per docs, so added 'i' if (match == null) { return false; } for (i = 0; i < match.length; i++) { if (!process(match[i].split(' '))) { return false; } } return (now.getTime()/1000); } |
Examples
» Example 1
Running
1 | strtotime('+1 day', 1129633200); |
Should return
1 | 1129719600 |
» Example 2
Running
1 | strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200); |
Should return
1 | 1130425202 |
Dependencies
No dependencies, you can use this function standalone.
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have strtotime goodness in JavaScript.
@Jack: The syntax highlighting code here is buggy, so when you copy-paste it directly, some lines will be commented out. Go to "raw js source" to make sure you have a good copy...It is working with the right code...
hmm - couple of bugs in strtotime...
1) strtotime('last Monday',now) returns last month not last monday - first 3 letters of month is the same as monday!
work around :
strtotime('last Tuesday, -1 day',now); // = last monday
2) strtotime('d-m-Y',now) isn't handled correctly (only 'Y-m-d' is) (as mentioned in other comments)
3) in 2 above, the d and m have to be zero padded to work I.E. strtotime('2010-3-1') has to be replaced with ('2010-03-01')
work around :
//examples from variable passed into function perhaps
day = 2;
month = 5;
year = 2010;
if (day < 10) dayzeropad = '0'; else dayzeropad = '';
if (month < 10) monthzeropad = '0'; else monthzeropad = '';
var tstring = year + '-' + monthzeropad + month + '-' + dayzeropad + day;
unixtime = strtotime(tstring);
bugs work-aroundable... cool stuff - thanks.
Is it possible to add support for german-like Dates ?
in PHP (5.3+), strototime('22.01.2010'); works as expected, but not with this js strtotime-method.
@ rav3n: You might be better off parsing your string first before feeding it to strtotime cause I believe PHP wouldn't support your format either.
@ iwosz: Sorry no IE7 on my machine. So unless someone else can get to the bottom of this I may have to see if I can find some WinXP virtual image with IE7 installed or sth like that
this function is great. however i need to format the string not as %Y-%m-%d but as %d-%m-%Y.
i understand that it's around line 150-170
@ Daniel Janesch & Theriault: Thanks for pointing out these problems. I've contacted the original author by mail.
First of all: Great work :-)
I have figured out that the following is not working correctly:
$d = new Date(2012, 11, 1, 7, 0, 0);
$r = strtotime('3 Wednesday', $d.getTime()/1000); // 2012-11-01 07:00:00
The correct result with php is 2012-11-21 00:00:00 but with phpjs I get 1351753200 wich is 2012-11-01 07:00:00.
Any clues?
PHP seems to apply the string as a whole and not from left-to-right as in this function. In the following PHP example, both $a and $b will be the same date. If PHP applied them from left-to-right, $b would be 2012-03-01, not 2012-02-29.
$d = strtotime('2011-02-22');
$a = strtotime('+1 year, +7 days', $d); // 2012-02-29
$b= strtotime('+7 days, +1 year', $d); // 2012-02-29
This function applies the string from left-to-right, which seems more logical, but is incorrect.
I noticed that none of the following work with this function:
today (same as 'now' but resets to 12:00:00AM), tomorrow (same as '+1 day' but also resets to 12), yesterday (same as '-1 day' but also resets to 12).
next week, last week (Currently returns date plus/minus seven days, should return next monday if I remember correctly on how it works in PHP).
last sunday of next month (returns next month, but not last sun/sat/mon/day... of that month)
YYYY-Wnn-d (Basically, sets year to YYYY, week number to nn, and then day of that week to d (0-6). Day of week may be omitted and also overflow, such as 7 for next week.)
00:00:00 (Changes time to 12:00:00 AM or anything else entered here.)
Does anyone else see anything missing from this function? I may recode it. I would like to see it complete -- it is a great function.
@majak: Yeah, thanks, it seemed that way from the code snippet (figured out what was the problem by finding that code by Google). Be careful using Prototype or other libraries that overload the prototype objects--it causes problems like these...
@Brett Zamir: Thank you for your help. Your fix works.
Just for reference, I'm using Prototype framework.
@majak, it appears you are using a library which overloads the Object prototype with its own functions which this PHP.JS function then iterates... You can solve this problem by adding right after line 184 (see above) the following line (and then the closing brace after line 187):
if (match.hasOwnProperty(i)) {
If you need to work with older browsers like IE 5, you can add this instead:
if (typeof match[i] !== 'function') {
See http://yuiblog.com/blog/2006/09/26/for-in-intrigue/ for a fuller explanation of why.
All of our for...in statements should really use such a test, though since it is cumbersome, and libraries should, imo, really not be altering the prototype in the first place, I think it is a toss-up as to whether we should add these checks ourselves. What do you think, Kevin? Perhaps we should in order to be most compatible...
I'm using the latest (2.85) minified and namespaced version of this function. This snippet:
$P.strtotime('+1 day');
produces TypeError. I have tested it in both FF and Chrome, here are their error messages:
FF:
TypeError: match[i].split is not a function
Chrome:
TypeError: Object function (inline) {
return (inline !== false ? this : this.toArray())._reverse();
} has no method 'split'
I suppose it happens on every relative date string input, like "-3 months".
The function expects a string--you are mostly passing something to the function which is not a string.
Getting an error --
Error: strTmp.replace is not a function
The debugger is pointing to this line --
strTmp = strTmp.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces


Brett Zamir
Aug 29th
You can see "raw js source", note #2, on the date function for the latest notes about how this could be implemented if you are able to make the changes to our code yourself (sorry, I have no time at the moment). Anyone else feel free to help out if you can.