1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
27 #include <gnumeric-config.h>
28 #include <goffice/goffice.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.
40 gnm_datetime_allow_negative (void)
42 static int 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);
54 go_format_unref (fmt
);
58 return (gboolean
)allow
;
62 datetime_value_to_serial_raw (GnmValue
const *v
, GODateConventions
const *conv
)
66 if (VALUE_IS_NUMBER (v
))
67 serial
= value_get_as_float (v
);
69 char const *str
= value_peek_string (v
);
70 GnmValue
*conversion
= format_match_number (str
, go_format_default_date (), conv
);
73 serial
= value_get_as_float (conversion
);
74 value_release (conversion
);
79 if (serial
< 0 && !gnm_datetime_allow_negative ())
85 /* ------------------------------------------------------------------------- */
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
)
93 return go_date_serial_raw_to_serial (serial
);
96 /* ------------------------------------------------------------------------- */
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);
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
129 annual_year_basis (GnmValue
const *value_date
, GOBasisType basis
,
130 GODateConventions
const *date_conv
)
135 case GO_BASIS_MSRB_30_360
:
137 case GO_BASIS_ACT_ACT
:
138 if (!datetime_value_to_g (&date
, value_date
, date_conv
))
140 return g_date_is_leap_year (g_date_get_year (&date
))
142 case GO_BASIS_ACT_360
:
144 case GO_BASIS_ACT_365
:
146 case GO_BASIS_30E_360
:
154 yearfrac (GDate
const *from
, GDate
const *to
, GOBasisType basis
)
159 if (!g_date_valid (from
) || !g_date_valid (to
))
162 days
= go_date_days_between_basis (from
, to
, basis
);
167 tmp
= from
; from
= to
; to
= tmp
;
171 case GO_BASIS_ACT_ACT
: {
172 int y1
= g_date_get_year (from
);
173 int y2
= g_date_get_year (to
);
178 gnm_date_add_years (&d1
, 1);
179 if (g_date_compare (to
, &d1
) > 0) {
180 /* More than one year. */
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
) -
192 /* Less than one year. */
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)))
203 peryear
= 365 + (gnm_float
)feb29s
/ years
;
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.
223 gnm_date_add_days (GDate
*d
, int n
)
225 if (!g_date_valid (d
))
229 guint32 lim
= 23936166; /* 31-Dec-65535 */
230 guint32 j
= g_date_get_julian (d
);
232 if (j
> lim
|| (unsigned)n
> lim
- j
)
235 g_date_add_days (d
, n
);
237 int m
= g_date_get_julian (d
) - 1;
242 g_date_subtract_days (d
, -n
);
251 /* Like g_date_add_months, but...
253 * 1. Do not spew criticals.
254 * 2. Number of months is signed.
257 gnm_date_add_months (GDate
*d
, int n
)
259 if (!g_date_valid (d
))
263 int m
= (65535 - g_date_get_year (d
)) * 12 +
264 (12 - g_date_get_month (d
));
269 g_date_add_months (d
, n
);
271 int m
= (g_date_get_year (d
) - 1) * 12 +
272 (g_date_get_month (d
) - 1);
277 g_date_subtract_months (d
, -n
);
286 /* Like g_date_add_years, but...
288 * 1. Do not spew criticals.
289 * 2. Number of years is signed.
292 gnm_date_add_years (GDate
*d
, int n
)
294 if (!g_date_valid (d
))
298 int m
= 65535 - g_date_get_year (d
);
303 g_date_add_years (d
, n
);
305 int m
= g_date_get_year (d
) - 1;
310 g_date_subtract_years (d
, -n
);
319 #define DAY_SECONDS (3600*24)
321 datetime_value_to_seconds (GnmValue
const *v
, GODateConventions
const *conv
)
324 gnm_float d
= datetime_value_to_serial_raw (v
, conv
);
325 if (d
>= G_MAXINT
|| d
< G_MININT
)
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. */
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
)