1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/calendar.h"
24 #include "data/settings.h"
25 #include "data/val-type.h"
28 #define _(msgid) gettext (msgid)
31 #define EPOCH (-577734)
33 /* Calculates and returns floor(a/b) for integer b > 0. */
35 floor_div (int a, int b)
38 return (a >= 0 ? a : a - b + 1) / b;
41 /* Calculates floor(a/b) and the corresponding remainder and
42 stores them into *Q and *R. */
44 floor_divmod (int a, int b, int *q, int *r)
46 *q = floor_div (a, b);
50 /* Returns true if Y is a leap year, false otherwise. */
54 return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
58 calendar_raw_gregorian_to_offset (int y, int m, int d)
62 + floor_div (y - 1, 4)
63 - floor_div (y - 1, 100)
64 + floor_div (y - 1, 400)
65 + floor_div (367 * m - 362, 12)
66 + (m <= 2 ? 0 : (m >= 2 && is_leap_year (y) ? -1 : -2))
71 calendar_gregorian_adjust (int *y, int *m, int *d,
72 const struct fmt_settings *settings)
75 if (*y >= 0 && *y < 100)
77 int epoch = fmt_settings_get_epoch (settings);
78 int century = epoch / 100 + (*y < epoch % 100);
82 /* Normalize month. */
83 if (*m < 1 || *m > 12)
100 if (*d < 0 || *d > 31)
104 if (*y < 1582 || (*y == 1582 && (*m < 10 || (*m == 10 && *d < 15))))
110 /* Returns the number of days from 14 Oct 1582 to (Y,M,D) in the
111 Gregorian calendar. Returns SYSMIS for dates before 14 Oct
114 calendar_gregorian_to_offset (int y, int m, int d,
115 const struct fmt_settings *settings,
118 int *bad_value = calendar_gregorian_adjust (&y, &m, &d, settings);
123 return calendar_raw_gregorian_to_offset (y, m, d);
130 *errorp = xasprintf (_("Date %04d-%d-%d is before the earliest "
131 "supported date 1582-10-15."), y, m, d);
132 else if (bad_value == &m)
133 *errorp = xasprintf (_("Month %d is not in the acceptable range of "
136 *errorp = xasprintf (_("Day %d is not in the acceptable range of "
143 /* Returns the number of days in the given YEAR from January 1 up
144 to (but not including) the first day of MONTH. */
146 cum_month_days (int year, int month)
148 static const int cum_month_days[12] =
153 31 + 28 + 31, /* Mar */
154 31 + 28 + 31 + 30, /* Apr */
155 31 + 28 + 31 + 30 + 31, /* May */
156 31 + 28 + 31 + 30 + 31 + 30, /* Jun */
157 31 + 28 + 31 + 30 + 31 + 30 + 31, /* Jul */
158 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, /* Aug */
159 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, /* Sep */
160 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, /* Oct */
161 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, /* Nov */
164 assert (month >= 1 && month <= 12);
165 return cum_month_days[month - 1] + (month >= 3 && is_leap_year (year));
168 /* Takes a count of days from 14 Oct 1582 and returns the
169 Gregorian calendar year it is in. Dates both before and after
170 the epoch are supported. */
172 calendar_offset_to_year (int ofs)
182 floor_divmod (d0, 365 * 400 + 100 - 3, &n400, &d1);
183 floor_divmod (d1, 365 * 100 + 25 - 1, &n100, &d2);
184 floor_divmod (d2, 365 * 4 + 1, &n4, &d3);
185 n1 = floor_div (d3, 365);
186 y = 400 * n400 + 100 * n100 + 4 * n4 + n1;
187 if (n100 != 4 && n1 != 4)
193 /* Takes a count of days from 14 Oct 1582 and translates it into
194 a Gregorian calendar date in (*Y,*M,*D). Also stores the
195 year-relative day number into *YD. Dates both before and
196 after the epoch are supported. */
198 calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d, int *yd)
200 int year = *y = calendar_offset_to_year (ofs);
201 int january1 = calendar_raw_gregorian_to_offset (year, 1, 1);
202 int yday = *yd = ofs - january1 + 1;
203 int march1 = january1 + cum_month_days (year, 3);
204 int correction = ofs < march1 ? 0 : (is_leap_year (year) ? 1 : 2);
205 int month = *m = (12 * (yday - 1 + correction) + 373) / 367;
206 *d = yday - cum_month_days (year, month);
209 /* Takes a count of days from 14 Oct 1582 and returns the 1-based
210 year-relative day number, that is, the number of days from the
211 beginning of the year. */
213 calendar_offset_to_yday (int ofs)
215 int year = calendar_offset_to_year (ofs);
216 int january1 = calendar_raw_gregorian_to_offset (year, 1, 1);
217 int yday = ofs - january1 + 1;
221 /* Takes a count of days from 14 Oct 1582 and returns the
222 corresponding weekday 1...7, with 1=Sunday. */
224 calendar_offset_to_wday (int ofs)
226 int wday = (ofs - EPOCH + 1) % 7 + 1;
232 /* Takes a count of days from 14 Oct 1582 and returns the month
235 calendar_offset_to_month (int ofs)
238 calendar_offset_to_gregorian (ofs, &y, &m, &d, &yd);
242 /* Takes a count of days from 14 Oct 1582 and returns the
243 corresponding day of the month. */
245 calendar_offset_to_mday (int ofs)
248 calendar_offset_to_gregorian (ofs, &y, &m, &d, &yd);
252 /* Returns the number of days in the specified month. */
254 calendar_days_in_month (int y, int m)
256 static const int days_per_month[12]
257 = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
259 assert (m >= 1 && m <= 12);
260 return m == 2 && is_leap_year (y) ? 29 : days_per_month[m - 1];