Fix tg_termpos1 for 64-bit termpos
[xapian.git] / xapian-applications / omega / date.cc
blob55ddac5ca4ced775a1233348dd7913ead8582ead
1 /** @file
2 * @brief date range parsing routines for omega
3 */
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,2024 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
23 * USA
26 #include <config.h>
28 #include "date.h"
30 #include <vector>
32 #include <cstdlib>
33 #include <ctime>
34 #include "parseint.h"
36 using namespace std;
38 static int
39 last_day(int y, int m)
41 static const int l[13] = {
42 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
44 if (m != 2) return l[m];
45 return 28 + (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0));
48 // Write exactly w chars to buffer p representing integer v.
50 // The result is left padded with zeros if v < pow(10, w - 1).
52 // If v >= pow(10, w), then the output will show v % pow(10, w) (i.e. the
53 // most significant digits are lost).
54 static void
55 format_int_fixed_width(char * p, int v, int w)
57 while (--w >= 0) {
58 p[w] = '0' + (v % 10);
59 v /= 10;
63 static Xapian::Query
64 date_range_filter(int y1, int m1, int d1, int y2, int m2, int d2)
66 if (y1 > y2 || (y1 == y2 && (m1 > m2 || (m1 == m2 && d1 > d2)))) {
67 // Start is after end.
68 return Xapian::Query::MatchNothing;
70 char buf[10];
71 format_int_fixed_width(buf + 1, y1, 4);
72 format_int_fixed_width(buf + 5, m1, 2);
73 vector<Xapian::Query> v;
75 int d_last = last_day(y1, m1);
76 int d_end = d_last;
77 if (d1 == 1 && m1 == 1 && y1 != y2) {
78 --y1;
79 goto whole_year_at_start;
81 if (y1 == y2 && m1 == m2 && d2 < d_last) {
82 d_end = d2;
84 // Deal with any initial partial month
85 if (d1 > 1 || d_end < d_last) {
86 buf[0] = 'D';
87 for ( ; d1 <= d_end; ++d1) {
88 format_int_fixed_width(buf + 7, d1, 2);
89 v.push_back(Xapian::Query(string_view(buf, 9)));
91 } else {
92 buf[0] = 'M';
93 v.push_back(Xapian::Query(string_view(buf, 7)));
96 if (y1 == y2 && m1 == m2) {
97 return Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end());
101 int m_last = (y1 < y2) ? 12 : m2 - 1;
102 while (++m1 <= m_last) {
103 format_int_fixed_width(buf + 5, m1, 2);
104 buf[0] = 'M';
105 v.push_back(Xapian::Query(string_view(buf, 7)));
109 if (y1 < y2) {
110 whole_year_at_start:
111 while (++y1 < y2) {
112 format_int_fixed_width(buf + 1, y1, 4);
113 buf[0] = 'Y';
114 v.push_back(Xapian::Query(string_view(buf, 5)));
116 format_int_fixed_width(buf + 1, y2, 4);
117 if (m2 == 12 && d2 >= 31) {
118 v.push_back(Xapian::Query(string_view(buf, 5)));
119 goto whole_year_at_end;
121 buf[0] = 'M';
122 for (m1 = 1; m1 < m2; ++m1) {
123 format_int_fixed_width(buf + 5, m1, 2);
124 v.push_back(Xapian::Query(string_view(buf, 7)));
128 format_int_fixed_width(buf + 5, m2, 2);
130 // Deal with any final partial month
131 if (d2 < last_day(y2, m2)) {
132 buf[0] = 'D';
133 for (d1 = 1; d1 <= d2; ++d1) {
134 format_int_fixed_width(buf + 7, d1, 2);
135 v.push_back(Xapian::Query(string_view(buf, 9)));
137 } else {
138 buf[0] = 'M';
139 v.push_back(Xapian::Query(string_view(buf, 7)));
142 whole_year_at_end:
143 return Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end());
146 static int DIGIT(char ch) { return ch - '0'; }
148 static int DIGIT2(const char *p) {
149 return DIGIT(p[0]) * 10 + DIGIT(p[1]);
152 static int DIGIT4(const char *p) {
153 return DIGIT2(p) * 100 + DIGIT2(p + 2);
156 static void
157 parse_date(const string & date, int *y, int *m, int *d, bool start)
159 // Support YYYYMMDD, YYYYMM and YYYY.
160 if (date.size() < 4) {
161 // We default to the start of 1970 when START isn't specified, so it
162 // seems logical to here do that here too.
163 *y = 1970;
164 } else {
165 *y = DIGIT4(date.c_str());
167 if (date.size() < 6) {
168 if (start) {
169 *m = 1;
170 *d = 1;
171 } else {
172 *m = 12;
173 *d = 31;
175 return;
177 *m = DIGIT2(date.c_str() + 4);
178 if (date.size() < 8) {
179 if (start) {
180 *d = 1;
181 } else {
182 *d = last_day(*y, *m);
184 return;
186 *d = DIGIT2(date.c_str() + 6);
189 Xapian::Query
190 date_range_filter(const string & date_start, const string & date_end,
191 const string & date_span)
193 int y1, m1, d1, y2, m2, d2;
194 if (!date_span.empty()) {
195 unsigned int days;
196 if (!parse_unsigned(date_span.c_str(), days)) {
197 throw "Datespan value must be >= 0";
199 time_t secs = days * (24 * 60 * 60);
200 if (!date_end.empty()) {
201 parse_date(date_end, &y2, &m2, &d2, false);
202 struct tm t;
203 t.tm_year = y2 - 1900;
204 t.tm_mon = m2 - 1;
205 t.tm_mday = d2;
206 t.tm_hour = 12;
207 t.tm_min = t.tm_sec = 0;
208 t.tm_isdst = -1;
209 time_t then = mktime(&t) - secs;
210 struct tm *t2 = localtime(&then);
211 y1 = t2->tm_year + 1900;
212 m1 = t2->tm_mon + 1;
213 d1 = t2->tm_mday;
214 } else if (!date_start.empty()) {
215 parse_date(date_start, &y1, &m1, &d1, true);
216 struct tm t;
217 t.tm_year = y1 - 1900;
218 t.tm_mon = m1 - 1;
219 t.tm_mday = d1;
220 t.tm_hour = 12;
221 t.tm_min = t.tm_sec = 0;
222 t.tm_isdst = -1;
223 time_t end = mktime(&t) + secs;
224 struct tm *t2 = localtime(&end);
225 y2 = t2->tm_year + 1900;
226 m2 = t2->tm_mon + 1;
227 d2 = t2->tm_mday;
228 } else {
229 time_t end = time(NULL);
230 struct tm *t = localtime(&end);
231 y2 = t->tm_year + 1900;
232 m2 = t->tm_mon + 1;
233 d2 = t->tm_mday;
234 time_t then = end - secs;
235 struct tm *t2 = localtime(&then);
236 y1 = t2->tm_year + 1900;
237 m1 = t2->tm_mon + 1;
238 d1 = t2->tm_mday;
240 } else {
241 if (date_start.empty()) {
242 y1 = 1970;
243 m1 = 1;
244 d1 = 1;
245 } else {
246 parse_date(date_start, &y1, &m1, &d1, true);
248 if (date_end.empty()) {
249 time_t now = time(NULL);
250 struct tm *t = localtime(&now);
251 y2 = t->tm_year + 1900;
252 m2 = t->tm_mon + 1;
253 d2 = t->tm_mday;
254 } else {
255 parse_date(date_end, &y2, &m2, &d2, false);
258 return date_range_filter(y1, m1, d1, y2, m2, d2);