2 * @brief date range parsing routines for omega
4 /* Copyright 1999,2000,2001 BrightStation PLC
5 * Copyright 2001 James Aylett
6 * Copyright 2001,2002 Ananova Ltd
7 * Copyright 2002 Intercede 1749 Ltd
8 * Copyright 2002,2003,2006,2014,2016,2017,2018 Olly Betts
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
38 last_day(int y
, int m
)
40 static const int l
[13] = {
41 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
43 if (m
!= 2) return l
[m
];
44 return 28 + (y
% 4 == 0 && (y
% 100 != 0 || y
% 400 == 0));
47 // Write exactly w chars to buffer p representing integer v.
49 // The result is left padded with zeros if v < pow(10, w - 1).
51 // If v >= pow(10, w), then the output will show v % pow(10, w) (i.e. the
52 // most significant digits are lost).
54 format_int_fixed_width(char * p
, int v
, int w
)
57 p
[w
] = '0' + (v
% 10);
63 date_range_filter(int y1
, int m1
, int d1
, int y2
, int m2
, int d2
)
65 if (y1
> y2
|| (y1
== y2
&& (m1
> m2
|| (m1
== m2
&& d1
> d2
)))) {
66 // Start is after end.
67 return Xapian::Query::MatchNothing
;
70 format_int_fixed_width(buf
+ 1, y1
, 4);
71 format_int_fixed_width(buf
+ 5, m1
, 2);
72 vector
<Xapian::Query
> v
;
74 int d_last
= last_day(y1
, m1
);
76 if (d1
== 1 && m1
== 1 && y1
!= y2
) {
78 goto whole_year_at_start
;
80 if (y1
== y2
&& m1
== m2
&& d2
< d_last
) {
83 // Deal with any initial partial month
84 if (d1
> 1 || d_end
< d_last
) {
86 for ( ; d1
<= d_end
; ++d1
) {
87 format_int_fixed_width(buf
+ 7, d1
, 2);
88 v
.push_back(Xapian::Query(string(buf
, 9)));
92 v
.push_back(Xapian::Query(string(buf
, 7)));
95 if (y1
== y2
&& m1
== m2
) {
96 return Xapian::Query(Xapian::Query::OP_OR
, v
.begin(), v
.end());
100 int m_last
= (y1
< y2
) ? 12 : m2
- 1;
101 while (++m1
<= m_last
) {
102 format_int_fixed_width(buf
+ 5, m1
, 2);
104 v
.push_back(Xapian::Query(string(buf
, 7)));
111 format_int_fixed_width(buf
+ 1, y1
, 4);
113 v
.push_back(Xapian::Query(string(buf
, 5)));
115 format_int_fixed_width(buf
+ 1, y2
, 4);
116 if (m2
== 12 && d2
>= 31) {
117 v
.push_back(Xapian::Query(string(buf
, 5)));
118 goto whole_year_at_end
;
121 for (m1
= 1; m1
< m2
; ++m1
) {
122 format_int_fixed_width(buf
+ 5, m1
, 2);
123 v
.push_back(Xapian::Query(string(buf
, 7)));
127 format_int_fixed_width(buf
+ 5, m2
, 2);
129 // Deal with any final partial month
130 if (d2
< last_day(y2
, m2
)) {
132 for (d1
= 1; d1
<= d2
; ++d1
) {
133 format_int_fixed_width(buf
+ 7, d1
, 2);
134 v
.push_back(Xapian::Query(string(buf
, 9)));
138 v
.push_back(Xapian::Query(string(buf
, 7)));
142 return Xapian::Query(Xapian::Query::OP_OR
, v
.begin(), v
.end());
145 static int DIGIT(char ch
) { return ch
- '0'; }
147 static int DIGIT2(const char *p
) {
148 return DIGIT(p
[0]) * 10 + DIGIT(p
[1]);
151 static int DIGIT4(const char *p
) {
152 return DIGIT2(p
) * 100 + DIGIT2(p
+ 2);
156 parse_date(const string
& date
, int *y
, int *m
, int *d
, bool start
)
158 // Support YYYYMMDD, YYYYMM and YYYY.
159 if (date
.size() < 4) {
160 // We default to the start of 1970 when START isn't specified, so it
161 // seems logical to here do that here too.
164 *y
= DIGIT4(date
.c_str());
166 if (date
.size() < 6) {
176 *m
= DIGIT2(date
.c_str() + 4);
177 if (date
.size() < 8) {
181 *d
= last_day(*y
, *m
);
185 *d
= DIGIT2(date
.c_str() + 6);
189 date_range_filter(const string
& date_start
, const string
& date_end
,
190 const string
& date_span
)
192 int y1
, m1
, d1
, y2
, m2
, d2
;
193 if (!date_span
.empty()) {
194 time_t secs
= atoi(date_span
.c_str()) * (24 * 60 * 60);
195 if (!date_end
.empty()) {
196 parse_date(date_end
, &y2
, &m2
, &d2
, false);
198 t
.tm_year
= y2
- 1900;
202 t
.tm_min
= t
.tm_sec
= 0;
204 time_t then
= mktime(&t
) - secs
;
205 struct tm
*t2
= localtime(&then
);
206 y1
= t2
->tm_year
+ 1900;
209 } else if (!date_start
.empty()) {
210 parse_date(date_start
, &y1
, &m1
, &d1
, true);
212 t
.tm_year
= y1
- 1900;
216 t
.tm_min
= t
.tm_sec
= 0;
218 time_t end
= mktime(&t
) + secs
;
219 struct tm
*t2
= localtime(&end
);
220 y2
= t2
->tm_year
+ 1900;
224 time_t end
= time(NULL
);
225 struct tm
*t
= localtime(&end
);
226 y2
= t
->tm_year
+ 1900;
229 time_t then
= end
- secs
;
230 struct tm
*t2
= localtime(&then
);
231 y1
= t2
->tm_year
+ 1900;
236 if (date_start
.empty()) {
241 parse_date(date_start
, &y1
, &m1
, &d1
, true);
243 if (date_end
.empty()) {
244 time_t now
= time(NULL
);
245 struct tm
*t
= localtime(&now
);
246 y2
= t
->tm_year
+ 1900;
250 parse_date(date_end
, &y2
, &m2
, &d2
, false);
253 return date_range_filter(y1
, m1
, d1
, y2
, m2
, d2
);