Introspection: fix problems with boxed type.
[gnumeric.git] / src / gnm-datetime.c
blobaab13df1ff71b7f02519d1e1214cfeb16b6314a3
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * gnm-datetime.c:
6 * Copyright (C) 2005
7 * Miguel de Icaza (miguel@gnu.org)
8 * Morten Welinder <terra@gnome.org>
9 * Jukka-Pekka Iivonen <iivonen@iki.fi>
10 * Andreas J. Guelzow <aguelzow@taliesin.ca>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) version 3.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 * USA
27 #include <gnumeric-config.h>
28 #include <goffice/goffice.h>
29 #include "value.h"
30 #include <string.h>
31 #include "gnm-datetime.h"
32 #include "gnm-format.h"
33 #include "number-match.h"
36 * Figure out whether the format engine in goffice allows negative values
37 * or (as XL) considers them errors.
39 gboolean
40 gnm_datetime_allow_negative (void)
42 static int allow = -1;
44 if (allow == -1) {
45 GOFormat *fmt = go_format_new_from_XL ("yyyy-mm-dd");
46 GnmValue *v = value_new_int (-42);
47 GODateConventions const *conv =
48 go_date_conv_from_str ("Lotus:1900");
49 char *text = format_value (fmt, v, -1, conv);
51 allow = (strcmp (text, "1899-11-19") == 0);
53 value_release (v);
54 go_format_unref (fmt);
55 g_free (text);
58 return (gboolean)allow;
61 gnm_float
62 datetime_value_to_serial_raw (GnmValue const *v, GODateConventions const *conv)
64 gnm_float serial;
66 if (VALUE_IS_NUMBER (v))
67 serial = value_get_as_float (v);
68 else {
69 char const *str = value_peek_string (v);
70 GnmValue *conversion = format_match_number (str, go_format_default_date (), conv);
72 if (conversion) {
73 serial = value_get_as_float (conversion);
74 value_release (conversion);
75 } else
76 serial = G_MAXINT;
79 if (serial < 0 && !gnm_datetime_allow_negative ())
80 serial = G_MAXINT;
82 return serial;
85 /* ------------------------------------------------------------------------- */
87 int
88 datetime_value_to_serial (GnmValue const *v, GODateConventions const *conv)
90 gnm_float serial = datetime_value_to_serial_raw (v, conv);
91 if (serial >= G_MAXINT || serial < G_MININT)
92 return G_MAXINT;
93 return go_date_serial_raw_to_serial (serial);
96 /* ------------------------------------------------------------------------- */
98 gboolean
99 datetime_value_to_g (GDate *res, GnmValue const *v, GODateConventions const *conv)
101 int serial = datetime_value_to_serial (v, conv);
102 if (serial == G_MAXINT) {
103 g_date_clear (res, 1);
104 return FALSE;
106 go_date_serial_to_g (res, serial, conv);
107 return g_date_valid (res);
110 /* ------------------------------------------------------------------------- */
113 * Returns the number of days in the year for the given date accoring to
114 * the day counting system specified by 'basis' argument. Basis may have
115 * one of the following values:
117 * 0 for US 30/360 (days in a month/days in a year)
118 * 1 for actual days/actual days
119 * 2 for actual days/360
120 * 3 for actual days/365
121 * 4 for European 30/360
123 * This function returns 360 for basis 0, 2, and 4, it returns value
124 * 365 for basis 3, and value 365 or 366 for basis 1 accoring to the
125 * year of the given date (366 is returned if the date is in a leap
126 * year).
129 annual_year_basis (GnmValue const *value_date, GOBasisType basis,
130 GODateConventions const *date_conv)
132 GDate date;
134 switch (basis) {
135 case GO_BASIS_MSRB_30_360:
136 return 360;
137 case GO_BASIS_ACT_ACT:
138 if (!datetime_value_to_g (&date, value_date, date_conv))
139 return -1;
140 return g_date_is_leap_year (g_date_get_year (&date))
141 ? 366 : 365;
142 case GO_BASIS_ACT_360:
143 return 360;
144 case GO_BASIS_ACT_365:
145 return 365;
146 case GO_BASIS_30E_360:
147 return 360;
148 default:
149 return -1;
153 gnm_float
154 yearfrac (GDate const *from, GDate const *to, GOBasisType basis)
156 int days;
157 gnm_float peryear;
159 if (!g_date_valid (from) || !g_date_valid (to))
160 return gnm_nan;
162 days = go_date_days_between_basis (from, to, basis);
164 if (days < 0) {
165 const GDate *tmp;
166 days = -days;
167 tmp = from; from = to; to = tmp;
170 switch (basis) {
171 case GO_BASIS_ACT_ACT: {
172 int y1 = g_date_get_year (from);
173 int y2 = g_date_get_year (to);
174 GDate d1, d2;
175 int feb29s, years;
177 d1 = *from;
178 gnm_date_add_years (&d1, 1);
179 if (g_date_compare (to, &d1) > 0) {
180 /* More than one year. */
181 years = y2 + 1 - y1;
183 g_date_clear (&d1, 1);
184 g_date_set_dmy (&d1, 1, 1, y1);
186 g_date_clear (&d2, 1);
187 g_date_set_dmy (&d2, 1, 1, y2 + 1);
189 feb29s = g_date_get_julian (&d2) - g_date_get_julian (&d1) -
190 365 * (y2 + 1 - y1);
191 } else {
192 /* Less than one year. */
193 years = 1;
195 if ((g_date_is_leap_year (y1) && g_date_get_month (from) < 3) ||
196 (g_date_is_leap_year (y2) &&
197 (g_date_get_month (to) * 0x100 + g_date_get_day (to) >= 2 * 0x100 + 29)))
198 feb29s = 1;
199 else
200 feb29s = 0;
203 peryear = 365 + (gnm_float)feb29s / years;
205 break;
208 default:
209 peryear = annual_year_basis (NULL, basis, NULL);
212 return days / peryear;
215 /* ------------------------------------------------------------------------- */
216 /* Like g_date_add_days, but...
218 * 1. Do not spew criticals.
219 * 2. Number of days is signed.
222 void
223 gnm_date_add_days (GDate *d, int n)
225 if (!g_date_valid (d))
226 return;
228 if (n >= 0) {
229 guint32 lim = 23936166; /* 31-Dec-65535 */
230 guint32 j = g_date_get_julian (d);
232 if (j > lim || (unsigned)n > lim - j)
233 goto bad;
235 g_date_add_days (d, n);
236 } else {
237 int m = g_date_get_julian (d) - 1;
239 if (m + n <= 0)
240 goto bad;
242 g_date_subtract_days (d, -n);
245 return;
247 bad:
248 g_date_clear (d, 1);
251 /* Like g_date_add_months, but...
253 * 1. Do not spew criticals.
254 * 2. Number of months is signed.
256 void
257 gnm_date_add_months (GDate *d, int n)
259 if (!g_date_valid (d))
260 return;
262 if (n >= 0) {
263 int m = (65535 - g_date_get_year (d)) * 12 +
264 (12 - g_date_get_month (d));
266 if (n > m)
267 goto bad;
269 g_date_add_months (d, n);
270 } else {
271 int m = (g_date_get_year (d) - 1) * 12 +
272 (g_date_get_month (d) - 1);
274 if (m + n <= 0)
275 goto bad;
277 g_date_subtract_months (d, -n);
280 return;
282 bad:
283 g_date_clear (d, 1);
286 /* Like g_date_add_years, but...
288 * 1. Do not spew criticals.
289 * 2. Number of years is signed.
291 void
292 gnm_date_add_years (GDate *d, int n)
294 if (!g_date_valid (d))
295 return;
297 if (n >= 0) {
298 int m = 65535 - g_date_get_year (d);
300 if (n > m)
301 goto bad;
303 g_date_add_years (d, n);
304 } else {
305 int m = g_date_get_year (d) - 1;
307 if (m + n <= 0)
308 goto bad;
310 g_date_subtract_years (d, -n);
313 return;
315 bad:
316 g_date_clear (d, 1);
319 #define DAY_SECONDS (3600*24)
321 datetime_value_to_seconds (GnmValue const *v, GODateConventions const *conv)
323 int secs;
324 gnm_float d = datetime_value_to_serial_raw (v, conv);
325 if (d >= G_MAXINT || d < G_MININT)
326 return -1;
328 /* Add epsilon before we scale and translate because otherwise it
329 will not be enough. */
330 d = gnm_add_epsilon (d);
332 /* Get the number down between 0 and 1 before we scale. */
333 d -= gnm_floor (d);
335 /* Scale and round. */
336 secs = (int)(gnm_add_epsilon (d) * DAY_SECONDS + 0.5);
338 /* We rounded, so we might have gone too far. */
339 if (secs >= DAY_SECONDS)
340 secs -= DAY_SECONDS;
342 return secs;