2 * Copyright 2010-2013 Ben Birch
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this software except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * date-range-parser.js
18 * Contributed to the Apache Software Foundation by:
20 * fork me at https://github.com/mobz/date-range-parser
22 Licensed to the Apache Software Foundation (ASF) under one
23 or more contributor license agreements. See the NOTICE file
24 distributed with this work for additional information
25 regarding copyright ownership. The ASF licenses this file
26 to you under the Apache License, Version 2.0 (the
27 "License"); you may not use this file except in compliance
28 with the License. You may obtain a copy of the License at
30 http://www.apache.org/licenses/LICENSE-2.0
32 Unless required by applicable law or agreed to in writing,
33 software distributed under the License is distributed on an
34 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
35 KIND, either express or implied. See the License for the
36 specific language governing permissions and limitations
43 var drp = window.dateRangeParser = {};
45 drp.defaultRange = 1000 * 60 * 60 * 24;
47 drp.now = null; // set a different value for now than the time at function invocation
49 drp.parse = function(v) {
51 var r = drp._parse(v);
52 r.end && r.end--; // remove 1 millisecond from the final end range
59 drp.print = function(t, p) {
60 var format = ["", "-", "-", " ", ":", ":", "."];
61 var da = makeArray(t);
63 for(var i = 0; i <= p; i++) {
64 str += format[i] + (da[i] < 10 ? "0" : "") + da[i];
73 "yr" : 365*24*60*60*1000,
74 "mon" : 31*24*60*60*1000,
75 "day" : 24*60*60*1000,
82 "yr" : "y,yr,yrs,year,years",
83 "mon" : "mo,mon,mos,mons,month,months",
84 "day" : "d,dy,dys,day,days",
85 "hr" : "h,hr,hrs,hour,hours",
86 "min" : "m,min,mins,minute,minutes",
87 "sec" : "s,sec,secs,second,seconds"
90 for(var key in alias_lu) {
91 if(alias_lu.hasOwnProperty(key)) {
92 var aliases = alias_lu[key].split(",");
93 for(var i = 0; i < aliases.length; i++) {
94 drp._relTokens[aliases[i]] = values[key];
100 function makeArray(d) {
101 var da = new Date(d);
102 return [ da.getUTCFullYear(), da.getUTCMonth()+1, da.getUTCDate(), da.getUTCHours(), da.getUTCMinutes(), da.getUTCSeconds(), da.getUTCMilliseconds() ];
105 function fromArray(a) {
106 var d = [].concat(a); d[1]--;
107 return Date.UTC.apply(null, d);
110 drp._parse = function parse(v) {
111 var now = this.now || new Date().getTime();
113 function precArray(d, p, offset) {
114 var tn = makeArray(d);
115 tn[p] += offset || 0;
116 for(var i = p+1; i < 7; i++) {
117 tn[i] = i < 3 ? 1 : 0;
121 function makePrecRange(dt, p, r) {
123 ret.start = fromArray(dt);
125 ret.end = fromArray(dt);
128 function procTerm(term) {
129 var m = term.replace(/\s/g, "").toLowerCase().match(/^([a-z ]+)$|^([ 0-9:-]+)$|^(\d+[a-z]+)$/);
130 if(m[1]) { // matches ([a-z ]+)
131 function dra(p, o, r) {
132 var dt = precArray(now, p, o);
134 dt[2] -= new Date(fromArray(dt)).getUTCDay();
136 return makePrecRange(dt, p, r);
139 case "now" : return { start: now, end: now, now: now };
140 case "today" : return dra( 2, 0 );
141 case "thisweek" : return dra( 2, 0, 7 );
142 case "thismonth" : return dra( 1, 0 );
143 case "thisyear" : return dra( 0, 0 );
144 case "yesterday" : return dra( 2, -1 );
145 case "lastweek" : return dra( 2, -7, 7 );
146 case "lastmonth" : return dra( 1, -1 );
147 case "lastyear" : return dra( 0, -1 );
148 case "tomorrow" : return dra( 2, 1 );
149 case "nextweek" : return dra( 2, 7, 7 );
150 case "nextmonth" : return dra( 1, 1 );
151 case "nextyear" : return dra(0, 1 );
153 throw "unknown token " + m[1];
154 } else if(m[2]) { // matches ([ 0-9:-]+)
156 var dt = m[2].match(/^(?:(\d{4})(?:\-(\d\d))?(?:\-(\d\d))?)? ?(?:(\d{1,2})(?:\:(\d\d)(?:\:(\d\d))?)?)?$/);
158 for(var p = 0, z = false, i = 0; i < 7; i++) {
160 dn[i] = parseInt(dt[i], 10);
165 dn[i] = i < 3 ? 1 : 0;
168 return makePrecRange(dn, p);
169 } else if(m[3]) { // matches (\d+[a-z]{1,4})
170 var dr = m[3].match(/(\d+)\s*([a-z]+)/i);
171 var n = parseInt(dr[1], 10);
172 return { rel: n * drp._relTokens[dr[2]] };
174 throw "unknown term " + term;
178 return { start: null, end: null };
180 var terms = v.split(/\s*([^<>]*[^<>-])?\s*(->|<>|<)?\s*([^<>]+)?\s*/);
182 var term1 = terms[1] ? procTerm(terms[1]) : null;
183 var op = terms[2] || "";
184 var term2 = terms[3] ? procTerm(terms[3]) : null;
186 if(op === "<" || op === "->" ) {
187 if(term1 && !term2) {
188 return { start: term1.start, end: null };
189 } else if(!term1 && term2) {
190 return { start: null, end: term2.end };
193 return { start: term1.start, end: term1.end + term2.rel };
194 } else if(term1.rel) {
195 return { start: term2.start - term1.rel, end: term2.end };
197 return { start: term1.start, end: term2.end };
200 } else if(op === "<>") {
202 return { start: term1.start - drp.defaultRange, end: term1.end + drp.defaultRange }
204 if(! ("rel" in term2)) throw "second term did not hav a range";
205 return { start: term1.start - term2.rel, end: term1.end + term2.rel };
209 return { start: now - term1.rel, end: now + term1.rel };
210 } else if(term1.now) {
211 return { start: term1.now - drp.defaultRange, end: term1.now + drp.defaultRange };
213 return { start: term1.start, end: term1.end };
216 throw "could not process value " + v;