2 * fn-date.c: Built in date functions.
5 * Miguel de Icaza (miguel@gnu.org)
6 * Jukka-Pekka Iivonen (iivonen@iki.fi)
7 * Morten Welinder <terra@gnome.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
22 #include <gnumeric-config.h>
26 #include <parse-util.h>
28 #include <gnm-datetime.h>
31 #include <gnm-format.h>
41 #include <goffice/goffice.h>
42 #include <gnm-plugin.h>
44 GNM_PLUGIN_MODULE_HEADER
;
46 #define DAY_SECONDS (3600*24)
47 #define DATE_CONV(ep) sheet_date_conv ((ep)->sheet)
50 make_date (GnmValue
*res
)
52 value_set_fmt (res
, go_format_default_date ());
57 value_get_basis (const GnmValue
*v
, int defalt
)
60 gnm_float b
= value_get_as_float (v
);
69 /***************************************************************************/
71 static GnmFuncHelp
const help_date
[] = {
72 { GNM_FUNC_HELP_NAME
, F_("DATE:create a date serial value")},
73 { GNM_FUNC_HELP_ARG
, F_("year:year of date")},
74 { GNM_FUNC_HELP_ARG
, F_("month:month of year")},
75 { GNM_FUNC_HELP_ARG
, F_("day:day of month")},
76 { GNM_FUNC_HELP_DESCRIPTION
, F_("The DATE function creates date serial values. 1-Jan-1900 is serial value 1, 2-Jan-1900 is serial value 2, and so on. For compatibility reasons, a serial value is reserved for the non-existing date 29-Feb-1900.") },
77 { GNM_FUNC_HELP_NOTE
, F_("If @{month} or @{day} is less than 1 or too big, then the year and/or month will be adjusted.") },
78 { GNM_FUNC_HELP_NOTE
, F_("For spreadsheets created with the Mac version of Excel, serial 1 is 1-Jan-1904.") },
79 { GNM_FUNC_HELP_EXAMPLES
, "=DATE(2008,1,1)" },
80 { GNM_FUNC_HELP_EXAMPLES
, "=DATE(2008,13,1)" },
81 { GNM_FUNC_HELP_EXAMPLES
, "=DATE(2008,1,-10)" },
82 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
83 { GNM_FUNC_HELP_SEEALSO
, "TODAY,YEAR,MONTH,DAY"},
88 gnumeric_date (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
90 gnm_float year
= value_get_as_float (argv
[0]);
91 gnm_float month
= value_get_as_float (argv
[1]);
92 gnm_float day
= value_get_as_float (argv
[2]);
94 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
96 if (year
< 0 || year
>= 10000)
98 if (!gnm_datetime_allow_negative () && year
< 1900)
99 year
+= 1900; /* Excel compatibility. */
100 else if (year
< 1000)
101 year
+= 1900; /* Somewhat more sane. */
103 /* This uses floor and not trunc on purpose. */
104 month
= gnm_floor (month
);
105 if (gnm_abs (month
) > 120000) /* Actual number not critical. */
108 /* This uses floor and not trunc on purpose. */
109 day
= gnm_floor (day
);
110 if (day
< -32768 || day
>= 32768)
111 day
= 32767; /* Absurd, but yes. */
113 g_date_clear (&date
, 1);
115 g_date_set_dmy (&date
, 1, 1, (int)year
);
116 gnm_date_add_months (&date
, (int)month
- 1);
117 gnm_date_add_days (&date
, (int)day
- 1);
119 if (!g_date_valid (&date
) ||
120 g_date_get_year (&date
) < (gnm_datetime_allow_negative ()
122 : go_date_convention_base (conv
)) ||
123 g_date_get_year (&date
) >= 11900)
126 return make_date (value_new_int (go_date_g_to_serial (&date
, conv
)));
129 return value_new_error_NUM (ei
->pos
);
132 /***************************************************************************/
134 static GnmFuncHelp
const help_unix2date
[] = {
135 { GNM_FUNC_HELP_NAME
, F_("UNIX2DATE:date value corresponding to the Unix timestamp @{t}")},
136 { GNM_FUNC_HELP_ARG
, F_("t:Unix time stamp")},
137 { GNM_FUNC_HELP_DESCRIPTION
, F_("The UNIX2DATE function translates Unix timestamps into the corresponding date. A Unix timestamp is the number of seconds since midnight (0:00) of January 1st, 1970 GMT.") },
138 { GNM_FUNC_HELP_EXAMPLES
, "=UNIX2DATE(1000000000)" },
139 { GNM_FUNC_HELP_SEEALSO
, "DATE2UNIX,DATE"},
140 { GNM_FUNC_HELP_END
}
144 gnumeric_unix2date (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
146 gnm_float futime
= value_get_as_float (argv
[0]);
147 time_t utime
= (time_t)futime
;
150 /* Check for overflow. */
151 if (gnm_abs (futime
- utime
) >= 1.0)
152 return value_new_error_VALUE (ei
->pos
);
154 serial
= go_date_timet_to_serial_raw (utime
, DATE_CONV (ei
->pos
));
155 if (serial
== G_MAXINT
)
156 return value_new_error_VALUE (ei
->pos
);
158 return make_date (value_new_float (serial
+
159 (futime
- utime
) / DAY_SECONDS
));
162 /***************************************************************************/
164 static GnmFuncHelp
const help_date2unix
[] = {
165 { GNM_FUNC_HELP_NAME
, F_("DATE2UNIX:the Unix timestamp corresponding to a date @{d}") },
166 { GNM_FUNC_HELP_ARG
, F_("d:date")},
167 { GNM_FUNC_HELP_DESCRIPTION
, F_("The DATE2UNIX function translates a date into a Unix timestamp. A Unix timestamp is the number of seconds since midnight (0:00) of January 1st, 1970 GMT.") },
168 { GNM_FUNC_HELP_EXAMPLES
, "=DATE2UNIX(DATE(2000,1,1))" },
169 { GNM_FUNC_HELP_SEEALSO
, "UNIX2DATE,DATE"},
170 { GNM_FUNC_HELP_END
}
174 gnumeric_date2unix (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
176 gnm_float fserial
= value_get_as_float (argv
[0]);
177 int serial
= (int)fserial
;
178 time_t utime
= go_date_serial_to_timet (serial
, DATE_CONV (ei
->pos
));
180 /* Check for overflow. */
181 if (gnm_abs (fserial
- serial
) >= 1.0 || utime
== (time_t)-1)
182 return value_new_error_VALUE (ei
->pos
);
184 return value_new_int (utime
+
185 gnm_fake_round (DAY_SECONDS
* (fserial
- serial
)));
188 /***************************************************************************/
190 static GnmFuncHelp
const help_datevalue
[] = {
191 { GNM_FUNC_HELP_NAME
, F_("DATEVALUE:the date part of a date and time serial value")},
192 { GNM_FUNC_HELP_ARG
, F_("serial:date and time serial value")},
193 { GNM_FUNC_HELP_DESCRIPTION
, F_("DATEVALUE returns the date serial value part of a date and time serial value.") },
194 { GNM_FUNC_HELP_EXAMPLES
, "=DATEVALUE(NOW())" },
195 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
196 { GNM_FUNC_HELP_SEEALSO
, "TIMEVALUE,DATE"},
197 { GNM_FUNC_HELP_END
}
201 gnumeric_datevalue (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
203 return value_new_int (datetime_value_to_serial (argv
[0], DATE_CONV (ei
->pos
)));
206 /***************************************************************************/
208 static GnmFuncHelp
const help_datedif
[] = {
209 { GNM_FUNC_HELP_NAME
, F_("DATEDIF:difference between dates") },
210 { GNM_FUNC_HELP_ARG
, F_("start_date:starting date serial value")},
211 { GNM_FUNC_HELP_ARG
, F_("end_date:ending date serial value")},
212 { GNM_FUNC_HELP_ARG
, F_("interval:counting unit")},
213 { GNM_FUNC_HELP_DESCRIPTION
, F_("DATEDIF returns the distance from @{start_date} to @{end_date} according to the unit specified by @{interval}.") },
214 { GNM_FUNC_HELP_NOTE
, F_("If @{interval} is \"y\", \"m\", or \"d\" then the distance is measured in complete years, months, or days respectively.") },
215 { GNM_FUNC_HELP_NOTE
, F_("If @{interval} is \"ym\" or \"yd\" then the distance is measured in complete months or days, respectively, but excluding any difference in years.") },
216 { GNM_FUNC_HELP_NOTE
, F_("If @{interval} is \"md\" then the distance is measured in complete days but excluding any difference in months.") },
217 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
218 { GNM_FUNC_HELP_EXAMPLES
, "=DATEDIF(DATE(2003,2,3),DATE(2007,4,2),\"m\")" },
219 { GNM_FUNC_HELP_EXAMPLES
, "=DATEDIF(DATE(2000,4,30),DATE(2003,8,4),\"d\")" },
220 { GNM_FUNC_HELP_EXAMPLES
, "=DATEDIF(DATE(2000,4,30),DATE(2003,8,4),\"y\")" },
221 { GNM_FUNC_HELP_SEEALSO
, "DAYS360"},
222 { GNM_FUNC_HELP_END
}
226 datedif_opt_ym (GDate
*gdate1
, GDate
*gdate2
)
228 g_assert (g_date_valid (gdate1
));
229 g_assert (g_date_valid (gdate2
));
231 return go_date_g_months_between (gdate1
, gdate2
) % 12;
235 datedif_opt_yd (GDate
*gdate1
, GDate
*gdate2
, int excel_compat
)
237 g_assert (g_date_valid (gdate1
));
238 g_assert (g_date_valid (gdate2
));
240 g_date_get_day (gdate1
);
242 gnm_date_add_years (gdate1
,
243 go_date_g_years_between (gdate1
, gdate2
));
244 /* according to glib.h, feb 29 turns to feb 28 if necessary */
247 int new_year1
, new_year2
;
249 /* treat all years divisible by four as leap years: */
250 /* this is clearly wrong, but it's what Excel does. */
251 /* (I use 2004 here since it is clearly a leap year.) */
252 new_year1
= 2004 + (g_date_get_year (gdate1
) & 0x3);
253 new_year2
= new_year1
+ (g_date_get_year (gdate2
) -
254 g_date_get_year (gdate1
));
255 g_date_set_year (gdate1
, new_year1
);
256 g_date_set_year (gdate2
, new_year2
);
259 static gboolean need_warning
= TRUE
;
261 g_warning("datedif is known to differ from "
262 "Excel for some values.");
263 need_warning
= FALSE
;
268 return g_date_days_between (gdate1
, gdate2
);
272 datedif_opt_md (GDate
*gdate1
, GDate
*gdate2
, gboolean excel_compat
)
276 g_assert (g_date_valid (gdate1
));
277 g_assert (g_date_valid (gdate2
));
279 day
= g_date_get_day (gdate1
);
281 gnm_date_add_months (gdate1
,
282 go_date_g_months_between (gdate1
, gdate2
));
283 /* according to glib.h, days>28 decrease if necessary */
286 int new_year1
, new_year2
;
288 /* treat all years divisible by four as leap years: */
289 /* this is clearly wrong, but it's what Excel does. */
290 /* (I use 2004 here since it is clearly a leap year.) */
291 new_year1
= 2004 + (g_date_get_year (gdate1
) & 0x3);
292 new_year2
= new_year1
+ (g_date_get_year (gdate2
) -
293 g_date_get_year (gdate1
));
294 g_date_set_year (gdate1
, new_year1
);
295 g_date_set_year (gdate2
, new_year2
);
297 /* add back the days if they were decreased by
298 gnm_date_add_months */
299 /* ( i feel this is inferior because it reports e.g.:
300 datedif(1/31/95,3/1/95,"d") == -2 ) */
301 gnm_date_add_days (gdate1
,
302 day
- g_date_get_day (gdate1
));
305 return g_date_days_between (gdate1
, gdate2
);
309 gnumeric_datedif (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
314 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
316 date1
= gnm_floor (value_get_as_float (argv
[0]));
317 date2
= gnm_floor (value_get_as_float (argv
[1]));
318 opt
= value_peek_string (argv
[2]);
321 return value_new_error_NUM (ei
->pos
);
323 go_date_serial_to_g (&d1
, date1
, conv
);
324 go_date_serial_to_g (&d2
, date2
, conv
);
325 if (!g_date_valid (&d1
) || !g_date_valid (&d2
))
326 return value_new_error_VALUE (ei
->pos
);
328 if (!strcmp (opt
, "d"))
329 return value_new_int (g_date_get_julian (&d2
) -
330 g_date_get_julian (&d1
));
331 else if (!strcmp (opt
, "m"))
332 return value_new_int (go_date_g_months_between (&d1
, &d2
));
333 else if (!strcmp (opt
, "y"))
334 return value_new_int (go_date_g_years_between (&d1
, &d2
));
335 else if (!strcmp (opt
, "ym"))
336 return value_new_int (datedif_opt_ym (&d1
, &d2
));
337 else if (!strcmp (opt
, "yd"))
338 return value_new_int (datedif_opt_yd (&d1
, &d2
, TRUE
));
339 else if (!strcmp (opt
, "md"))
340 return value_new_int (datedif_opt_md (&d1
, &d2
, TRUE
));
342 return value_new_error_VALUE (ei
->pos
);
345 /***************************************************************************/
347 static GnmFuncHelp
const help_edate
[] = {
348 { GNM_FUNC_HELP_NAME
, F_("EDATE:adjust a date by a number of months") },
349 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
350 { GNM_FUNC_HELP_ARG
, F_("months:signed number of months")},
351 { GNM_FUNC_HELP_DESCRIPTION
, F_("EDATE returns @{date} moved forward or backward the number of months specified by @{months}.") },
352 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
353 { GNM_FUNC_HELP_EXAMPLES
, "=EDATE(DATE(2001,12,30),2)" },
354 { GNM_FUNC_HELP_SEEALSO
, "DATE"},
355 { GNM_FUNC_HELP_END
}
359 gnumeric_edate (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
361 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
362 gnm_float serial
= value_get_as_float (argv
[0]);
363 gnm_float months
= value_get_as_float (argv
[1]);
366 if (serial
< 0 || serial
> INT_MAX
)
367 return value_new_error_NUM (ei
->pos
);
368 if (months
> INT_MAX
/ 2 || -months
> INT_MAX
/ 2)
369 return value_new_error_NUM (ei
->pos
);
371 go_date_serial_to_g (&date
, (int)serial
, conv
);
372 gnm_date_add_months (&date
, (int)months
);
374 if (!g_date_valid (&date
) ||
375 g_date_get_year (&date
) < 1900 ||
376 g_date_get_year (&date
) > 9999)
377 return value_new_error_NUM (ei
->pos
);
379 return make_date (value_new_int (go_date_g_to_serial (&date
, conv
)));
382 /***************************************************************************/
384 static GnmFuncHelp
const help_today
[] = {
385 { GNM_FUNC_HELP_NAME
, F_("TODAY:the date serial value of today") },
386 { GNM_FUNC_HELP_DESCRIPTION
, F_("The TODAY function returns the date serial value of the day it is computed. Recomputing on a later date will produce a different value.") },
387 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
388 { GNM_FUNC_HELP_EXAMPLES
, "=TODAY()" },
389 { GNM_FUNC_HELP_SEEALSO
, "DATE"},
390 { GNM_FUNC_HELP_END
}
394 gnumeric_today (GnmFuncEvalInfo
*ei
, G_GNUC_UNUSED GnmValue
const * const *argv
)
396 return make_date (value_new_int (go_date_timet_to_serial (time (NULL
), DATE_CONV (ei
->pos
))));
399 /***************************************************************************/
401 static GnmFuncHelp
const help_now
[] = {
402 { GNM_FUNC_HELP_NAME
, F_("NOW:the date and time serial value of the current time") },
403 { GNM_FUNC_HELP_DESCRIPTION
, F_("The NOW function returns the date and time serial value of the moment it is computed. Recomputing later will produce a different value.") },
404 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
405 { GNM_FUNC_HELP_EXAMPLES
, "=NOW()" },
406 { GNM_FUNC_HELP_SEEALSO
, "DATE"},
407 { GNM_FUNC_HELP_END
}
411 gnumeric_now (GnmFuncEvalInfo
*ei
, G_GNUC_UNUSED GnmValue
const * const *argv
)
413 return value_new_float (go_date_timet_to_serial_raw (time (NULL
), DATE_CONV (ei
->pos
)));
416 /***************************************************************************/
418 static GnmFuncHelp
const help_time
[] = {
419 { GNM_FUNC_HELP_NAME
, F_("TIME:create a time serial value")},
420 { GNM_FUNC_HELP_ARG
, F_("hour:hour of the day")},
421 { GNM_FUNC_HELP_ARG
, F_("minute:minute within the hour")},
422 { GNM_FUNC_HELP_ARG
, F_("second:second within the minute")},
423 { GNM_FUNC_HELP_DESCRIPTION
, F_("The TIME function computes the fractional day after midnight at the time given by @{hour}, @{minute}, and @{second}.") },
424 { GNM_FUNC_HELP_NOTE
, F_("While the return value is automatically formatted to look like a time between 0:00 and 24:00, "
425 "the underlying serial time value is a number between 0 and 1.")},
426 { GNM_FUNC_HELP_NOTE
, F_("If any of @{hour}, @{minute}, and @{second} is negative, #NUM! is returned")},
427 { GNM_FUNC_HELP_EXAMPLES
, "=TIME(12,30,2)" },
428 { GNM_FUNC_HELP_EXAMPLES
, "=TIME(25,100,18)" },
429 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
430 { GNM_FUNC_HELP_SEEALSO
, "ODF.TIME,HOUR,MINUTE,SECOND"},
431 { GNM_FUNC_HELP_END
}
435 gnumeric_time (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
437 gnm_float hours
, minutes
, seconds
;
440 hours
= gnm_fmod (value_get_as_float (argv
[0]), 24);
441 minutes
= value_get_as_float (argv
[1]);
442 seconds
= value_get_as_float (argv
[2]);
444 if (hours
< 0 || minutes
< 0 || seconds
< 0)
445 return value_new_error_NUM (ei
->pos
);
447 time
= (hours
* 3600 + minutes
* 60 + seconds
) / DAY_SECONDS
;
448 time
-= gnm_fake_floor (time
);
450 return value_new_float (time
);
453 /***************************************************************************/
455 static GnmFuncHelp
const help_odf_time
[] = {
456 { GNM_FUNC_HELP_NAME
, F_("ODF.TIME:create a time serial value")},
457 { GNM_FUNC_HELP_ARG
, F_("hour:hour")},
458 { GNM_FUNC_HELP_ARG
, F_("minute:minute")},
459 { GNM_FUNC_HELP_ARG
, F_("second:second")},
460 { GNM_FUNC_HELP_DESCRIPTION
, F_("The ODF.TIME function computes the time given by @{hour}, @{minute}, and @{second} as a fraction of a day.") },
461 { GNM_FUNC_HELP_NOTE
, F_("While the return value is automatically formatted to look like a time between 0:00 and 24:00, "
462 "the underlying serial time value can be any number.")},
463 { GNM_FUNC_HELP_EXAMPLES
, "=ODF.TIME(12,30,2)" },
464 { GNM_FUNC_HELP_EXAMPLES
, "=ODF.TIME(25,100,-18)" },
465 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.") },
466 { GNM_FUNC_HELP_SEEALSO
, "TIME,HOUR,MINUTE,SECOND"},
467 { GNM_FUNC_HELP_END
}
471 gnumeric_odf_time (G_GNUC_UNUSED GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
473 gnm_float hours
, minutes
, seconds
;
475 hours
= value_get_as_float (argv
[0]);
476 minutes
= value_get_as_float (argv
[1]);
477 seconds
= value_get_as_float (argv
[2]);
479 return make_date (value_new_float ((hours
* 3600 + minutes
* 60 + seconds
) /
483 /***************************************************************************/
485 static GnmFuncHelp
const help_timevalue
[] = {
486 { GNM_FUNC_HELP_NAME
, F_("TIMEVALUE:the time part of a date and time serial value")},
487 { GNM_FUNC_HELP_ARG
, F_("serial:date and time serial value")},
488 { GNM_FUNC_HELP_DESCRIPTION
, F_("TIMEVALUE returns the time-of-day part of a date and time serial value.") },
489 { GNM_FUNC_HELP_EXAMPLES
, "=TIMEVALUE(NOW())" },
490 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
491 { GNM_FUNC_HELP_SEEALSO
, "DATEVALUE,TIME"},
492 { GNM_FUNC_HELP_END
}
496 gnumeric_timevalue (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
498 gnm_float raw
= datetime_value_to_serial_raw (argv
[0], DATE_CONV (ei
->pos
));
499 return value_new_float (raw
- (int)raw
);
502 /***************************************************************************/
504 static GnmFuncHelp
const help_hour
[] = {
505 { GNM_FUNC_HELP_NAME
, F_("HOUR:compute hour part of fractional day")},
506 { GNM_FUNC_HELP_ARG
, F_("time:time of day as fractional day")},
507 { GNM_FUNC_HELP_DESCRIPTION
, F_("The HOUR function computes the hour part of the fractional day given by @{time}.") },
508 { GNM_FUNC_HELP_EXAMPLES
, "=HOUR(TIME(12,30,2))" },
509 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
510 { GNM_FUNC_HELP_SEEALSO
, "TIME,MINUTE,SECOND"},
511 { GNM_FUNC_HELP_END
}
515 gnumeric_hour (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
517 int secs
= datetime_value_to_seconds (argv
[0], DATE_CONV (ei
->pos
));
520 return value_new_error_NUM (ei
->pos
);
522 return value_new_int (secs
/ 3600);
525 /***************************************************************************/
527 static GnmFuncHelp
const help_minute
[] = {
528 { GNM_FUNC_HELP_NAME
, F_("MINUTE:compute minute part of fractional day")},
529 { GNM_FUNC_HELP_ARG
, F_("time:time of day as fractional day")},
530 { GNM_FUNC_HELP_DESCRIPTION
, F_("The MINUTE function computes the minute part of the fractional day given by @{time}.") },
531 { GNM_FUNC_HELP_EXAMPLES
, "=MINUTE(TIME(12,30,2))" },
532 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
533 { GNM_FUNC_HELP_SEEALSO
, "TIME,HOUR,SECOND"},
534 { GNM_FUNC_HELP_END
}
538 gnumeric_minute (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
540 int secs
= datetime_value_to_seconds (argv
[0], DATE_CONV (ei
->pos
));
543 return value_new_error_NUM (ei
->pos
);
545 return value_new_int (secs
/ 60 % 60);
548 /***************************************************************************/
550 static GnmFuncHelp
const help_second
[] = {
551 { GNM_FUNC_HELP_NAME
, F_("SECOND:compute seconds part of fractional day")},
552 { GNM_FUNC_HELP_ARG
, F_("time:time of day as fractional day")},
553 { GNM_FUNC_HELP_DESCRIPTION
, F_("The SECOND function computes the seconds part of the fractional day given by @{time}.") },
554 { GNM_FUNC_HELP_EXAMPLES
, "=SECOND(TIME(12,30,2))" },
555 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
556 { GNM_FUNC_HELP_SEEALSO
, "TIME,HOUR,MINUTE"},
557 { GNM_FUNC_HELP_END
}
561 gnumeric_second (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
563 int secs
= datetime_value_to_seconds (argv
[0], DATE_CONV (ei
->pos
));
566 return value_new_error_NUM (ei
->pos
);
568 return value_new_int (secs
% 60);
571 /***************************************************************************/
573 static GnmFuncHelp
const help_year
[] = {
574 { GNM_FUNC_HELP_NAME
, F_("YEAR:the year part of a date serial value") },
575 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
576 { GNM_FUNC_HELP_DESCRIPTION
, F_("The YEAR function returns the year part of @{date}.") },
577 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
578 { GNM_FUNC_HELP_EXAMPLES
, "=YEAR(TODAY())" },
579 { GNM_FUNC_HELP_EXAMPLES
, "=YEAR(DATE(1940,4,9))" },
580 { GNM_FUNC_HELP_SEEALSO
, "DATE,MONTH,DAY"},
581 { GNM_FUNC_HELP_END
}
585 gnumeric_year (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
589 if (datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
)))
590 return value_new_int (g_date_get_year (&date
));
592 return value_new_error_NUM (ei
->pos
);
595 /***************************************************************************/
597 static GnmFuncHelp
const help_month
[] = {
598 { GNM_FUNC_HELP_NAME
, F_("MONTH:the month part of a date serial value") },
599 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
600 { GNM_FUNC_HELP_DESCRIPTION
, F_("The MONTH function returns the month part of @{date}.") },
601 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
602 { GNM_FUNC_HELP_EXAMPLES
, "=MONTH(TODAY())" },
603 { GNM_FUNC_HELP_EXAMPLES
, "=MONTH(DATE(1940,4,9))" },
604 { GNM_FUNC_HELP_SEEALSO
, "DATE,YEAR,DAY"},
605 { GNM_FUNC_HELP_END
}
609 gnumeric_month (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
613 if (datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
)))
614 return value_new_int (g_date_get_month (&date
));
616 return value_new_error_NUM (ei
->pos
);
619 /***************************************************************************/
621 static GnmFuncHelp
const help_day
[] = {
622 { GNM_FUNC_HELP_NAME
, F_("DAY:the day-of-month part of a date serial value") },
623 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
624 { GNM_FUNC_HELP_DESCRIPTION
, F_("The DAY function returns the day-of-month part of @{date}.") },
625 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
626 { GNM_FUNC_HELP_EXAMPLES
, "=DAY(TODAY())" },
627 { GNM_FUNC_HELP_EXAMPLES
, "=DAY(DATE(1940,4,9))" },
628 { GNM_FUNC_HELP_SEEALSO
, "DATE,YEAR,MONTH"},
629 { GNM_FUNC_HELP_END
}
633 gnumeric_day (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
637 if (datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
)))
638 return value_new_int (g_date_get_day (&date
));
640 return value_new_error_NUM (ei
->pos
);
643 /***************************************************************************/
645 static GnmFuncHelp
const help_weekday
[] = {
646 { GNM_FUNC_HELP_NAME
, F_("WEEKDAY:day-of-week") },
647 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
648 { GNM_FUNC_HELP_ARG
, F_("method:numbering system, defaults to 1")},
649 { GNM_FUNC_HELP_DESCRIPTION
, F_("The WEEKDAY function returns the day-of-week of @{date}. The value of @{method} determines how days are numbered; it defaults to 1.") },
650 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 1, then Sunday is 1, Monday is 2, etc.") },
651 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 2, then Monday is 1, Tuesday is 2, etc.") },
652 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 3, then Monday is 0, Tuesday is 1, etc.") },
653 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 11, then Monday is 1, Tuesday is 2, etc.") },
654 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 12, then Tuesday is 1, Wednesday is 2, etc.") },
655 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 13, then Wednesday is 1, Thursday is 2, etc.") },
656 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 14, then Thursday is 1, Friday is 2, etc.") },
657 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 15, then Friday is 1, Saturday is 2, etc.") },
658 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 16, then Saturday is 1, Sunday is 2, etc.") },
659 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 17, then Sunday is 1, Monday is 2, etc.") },
660 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
661 { GNM_FUNC_HELP_EXAMPLES
, "=WEEKDAY(DATE(1940,4,9))" },
662 { GNM_FUNC_HELP_SEEALSO
, "DATE,ISOWEEKNUM"},
663 { GNM_FUNC_HELP_END
}
667 gnumeric_weekday (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
671 gnm_float method
= argv
[1] ? value_get_as_float (argv
[1]) : 1;
673 if (method
< 1 || method
>= G_MAXINT
)
674 return value_new_error_NUM (ei
->pos
);
676 if (!datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
)))
677 return value_new_error_NUM (ei
->pos
);
679 switch ((int)method
) {
682 res
= (g_date_get_weekday (&date
) % 7) + 1;
686 res
= (g_date_get_weekday (&date
) + 6) % 7 + 1;
689 res
= (g_date_get_weekday (&date
) + 6) % 7;
692 res
= (g_date_get_weekday (&date
) + 5) % 7 + 1;
695 res
= (g_date_get_weekday (&date
) + 4) % 7 + 1;
698 res
= (g_date_get_weekday (&date
) + 3) % 7 + 1;
701 res
= (g_date_get_weekday (&date
) + 2) % 7 + 1;
704 res
= (g_date_get_weekday (&date
) + 1) % 7 + 1;
707 return value_new_error_NUM (ei
->pos
);
710 return value_new_int (res
);
713 /***************************************************************************/
715 static GnmFuncHelp
const help_days360
[] = {
716 { GNM_FUNC_HELP_NAME
, F_("DAYS360:days between dates") },
717 { GNM_FUNC_HELP_ARG
, F_("start_date:starting date serial value")},
718 { GNM_FUNC_HELP_ARG
, F_("end_date:ending date serial value")},
719 { GNM_FUNC_HELP_ARG
, F_("method:counting method")},
720 { GNM_FUNC_HELP_DESCRIPTION
, F_("DAYS360 returns the number of days from @{start_date} to @{end_date}.") },
721 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 0, the default, the MS Excel (tm) US method will be used. This is a somewhat complicated industry standard method where the last day of February is considered to be the 30th day of the month, but only for @{start_date}.") },
722 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 1, the European method will be used. In this case, if the day of the month is 31 it will be considered as 30") },
723 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 2, a saner version of the US method is used in which both dates get the same February treatment.") },
724 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
725 { GNM_FUNC_HELP_EXAMPLES
, "=DAYS360(DATE(2003,2,3),DATE(2007,4,2))" },
726 { GNM_FUNC_HELP_SEEALSO
, "DATEDIF"},
727 { GNM_FUNC_HELP_END
}
731 gnumeric_days360 (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
735 GODateConventions
const *date_conv
= DATE_CONV (ei
->pos
);
736 gnm_float serial1
= datetime_value_to_serial (argv
[0], date_conv
);
737 gnm_float serial2
= datetime_value_to_serial (argv
[1], date_conv
);
738 gnm_float method
= argv
[2] ? gnm_floor (value_get_as_float (argv
[2])) : 0;
740 switch ((int)method
) {
741 case 0: basis
= GO_BASIS_MSRB_30_360
; break;
743 case 1: basis
= GO_BASIS_30E_360
; break;
744 case 2: basis
= GO_BASIS_MSRB_30_360_SYM
; break;
747 go_date_serial_to_g (&date1
, serial1
, date_conv
);
748 go_date_serial_to_g (&date2
, serial2
, date_conv
);
749 if (!g_date_valid (&date1
) || !g_date_valid (&date2
))
750 return value_new_error_VALUE (ei
->pos
);
752 return value_new_int (go_date_days_between_basis (&date1
, &date2
, basis
));
755 /***************************************************************************/
757 static GnmFuncHelp
const help_eomonth
[] = {
758 { GNM_FUNC_HELP_NAME
, F_("EOMONTH:end of month") },
759 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
760 { GNM_FUNC_HELP_ARG
, F_("months:signed number of months")},
761 { GNM_FUNC_HELP_DESCRIPTION
, F_("EOMONTH returns the date serial value of the end of the month specified by @{date} adjusted forward or backward the number of months specified by @{months}.") },
762 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible.") },
763 { GNM_FUNC_HELP_EXAMPLES
, "=EOMONTH(DATE(2001,12,14),2)" },
764 { GNM_FUNC_HELP_SEEALSO
, "EDATE"},
765 { GNM_FUNC_HELP_END
}
769 gnumeric_eomonth (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
771 gnm_float months
= argv
[1] ? value_get_as_float (argv
[1]) : 0;
773 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
775 datetime_value_to_g (&date
, argv
[0], conv
);
776 if (!g_date_valid (&date
))
777 return value_new_error_VALUE (ei
->pos
);
779 if (months
> INT_MAX
/ 2 || -months
> INT_MAX
/ 2)
780 return value_new_error_NUM (ei
->pos
);
782 gnm_date_add_months (&date
, (int)months
);
783 if (!g_date_valid (&date
) ||
784 g_date_get_year (&date
) < 1900 ||
785 g_date_get_year (&date
) > 9999)
786 return value_new_error_NUM (ei
->pos
);
788 g_date_set_day (&date
,
789 g_date_get_days_in_month (g_date_get_month (&date
),
790 g_date_get_year (&date
)));
792 return make_date (value_new_int (go_date_g_to_serial (&date
, conv
)));
795 /***************************************************************************/
797 static GnmFuncHelp
const help_workday
[] = {
798 { GNM_FUNC_HELP_NAME
, F_("WORKDAY:add working days") },
799 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
800 { GNM_FUNC_HELP_ARG
, F_("days:number of days to add")},
801 { GNM_FUNC_HELP_ARG
, F_("holidays:array of holidays")},
802 { GNM_FUNC_HELP_ARG
, F_("weekend:array of 0s and 1s, indicating whether a weekday "
803 "(S, M, T, W, T, F, S) is on the weekend, defaults to {1,0,0,0,0,0,1}")},
804 { GNM_FUNC_HELP_DESCRIPTION
, F_("WORKDAY adjusts @{date} by @{days} skipping over weekends and @{holidays} in the process.") },
805 { GNM_FUNC_HELP_NOTE
, F_("@{days} may be negative.") },
806 { GNM_FUNC_HELP_NOTE
, F_("If an entry of @{weekend} is non-zero, the corresponding weekday is not a work day.")},
807 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible if the last argument is omitted.") },
808 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.") },
809 { GNM_FUNC_HELP_EXAMPLES
, "=WORKDAY(DATE(2001,12,14),2)" },
810 { GNM_FUNC_HELP_EXAMPLES
, "=WORKDAY(DATE(2001,12,14),2,,{0,0,0,0,0,1,1})" },
811 { GNM_FUNC_HELP_SEEALSO
, "NETWORKDAYS"},
812 { GNM_FUNC_HELP_END
}
816 float_compare (gnm_float
const *a
, gnm_float
const *b
)
827 gnumeric_workday (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
830 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
831 gnm_float days
= value_get_as_float (argv
[1]);
833 gnm_float
*holidays
= NULL
;
834 gnm_float
*weekends
= NULL
;
835 gnm_float
const default_weekends
[] = {1.,0.,0.,0.,0.,0.,1.};
836 int nholidays
, nweekends
, n_non_weekend
= 0;
837 GDateWeekday weekday
;
841 datetime_value_to_g (&date
, argv
[0], conv
);
842 if (!g_date_valid (&date
))
845 if (days
> INT_MAX
/ 2 || -days
> INT_MAX
/ 2)
846 return value_new_error_NUM (ei
->pos
);
850 GnmValue
*result
= NULL
;
852 weekends
= collect_floats_value (argv
[3], ei
->pos
,
853 COLLECT_COERCE_STRINGS
|
854 COLLECT_ZEROONE_BOOLS
|
855 COLLECT_IGNORE_BLANKS
,
856 &nweekends
, &result
);
863 weekends
= (gnm_float
*)default_weekends
;
867 for (i
= 0; i
< 7; i
++)
868 if (weekends
[i
] == 0)
870 if (n_non_weekend
== 0 && idays
!= 0)
872 if (n_non_weekend
== 0 && idays
== 0) {
873 if (weekends
!= default_weekends
)
875 return make_date (value_new_int (go_date_g_to_serial (&date
, conv
)));
881 GnmValue
*result
= NULL
;
883 holidays
= collect_floats_value (argv
[2], ei
->pos
,
884 COLLECT_COERCE_STRINGS
|
885 COLLECT_IGNORE_BOOLS
|
886 COLLECT_IGNORE_BLANKS
,
887 &nholidays
, &result
);
889 if (weekends
!= default_weekends
)
893 qsort (holidays
, nholidays
, sizeof (holidays
[0]), (void *) &float_compare
);
895 for (i
= j
= 0; i
< nholidays
; i
++) {
896 gnm_float s
= holidays
[i
];
898 if (s
< 0 || s
> INT_MAX
)
901 if (j
> 0 && hserial
== holidays
[j
- 1])
903 go_date_serial_to_g (&hol
, hserial
, conv
);
904 if (!g_date_valid (&hol
))
906 if (weekends
[g_date_get_weekday (&hol
) % 7] != 0.)
908 holidays
[j
++] = hserial
;
917 weekday
= g_date_get_weekday (&date
);
924 weekday
= weekday
% 7;
925 while (weekends
[weekday
]) {
926 weekday
= (weekday
> 0) ? (weekday
- 1) : G_DATE_SATURDAY
;
929 g_date_subtract_days (&date
, diff
);
930 old_serial
= go_date_g_to_serial (&date
, conv
);
933 int dm_part_week
= idays
% n_non_weekend
;
934 int ds
= idays
/ n_non_weekend
* 7;
936 g_date_add_days (&date
, ds
);
938 while (dm_part_week
) {
939 g_date_add_days (&date
, 1);
940 weekday
= (weekday
+ 1) % 7;
941 if (!weekends
[weekday
])
945 serial
= go_date_g_to_serial (&date
, conv
);
947 * we may have passed holidays.
950 while (h
< nholidays
&& holidays
[h
] <= serial
) {
951 if (holidays
[h
] > old_serial
)
957 } else if (idays
< 0) {
958 int h
= nholidays
- 1;
962 weekday
= weekday
% 7;
963 while (weekends
[weekday
]) {
964 weekday
= (weekday
+ 1) % 7;
967 g_date_add_days (&date
, diff
);
968 old_serial
= go_date_g_to_serial (&date
, conv
);
972 int dm_part_week
= idays
% n_non_weekend
;
973 int ds
= idays
/ n_non_weekend
* 7;
975 g_date_subtract_days (&date
, ds
);
977 while (dm_part_week
) {
978 g_date_subtract_days (&date
, 1);
979 weekday
= (weekday
> 0) ? (weekday
- 1)
981 if (!weekends
[weekday
])
985 serial
= go_date_g_to_serial (&date
, conv
);
987 * we may have passed holidays.
990 while (h
>= 0 && holidays
[h
] >= serial
) {
991 if (holidays
[h
] < old_serial
)
997 } else serial
= go_date_g_to_serial (&date
, conv
);
999 if (weekends
!= default_weekends
)
1003 go_date_serial_to_g (&date
, serial
, conv
);
1004 if (!g_date_valid (&date
) ||
1005 g_date_get_year (&date
) < 1900 ||
1006 g_date_get_year (&date
) > 9999)
1007 return value_new_error_NUM (ei
->pos
);
1009 return make_date (value_new_int (go_date_g_to_serial (&date
, conv
)));
1012 if (weekends
!= default_weekends
)
1015 return value_new_error_VALUE (ei
->pos
);
1018 /**************************************************************************
1022 The optional 4th parameter Workdays can be used to specify a different definition for the standard
1023 work week by passing in a list of numbers which define which days of the week are workdays
1024 (indicated by 0) or not (indicated by non-zero) in order Sunday, Monday,...,Saturday. So, the
1025 default definition of the work week excludes Saturday and Sunday and is: {1;0;0;0;0;0;1}. To
1026 define the work week as excluding Friday and Saturday, the third parameter would be:
1029 In the implementation, we are using g_date_get_weekday which returns
1032 G_DATE_BAD_WEEKDAY = 0,
1035 G_DATE_WEDNESDAY = 3,
1036 G_DATE_THURSDAY = 4,
1038 G_DATE_SATURDAY = 6,
1041 Since Sunday here is 7 rather than a 0 we need to make appropriate adjustments.
1043 ***************************************************************************/
1044 static GnmFuncHelp
const help_networkdays
[] = {
1045 { GNM_FUNC_HELP_NAME
, F_("NETWORKDAYS:number of workdays in range") },
1046 { GNM_FUNC_HELP_ARG
, F_("start_date:starting date serial value")},
1047 { GNM_FUNC_HELP_ARG
, F_("end_date:ending date serial value")},
1048 { GNM_FUNC_HELP_ARG
, F_("holidays:array of holidays")},
1049 { GNM_FUNC_HELP_ARG
, F_("weekend:array of 0s and 1s, indicating whether a weekday "
1050 "(S, M, T, W, T, F, S) is on the weekend, defaults to {1,0,0,0,0,0,1}")},
1051 { GNM_FUNC_HELP_DESCRIPTION
, F_("NETWORKDAYS calculates the number of days from @{start_date} to @{end_date} "
1052 "skipping weekends and @{holidays} in the process.") },
1053 { GNM_FUNC_HELP_NOTE
, F_("If an entry of @{weekend} is non-zero, the corresponding weekday is not a work day.")},
1054 { GNM_FUNC_HELP_EXCEL
, F_("This function is Excel compatible if the last argument is omitted.") },
1055 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.") },
1056 { GNM_FUNC_HELP_EXAMPLES
, "=NETWORKDAYS(DATE(2001,1,2),DATE(2001,2,15))" },
1057 { GNM_FUNC_HELP_EXAMPLES
, "=NETWORKDAYS(DATE(2001,1,2),DATE(2001,2,15),,{0, 0, 0, 1, 1, 0, 0})" },
1058 { GNM_FUNC_HELP_SEEALSO
, "WORKDAY"},
1059 { GNM_FUNC_HELP_END
}
1063 networkdays_calc (GDate start_date
, int start_serial
, int end_serial
,
1064 int n_non_weekend
, gnm_float
*weekends
, int nholidays
, gnm_float
*holidays
)
1067 int old_start_serial
= start_serial
;
1068 GDateWeekday weekday
;
1072 weekday
= g_date_get_weekday (&start_date
);
1073 if (weekday
== G_DATE_BAD_WEEKDAY
)
1075 if (weekday
== G_DATE_SUNDAY
)
1078 weeks
= (end_serial
- start_serial
)/7;
1079 start_serial
= start_serial
+ weeks
* 7;
1080 res
= weeks
* n_non_weekend
;
1082 for (i
= start_serial
; i
<= end_serial
; i
++) {
1083 if (!weekends
[weekday
])
1085 weekday
= (weekday
+ 1) % 7;
1089 * we may have included holidays.
1092 while (h
< nholidays
&& holidays
[h
] <= end_serial
) {
1093 if (holidays
[h
] >= old_start_serial
)
1102 gnumeric_networkdays (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1106 int res
, total_res
= 0;
1107 GDate start_date
, trouble_mar
, trouble_feb
, end_date
;
1108 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
1109 gnm_float
*holidays
= NULL
;
1110 gnm_float
*weekends
= NULL
;
1111 gnm_float
const default_weekends
[] = {1.,0.,0.,0.,0.,0.,1.};
1112 int nholidays
, nweekends
, n_non_weekend
= 0;
1114 GDateWeekday weekday
;
1115 gboolean includes_bad_day
= FALSE
;
1117 /* Check the date period*/
1119 start_serial
= datetime_value_to_serial (argv
[0], conv
);
1120 end_serial
= datetime_value_to_serial (argv
[1], conv
);
1122 /* Swap if necessary */
1123 if (start_serial
> end_serial
) {
1124 int tmp
= start_serial
;
1125 start_serial
= end_serial
;
1129 /* Make sure that start and end serial are valid */
1130 if (start_serial
<= 0 || end_serial
<= 0)
1131 return value_new_error_NUM (ei
->pos
);
1133 go_date_serial_to_g (&start_date
, start_serial
, conv
);
1134 if (!g_date_valid (&start_date
))
1136 go_date_serial_to_g (&end_date
, end_serial
, conv
);
1137 if (!g_date_valid (&end_date
))
1140 g_date_set_dmy (&trouble_mar
, 1, 3, 1900);
1141 if (g_date_compare (&start_date
, &trouble_mar
) < 0) {
1142 g_date_set_dmy (&trouble_feb
, 28, 2, 1900);
1143 includes_bad_day
= (!g_date_valid (&end_date
) || g_date_compare (&trouble_feb
, &end_date
) < 0);
1146 /* get the weekend info */
1149 GnmValue
*result
= NULL
;
1151 weekends
= collect_floats_value (argv
[3], ei
->pos
,
1152 COLLECT_COERCE_STRINGS
|
1153 COLLECT_ZEROONE_BOOLS
|
1154 COLLECT_IGNORE_BLANKS
,
1155 &nweekends
, &result
);
1162 weekends
= (gnm_float
*)default_weekends
;
1166 /* If everything is a weekend we know the answer already */
1168 for (i
= 0; i
< 7; i
++)
1169 if (weekends
[i
] == 0)
1171 if (n_non_weekend
== 0) {
1172 if (weekends
!= default_weekends
)
1174 return value_new_int (0);
1177 /* Now get the holiday info */
1182 GnmValue
*result
= NULL
;
1184 holidays
= collect_floats_value (argv
[2], ei
->pos
,
1185 COLLECT_COERCE_STRINGS
|
1186 COLLECT_IGNORE_BOOLS
|
1187 COLLECT_IGNORE_BLANKS
,
1188 &nholidays
, &result
);
1190 if (weekends
!= default_weekends
)
1194 qsort (holidays
, nholidays
, sizeof (holidays
[0]), (void *) &float_compare
);
1196 for (i
= j
= 0; i
< nholidays
; i
++) {
1197 gnm_float s
= holidays
[i
];
1199 if (s
< 0 || s
> INT_MAX
)
1202 if (j
> 0 && hserial
== holidays
[j
- 1])
1203 continue; /* Dupe */
1204 go_date_serial_to_g (&hol
, hserial
, conv
);
1205 if (!g_date_valid (&hol
))
1207 weekday
= g_date_get_weekday (&hol
);
1208 if (weekday
== G_DATE_BAD_WEEKDAY
)
1210 if (weekday
== G_DATE_SUNDAY
)
1212 /* We skip holidays that are on the weekend */
1213 if (weekends
[weekday
] != 0.)
1215 holidays
[j
++] = hserial
;
1223 if (includes_bad_day
) {
1224 total_res
= networkdays_calc (start_date
, start_serial
,
1225 go_date_g_to_serial (&trouble_feb
, conv
),
1226 n_non_weekend
, weekends
, nholidays
, holidays
);
1229 res
= networkdays_calc (trouble_mar
, go_date_g_to_serial (&trouble_mar
, conv
),
1231 n_non_weekend
, weekends
, nholidays
, holidays
);
1236 total_res
= networkdays_calc (start_date
, start_serial
, end_serial
,
1237 n_non_weekend
, weekends
, nholidays
, holidays
);
1242 if (weekends
!= default_weekends
)
1246 return value_new_int (total_res
);
1249 if (weekends
!= default_weekends
)
1252 return value_new_error_VALUE (ei
->pos
);
1255 /***************************************************************************/
1257 static GnmFuncHelp
const help_isoweeknum
[] = {
1258 { GNM_FUNC_HELP_NAME
, F_("ISOWEEKNUM:ISO week number")},
1259 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
1260 { GNM_FUNC_HELP_DESCRIPTION
, F_("ISOWEEKNUM calculates the week number according to the ISO 8601 standard. Weeks start on Mondays and week 1 contains the first Thursday of the year.") },
1261 { GNM_FUNC_HELP_NOTE
, F_("January 1 of a year is sometimes in week 52 or 53 of the previous year. Similarly, December 31 is sometimes in week 1 of the following year.") },
1262 { GNM_FUNC_HELP_EXAMPLES
, "=ISOWEEKNUM(DATE(2000,1,1))" },
1263 { GNM_FUNC_HELP_EXAMPLES
, "=ISOWEEKNUM(DATE(2008,1,1))" },
1264 { GNM_FUNC_HELP_SEEALSO
, "ISOYEAR,WEEKNUM"},
1265 { GNM_FUNC_HELP_END
}
1269 gnumeric_isoweeknum (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1272 datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
));
1273 if (!g_date_valid (&date
))
1274 return value_new_error_VALUE (ei
->pos
);
1276 return value_new_int (go_date_weeknum (&date
, GO_WEEKNUM_METHOD_ISO
));
1279 /***************************************************************************/
1281 static GnmFuncHelp
const help_isoyear
[] = {
1282 { GNM_FUNC_HELP_NAME
, F_("ISOYEAR:year corresponding to the ISO week number")},
1283 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
1284 { GNM_FUNC_HELP_DESCRIPTION
, F_("ISOYEAR calculates the year to go with week number according to the ISO 8601 standard.") },
1285 { GNM_FUNC_HELP_NOTE
, F_("January 1 of a year is sometimes in week 52 or 53 of the previous year. Similarly, December 31 is sometimes in week 1 of the following year.") },
1286 { GNM_FUNC_HELP_EXAMPLES
, "=ISOYEAR(DATE(2000,1,1))" },
1287 { GNM_FUNC_HELP_EXAMPLES
, "=ISOYEAR(DATE(2008,1,1))" },
1288 { GNM_FUNC_HELP_SEEALSO
, "ISOWEEKNUM,YEAR"},
1289 { GNM_FUNC_HELP_END
}
1293 gnumeric_isoyear (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1300 datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
));
1301 if (!g_date_valid (&date
))
1302 return value_new_error_VALUE (ei
->pos
);
1304 isoweeknum
= go_date_weeknum (&date
, GO_WEEKNUM_METHOD_ISO
);
1305 year
= g_date_get_year (&date
);
1306 month
= g_date_get_month (&date
);
1307 if (isoweeknum
>= 52 && month
== G_DATE_JANUARY
)
1309 else if (isoweeknum
== 1 && month
== G_DATE_DECEMBER
)
1312 return value_new_int (year
);
1315 /***************************************************************************/
1317 static GnmFuncHelp
const help_weeknum
[] = {
1318 { GNM_FUNC_HELP_NAME
, F_("WEEKNUM:week number")},
1319 { GNM_FUNC_HELP_ARG
, F_("date:date serial value")},
1320 { GNM_FUNC_HELP_ARG
, F_("method:numbering system, defaults to 1")},
1321 { GNM_FUNC_HELP_DESCRIPTION
, F_("WEEKNUM calculates the week number according to @{method} which defaults to 1.") },
1322 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 1, then weeks start on Sundays and January 1 is in week 1.") },
1323 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 2, then weeks start on Mondays and January 1 is in week 1.") },
1324 { GNM_FUNC_HELP_NOTE
, F_("If @{method} is 150, then the ISO 8601 numbering is used.") },
1325 { GNM_FUNC_HELP_EXAMPLES
, "=WEEKNUM(DATE(2000,1,1))" },
1326 { GNM_FUNC_HELP_EXAMPLES
, "=WEEKNUM(DATE(2008,1,1))" },
1327 { GNM_FUNC_HELP_SEEALSO
, "ISOWEEKNUM"},
1328 { GNM_FUNC_HELP_END
}
1332 gnumeric_weeknum (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1335 gnm_float method
= argv
[1] ? gnm_floor (value_get_as_float (argv
[1])) : 1;
1339 m
= GO_WEEKNUM_METHOD_SUNDAY
;
1340 else if (method
== 2)
1341 m
= GO_WEEKNUM_METHOD_MONDAY
;
1342 else if (method
== 150 || method
== 21)
1343 m
= GO_WEEKNUM_METHOD_ISO
;
1345 return value_new_error_VALUE (ei
->pos
);
1347 datetime_value_to_g (&date
, argv
[0], DATE_CONV (ei
->pos
));
1348 if (!g_date_valid (&date
))
1349 return value_new_error_VALUE (ei
->pos
);
1351 return value_new_int (go_date_weeknum (&date
, m
));
1354 /***************************************************************************/
1356 static GnmFuncHelp
const help_yearfrac
[] = {
1357 { GNM_FUNC_HELP_NAME
, F_("YEARFRAC:fractional number of years between dates")},
1358 { GNM_FUNC_HELP_ARG
, F_("start_date:starting date serial value")},
1359 { GNM_FUNC_HELP_ARG
, F_("end_date:ending date serial value")},
1360 { GNM_FUNC_HELP_ARG
, F_("basis:calendar basis")},
1361 { GNM_FUNC_HELP_DESCRIPTION
, F_("YEARFRAC calculates the number of days from @{start_date} to @{end_date} according to the calendar specified by @{basis}, which defaults to 0, and expresses the result as a fractional number of years.") },
1363 { GNM_FUNC_HELP_SEEALSO
, "DATE"},
1364 { GNM_FUNC_HELP_EXAMPLES
, "=YEARFRAC(DATE(2000,1,1),DATE(2001,4,1))" },
1365 { GNM_FUNC_HELP_END
}
1369 gnumeric_yearfrac (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1371 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
1372 GDate start_date
, end_date
;
1373 int basis
= value_get_basis (argv
[2], GO_BASIS_MSRB_30_360
);
1375 if (basis
< 0 || basis
> 4 ||
1376 !datetime_value_to_g (&start_date
, argv
[0], conv
) ||
1377 !datetime_value_to_g (&end_date
, argv
[1], conv
))
1378 return value_new_error_NUM (ei
->pos
);
1380 return value_new_float (yearfrac (&start_date
, &end_date
, basis
));
1383 /***************************************************************************/
1385 static GnmFuncHelp
const help_days
[] = {
1386 { GNM_FUNC_HELP_NAME
, F_("DAYS:difference between dates in days") },
1387 { GNM_FUNC_HELP_ARG
, F_("end_date:ending date serial value")},
1388 { GNM_FUNC_HELP_ARG
, F_("start_date:starting date serial value")},
1389 { GNM_FUNC_HELP_DESCRIPTION
, F_("DAYS returns the positive or negative number of days from @{start_date} to @{end_date}.") },
1390 { GNM_FUNC_HELP_ODF
, F_("This function is OpenFormula compatible.") },
1391 { GNM_FUNC_HELP_EXAMPLES
, "=DAYS(DATE(2003,2,3),DATE(2007,4,2))" },
1392 { GNM_FUNC_HELP_EXAMPLES
, "=DAYS(DATE(2007,4,2),DATE(2003,2,3))" },
1393 { GNM_FUNC_HELP_EXAMPLES
, "=DAYS(DATE(1900,2,28),DATE(1900,3,1))" },
1394 { GNM_FUNC_HELP_SEEALSO
, "DATEDIF"},
1395 { GNM_FUNC_HELP_END
}
1400 gnumeric_days (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
1404 GODateConventions
const *conv
= DATE_CONV (ei
->pos
);
1406 date1
= gnm_floor (value_get_as_float (argv
[0]));
1407 date2
= gnm_floor (value_get_as_float (argv
[1]));
1409 go_date_serial_to_g (&d1
, date1
, conv
);
1410 go_date_serial_to_g (&d2
, date2
, conv
);
1412 return value_new_int (g_date_days_between (&d2
, &d1
));
1415 /***************************************************************************/
1417 GnmFuncDescriptor
const datetime_functions
[] = {
1418 { "date", "fff", help_date
,
1419 gnumeric_date
, NULL
,
1420 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_DATE
,
1421 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1422 { "datevalue", "f", help_datevalue
,
1423 gnumeric_datevalue
, NULL
,
1424 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1425 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1426 { "datedif", "ffs", help_datedif
,
1427 gnumeric_datedif
, NULL
,
1428 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1429 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1430 { "day", "f", help_day
,
1432 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1433 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1434 { "days360", "ff|f", help_days360
,
1435 gnumeric_days360
, NULL
,
1436 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1437 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1438 { "edate", "ff", help_edate
,
1439 gnumeric_edate
, NULL
,
1440 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_DATE
,
1441 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1442 { "eomonth", "f|f", help_eomonth
,
1443 gnumeric_eomonth
, NULL
,
1444 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_DATE
,
1445 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1446 { "hour", "f", help_hour
,
1447 gnumeric_hour
, NULL
,
1448 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1449 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1450 { "minute", "f", help_minute
,
1451 gnumeric_minute
, NULL
,
1452 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1453 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1454 { "month", "f", help_month
,
1455 gnumeric_month
, NULL
,
1456 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1457 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1458 { "networkdays", "ff|?A",
1459 help_networkdays
, gnumeric_networkdays
, NULL
,
1460 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1461 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1462 { "now", "", help_now
,
1464 GNM_FUNC_VOLATILE
+ GNM_FUNC_AUTO_TIME
,
1465 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1466 { "odf.time", "fff", help_odf_time
,
1467 gnumeric_odf_time
, NULL
,
1468 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_TIME
,
1469 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1470 { "second", "f", help_second
,
1471 gnumeric_second
, NULL
,
1472 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1473 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1474 { "time", "fff", help_time
,
1475 gnumeric_time
, NULL
,
1476 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_TIME
,
1477 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1478 { "timevalue", "f", help_timevalue
,
1479 gnumeric_timevalue
, NULL
,
1480 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1481 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1482 { "today", "", help_today
,
1483 gnumeric_today
, NULL
,
1484 GNM_FUNC_VOLATILE
+ GNM_FUNC_AUTO_DATE
,
1485 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1486 { "weekday", "f|f", help_weekday
,
1487 gnumeric_weekday
, NULL
,
1488 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1489 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1490 { "weeknum", "f|f", help_weeknum
,
1491 gnumeric_weeknum
, NULL
,
1492 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1493 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1494 { "workday", "ff|?A", help_workday
,
1495 gnumeric_workday
, NULL
,
1496 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_DATE
,
1497 GNM_FUNC_IMPL_STATUS_SUBSET
, GNM_FUNC_TEST_STATUS_BASIC
},
1498 { "year", "f", help_year
,
1499 gnumeric_year
, NULL
,
1500 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1501 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_BASIC
},
1502 { "yearfrac", "ff|f", help_yearfrac
,
1503 gnumeric_yearfrac
, NULL
,
1504 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1505 GNM_FUNC_IMPL_STATUS_COMPLETE
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1507 { "unix2date", "f", help_unix2date
,
1508 gnumeric_unix2date
, NULL
,
1509 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_DATE
,
1510 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1511 { "date2unix", "f", help_date2unix
,
1512 gnumeric_date2unix
, NULL
,
1513 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1514 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1515 { "isoweeknum", "f", help_isoweeknum
,
1516 gnumeric_isoweeknum
, NULL
,
1517 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1518 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1519 { "isoyear", "f", help_isoyear
,
1520 gnumeric_isoyear
, NULL
,
1521 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1522 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},
1524 help_days
, gnumeric_days
, NULL
,
1525 GNM_FUNC_SIMPLE
+ GNM_FUNC_AUTO_UNITLESS
,
1526 GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
, GNM_FUNC_TEST_STATUS_NO_TESTSUITE
},