1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "data/calendar.h"
20
21 #include <assert.h>
22 #include <stdbool.h>
23
24 #include "data/settings.h"
25 #include "data/val-type.h"
26
27 #include "gettext.h"
28 #define _(msgid) gettext (msgid)
29
30 /* 14 Oct 1582. */
31 #define EPOCH (-577734)
32
33 /* Calculates and returns floor(a/b) for integer b > 0. */
34 static int
floor_div(int a,int b)35 floor_div (int a, int b)
36 {
37 assert (b > 0);
38 return (a >= 0 ? a : a - b + 1) / b;
39 }
40
41 /* Calculates floor(a/b) and the corresponding remainder and
42 stores them into *Q and *R. */
43 static void
floor_divmod(int a,int b,int * q,int * r)44 floor_divmod (int a, int b, int *q, int *r)
45 {
46 *q = floor_div (a, b);
47 *r = a - b * *q;
48 }
49
50 /* Returns true if Y is a leap year, false otherwise. */
51 static bool
is_leap_year(int y)52 is_leap_year (int y)
53 {
54 return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
55 }
56
57 static int
raw_gregorian_to_offset(int y,int m,int d)58 raw_gregorian_to_offset (int y, int m, int d)
59 {
60 return (EPOCH - 1
61 + 365 * (y - 1)
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))
67 + d);
68 }
69
70 /* Returns the number of days from 14 Oct 1582 to (Y,M,D) in the
71 Gregorian calendar. Returns SYSMIS for dates before 14 Oct
72 1582. */
73 double
calendar_gregorian_to_offset(int y,int m,int d,char ** errorp)74 calendar_gregorian_to_offset (int y, int m, int d, char **errorp)
75 {
76 /* Normalize year. */
77 if (y >= 0 && y < 100)
78 {
79 int epoch = settings_get_epoch ();
80 int century = epoch / 100 + (y < epoch % 100);
81 y += century * 100;
82 }
83
84 /* Normalize month. */
85 if (m < 1 || m > 12)
86 {
87 if (m == 0)
88 {
89 y--;
90 m = 12;
91 }
92 else if (m == 13)
93 {
94 y++;
95 m = 1;
96 }
97 else
98 {
99 if (errorp != NULL)
100 *errorp = xasprintf (_("Month %d is not in acceptable range of "
101 "0 to 13."), m);
102 return SYSMIS;
103 }
104 }
105
106 /* Normalize day. */
107 if (d < 0 || d > 31)
108 {
109 if (errorp != NULL)
110 *errorp = xasprintf (_("Day %d is not in acceptable range of "
111 "0 to 31."), d);
112 return SYSMIS;
113 }
114
115 /* Validate date. */
116 if (y < 1582 || (y == 1582 && (m < 10 || (m == 10 && d < 15))))
117 {
118 if (errorp != NULL)
119 *errorp = xasprintf (_("Date %04d-%d-%d is before the earliest "
120 "acceptable date of 1582-10-15."), y, m, d);
121 return SYSMIS;
122 }
123
124 /* Calculate offset. */
125 if (errorp != NULL)
126 *errorp = NULL;
127 return raw_gregorian_to_offset (y, m, d);
128 }
129
130 /* Returns the number of days in the given YEAR from January 1 up
131 to (but not including) the first day of MONTH. */
132 static int
cum_month_days(int year,int month)133 cum_month_days (int year, int month)
134 {
135 static const int cum_month_days[12] =
136 {
137 0,
138 31, /* Jan */
139 31 + 28, /* Feb */
140 31 + 28 + 31, /* Mar */
141 31 + 28 + 31 + 30, /* Apr */
142 31 + 28 + 31 + 30 + 31, /* May */
143 31 + 28 + 31 + 30 + 31 + 30, /* Jun */
144 31 + 28 + 31 + 30 + 31 + 30 + 31, /* Jul */
145 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, /* Aug */
146 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, /* Sep */
147 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, /* Oct */
148 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, /* Nov */
149 };
150
151 assert (month >= 1 && month <= 12);
152 return cum_month_days[month - 1] + (month >= 3 && is_leap_year (year));
153 }
154
155 /* Takes a count of days from 14 Oct 1582 and returns the
156 Gregorian calendar year it is in. Dates both before and after
157 the epoch are supported. */
158 int
calendar_offset_to_year(int ofs)159 calendar_offset_to_year (int ofs)
160 {
161 int d0;
162 int n400, d1;
163 int n100, d2;
164 int n4, d3;
165 int n1;
166 int y;
167
168 d0 = ofs - EPOCH;
169 floor_divmod (d0, 365 * 400 + 100 - 3, &n400, &d1);
170 floor_divmod (d1, 365 * 100 + 25 - 1, &n100, &d2);
171 floor_divmod (d2, 365 * 4 + 1, &n4, &d3);
172 n1 = floor_div (d3, 365);
173 y = 400 * n400 + 100 * n100 + 4 * n4 + n1;
174 if (n100 != 4 && n1 != 4)
175 y++;
176
177 return y;
178 }
179
180 /* Takes a count of days from 14 Oct 1582 and translates it into
181 a Gregorian calendar date in (*Y,*M,*D). Also stores the
182 year-relative day number into *YD. Dates both before and
183 after the epoch are supported. */
184 void
calendar_offset_to_gregorian(int ofs,int * y,int * m,int * d,int * yd)185 calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d, int *yd)
186 {
187 int year = *y = calendar_offset_to_year (ofs);
188 int january1 = raw_gregorian_to_offset (year, 1, 1);
189 int yday = *yd = ofs - january1 + 1;
190 int march1 = january1 + cum_month_days (year, 3);
191 int correction = ofs < march1 ? 0 : (is_leap_year (year) ? 1 : 2);
192 int month = *m = (12 * (yday - 1 + correction) + 373) / 367;
193 *d = yday - cum_month_days (year, month);
194 }
195
196 /* Takes a count of days from 14 Oct 1582 and returns the 1-based
197 year-relative day number, that is, the number of days from the
198 beginning of the year. */
199 int
calendar_offset_to_yday(int ofs)200 calendar_offset_to_yday (int ofs)
201 {
202 int year = calendar_offset_to_year (ofs);
203 int january1 = raw_gregorian_to_offset (year, 1, 1);
204 int yday = ofs - january1 + 1;
205 return yday;
206 }
207
208 /* Takes a count of days from 14 Oct 1582 and returns the
209 corresponding weekday 1...7, with 1=Sunday. */
210 int
calendar_offset_to_wday(int ofs)211 calendar_offset_to_wday (int ofs)
212 {
213 int wday = (ofs - EPOCH + 1) % 7 + 1;
214 if (wday <= 0)
215 wday += 7;
216 return wday;
217 }
218
219 /* Takes a count of days from 14 Oct 1582 and returns the month
220 it is in. */
221 int
calendar_offset_to_month(int ofs)222 calendar_offset_to_month (int ofs)
223 {
224 int y, m, d, yd;
225 calendar_offset_to_gregorian (ofs, &y, &m, &d, &yd);
226 return m;
227 }
228
229 /* Takes a count of days from 14 Oct 1582 and returns the
230 corresponding day of the month. */
231 int
calendar_offset_to_mday(int ofs)232 calendar_offset_to_mday (int ofs)
233 {
234 int y, m, d, yd;
235 calendar_offset_to_gregorian (ofs, &y, &m, &d, &yd);
236 return d;
237 }
238
239 /* Returns the number of days in the specified month. */
240 int
calendar_days_in_month(int y,int m)241 calendar_days_in_month (int y, int m)
242 {
243 static const int days_per_month[12]
244 = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
245
246 assert (m >= 1 && m <= 12);
247 return m == 2 && is_leap_year (y) ? 29 : days_per_month[m - 1];
248 }
249