kspread

kspread_functions_datetime.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1998-2003 The KSpread Team
3 www.koffice.org/kspread
4 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22// built-in date/time functions
23
24#include "functions.h"
25#include "kspread_functions_helper.h"
26#include "valuecalc.h"
27#include "valueconverter.h"
28
29#include <kcalendarsystem.h>
30#include <tdelocale.h>
31
32using namespace KSpread;
33
34// prototypes, sorted
35Value func_currentDate (valVector args, ValueCalc *calc, FuncExtra *);
36Value func_currentDateTime (valVector args, ValueCalc *calc, FuncExtra *);
37Value func_currentTime (valVector args, ValueCalc *calc, FuncExtra *);
38Value func_date (valVector args, ValueCalc *calc, FuncExtra *);
39Value func_datevalue (valVector args, ValueCalc *calc, FuncExtra *);
40Value func_day (valVector args, ValueCalc *calc, FuncExtra *);
41Value func_dayname (valVector args, ValueCalc *calc, FuncExtra *);
42Value func_dayOfYear (valVector args, ValueCalc *calc, FuncExtra *);
43Value func_days (valVector args, ValueCalc *calc, FuncExtra *);
44Value func_days360 (valVector args, ValueCalc *calc, FuncExtra *);
45Value func_daysInMonth (valVector args, ValueCalc *calc, FuncExtra *);
46Value func_daysInYear (valVector args, ValueCalc *calc, FuncExtra *);
47Value func_easterSunday (valVector args, ValueCalc *calc, FuncExtra *);
48Value func_edate (valVector args, ValueCalc *calc, FuncExtra *);
49Value func_eomonth (valVector args, ValueCalc *calc, FuncExtra *);
50Value func_hour (valVector args, ValueCalc *calc, FuncExtra *);
51Value func_hours (valVector args, ValueCalc *calc, FuncExtra *);
52Value func_isLeapYear (valVector args, ValueCalc *calc, FuncExtra *);
53Value func_isoWeekNum (valVector args, ValueCalc *calc, FuncExtra *);
54Value func_minute (valVector args, ValueCalc *calc, FuncExtra *);
55Value func_minutes (valVector args, ValueCalc *calc, FuncExtra *);
56Value func_month (valVector args, ValueCalc *calc, FuncExtra *);
57Value func_monthname (valVector args, ValueCalc *calc, FuncExtra *);
58Value func_months (valVector args, ValueCalc *calc, FuncExtra *);
59Value func_second (valVector args, ValueCalc *calc, FuncExtra *);
60Value func_seconds (valVector args, ValueCalc *calc, FuncExtra *);
61Value func_time (valVector args, ValueCalc *calc, FuncExtra *);
62Value func_timevalue (valVector args, ValueCalc *calc, FuncExtra *);
63Value func_today (valVector args, ValueCalc *calc, FuncExtra *);
64Value func_weekday (valVector args, ValueCalc *calc, FuncExtra *);
65Value func_weeks (valVector args, ValueCalc *calc, FuncExtra *);
66Value func_weeksInYear (valVector args, ValueCalc *calc, FuncExtra *);
67Value func_year (valVector args, ValueCalc *calc, FuncExtra *);
68Value func_years (valVector args, ValueCalc *calc, FuncExtra *);
69
70// registers all date/time functions
71// sadly, many of these functions aren't Excel compatible
72void RegisterDateTimeFunctions()
73{
74 // missing: Excel: WORKDAY, NETWORKDAYS, WEEKNUM, DATEDIF
75 // Gnumeric: UNIX2DATE, DATE2UNIX
76 // TODO: do we really need DATEVALUE and TIMEVALUE ?
77 FunctionRepository* repo = FunctionRepository::self();
78 Function *f;
79
80 f = new Function ("CURRENTDATE", func_currentDate);
81 f->setParamCount (0);
82 repo->add (f);
83 f = new Function ("CURRENTDATETIME", func_currentDateTime);
84 f->setParamCount (0);
85 repo->add (f);
86 f = new Function ("CURRENTTIME", func_currentTime);
87 f->setParamCount (0);
88 repo->add (f);
89 f = new Function ("DATE", func_date);
90 f->setParamCount (3);
91 repo->add (f);
92 f = new Function ("DATEVALUE", func_datevalue);
93 repo->add (f);
94 f = new Function ("DAY", func_day);
95 repo->add (f);
96 f = new Function ("DAYNAME", func_dayname);
97 repo->add (f);
98 f = new Function ("DAYOFYEAR", func_dayOfYear);
99 f->setParamCount (3);
100 repo->add (f);
101 f = new Function ("DAYS", func_days);
102 f->setParamCount (2);
103 repo->add (f);
104 f = new Function ("DAYS360", func_days360);
105 f->setParamCount (2, 3);
106 repo->add (f);
107 f = new Function ("DAYSINMONTH", func_daysInMonth);
108 f->setParamCount (2);
109 repo->add (f);
110 f = new Function ("DAYSINYEAR", func_daysInYear);
111 repo->add (f);
112 f = new Function ("EASTERSUNDAY", func_easterSunday);
113 repo->add (f);
114 f = new Function ("EDATE", func_edate);
115 f->setParamCount (2);
116 repo->add (f);
117 f = new Function ("EOMONTH", func_eomonth);
118 f->setParamCount (2);
119 repo->add (f);
120 f = new Function ("HOUR", func_hour);
121 f->setParamCount (0, 1);
122 repo->add (f);
123 f = new Function ("HOURS", func_hour); // same as HOUR
124 f->setParamCount (0, 1);
125 repo->add (f);
126 f = new Function ("ISLEAPYEAR", func_isLeapYear);
127 repo->add (f);
128 f = new Function ("ISOWEEKNUM", func_isoWeekNum);
129 repo->add (f);
130 f = new Function ("MINUTE", func_minute);
131 f->setParamCount (0, 1);
132 repo->add (f);
133 f = new Function ("MINUTES", func_minute); // same as MINUTE
134 f->setParamCount (0, 1);
135 repo->add (f);
136 f = new Function ("MONTH", func_month);
137 repo->add (f);
138 f = new Function ("MONTHNAME", func_monthname);
139 repo->add (f);
140 f = new Function ("MONTHS", func_months);
141 f->setParamCount (3);
142 repo->add (f);
143 f = new Function ("NOW", func_currentDateTime);
144 f->setParamCount (0);
145 repo->add (f);
146 f = new Function ("SECOND", func_second);
147 f->setParamCount (0, 1);
148 repo->add (f);
149 f = new Function ("SECONDS", func_second); // same as SECOND
150 f->setParamCount (0, 1);
151 repo->add (f);
152 f = new Function ("TIME", func_time);
153 f->setParamCount (3);
154 repo->add (f);
155 f = new Function ("TIMEVALUE", func_timevalue);
156 repo->add (f);
157 f = new Function ("TODAY", func_currentDate);
158 f->setParamCount (0);
159 repo->add (f);
160 f = new Function ("WEEKDAY", func_weekday);
161 f->setParamCount (1, 2);
162 repo->add (f);
163 f = new Function ("WEEKS", func_weeks);
164 f->setParamCount (3);
165 repo->add (f);
166 f = new Function ("WEEKSINYEAR", func_weeksInYear);
167 repo->add (f);
168 f = new Function ("YEAR", func_year);
169 repo->add (f);
170 f = new Function ("YEARS", func_years);
171 f->setParamCount (3);
172 repo->add (f);
173}
174
175// Function: EDATE
176Value func_edate (valVector args, ValueCalc *calc, FuncExtra *)
177{
178 TQDate date = calc->conv()->asDate (args[0]).asDate();
179 int months = calc->conv()->asInteger (args[1]).asInteger();
180
181 date = calc->conv()->locale()->calendar()->addMonths (date, months);
182
183 if (!date.isValid())
184 return Value::errorVALUE();
185
186 return Value (date);
187}
188
189// Function: EOMONTH
190Value func_eomonth (valVector args, ValueCalc *calc, FuncExtra *)
191{
192 // add months to date using EDATE
193 Value modDate = func_edate (args, calc, 0);
194 if (modDate.isError()) return modDate;
195
196 // modDate is currently in Date format
197 TQDate date = modDate.asDate();
198 date.setYMD (date.year(), date.month(), date.daysInMonth());
199
200 return Value (date);
201}
202
203// Function: DAYS360
204// algorithm adapted from gnumeric
205Value func_days360 (valVector args, ValueCalc *calc, FuncExtra *)
206{
207 TQDate date1 = calc->conv()->asDate (args[0]).asDate();
208 TQDate date2 = calc->conv()->asDate (args[1]).asDate();
209 bool european = false;
210 if (args.count() == 3)
211 european = calc->conv()->asBoolean (args[2]).asBoolean();
212
213 int day1, day2;
214 int month1, month2;
215 int year1, year2;
216 bool negative = false;
217
218 if (date1.daysTo( date2 ) < 0)
219 {
220 TQDate tmp( date1 );
221 date1 = date2;
222 date2 = tmp;
223 negative = true;
224 }
225
226 day1 = date1.day();
227 day2 = date2.day();
228 month1 = date1.month();
229 month2 = date2.month();
230 year1 = date1.year();
231 year2 = date2.year();
232
233 if ( european )
234 {
235 if ( day1 == 31 )
236 day1 = 30;
237 if ( day2 == 31 )
238 day2 = 30;
239 }
240 else
241 {
242 // thanks to the Gnumeric developers for this...
243 if ( month1 == 2 && month2 == 2
244 && date1.daysInMonth() == day1
245 && date2.daysInMonth() == day2 )
246 day2 = 30;
247
248 if ( month1 == 2 && date1.daysInMonth() == day1 )
249 day1 = 30;
250
251 if ( day2 == 31 && day1 >= 30 )
252 day2 = 30;
253
254 if ( day1 == 31 )
255 day1 = 30;
256 }
257
258 int result = ( ( year2 - year1 ) * 12 + ( month2 - month1 ) ) * 30
259 + ( day2 - day1 );
260
261 return Value (result);
262}
263
264// Function: YEAR
265Value func_year (valVector args, ValueCalc *calc, FuncExtra *)
266{
267 Value v = calc->conv()->asDate (args[0]);
268 if (v.isError()) return v;
269 TQDate date = v.asDate();
270 return Value (date.year ());
271}
272
273// Function: MONTH
274Value func_month (valVector args, ValueCalc *calc, FuncExtra *)
275{
276 Value v = calc->conv()->asDate (args[0]);
277 if (v.isError()) return v;
278 TQDate date = v.asDate();
279 return Value (date.month ());
280}
281
282// Function: DAY
283Value func_day (valVector args, ValueCalc *calc, FuncExtra *)
284{
285 Value v = calc->conv()->asDate (args[0]);
286 if (v.isError()) return v;
287 TQDate date = v.asDate();
288 return Value (date.day ());
289}
290
291// Function: HOUR
292Value func_hour (valVector args, ValueCalc *calc, FuncExtra *)
293{
294 TQTime time;
295 if (args.count() == 1)
296 {
297 Value v = calc->conv()->asTime (args[0]);
298 if (v.isError()) return v;
299 time = v.asTime();
300 }
301 else
302 time = TQTime::currentTime ();
303 return Value (time.hour ());
304}
305
306// Function: MINUTE
307Value func_minute (valVector args, ValueCalc *calc, FuncExtra *)
308{
309 TQTime time;
310 if (args.count() == 1)
311 {
312 Value v = calc->conv()->asTime (args[0]);
313 if (v.isError()) return v;
314 time = v.asTime();
315 }
316 else
317 time = TQTime::currentTime ();
318 return Value (time.minute ());
319}
320
321// Function: SECOND
322Value func_second (valVector args, ValueCalc *calc, FuncExtra *)
323{
324 TQTime time;
325 if (args.count() == 1)
326 {
327 Value v = calc->conv()->asTime (args[0]);
328 if (v.isError()) return v;
329 time = v.asTime();
330 }
331 else
332 time = TQTime::currentTime ();
333 return Value (time.second ());
334}
335
336// Function: weekday
337Value func_weekday (valVector args, ValueCalc *calc, FuncExtra *)
338{
339 Value v = calc->conv()->asDate (args[0]).asDate();
340 if (v.isError()) return v;
341 TQDate date = v.asDate();
342 int method = 1;
343 if (args.count() == 2)
344 method = calc->conv()->asInteger (args[1]).asInteger();
345
346 if ( method < 1 || method > 3 )
347 return Value::errorVALUE();
348
349 int result = date.dayOfWeek();
350
351 if (method == 3)
352 --result;
353 else if (method == 1)
354 {
355 ++result;
356 result = result % 7;
357 }
358
359 return Value (result);
360}
361
362// Function: datevalue
363// same result would be obtained by applying number format on a date value
364Value func_datevalue (valVector args, ValueCalc *calc, FuncExtra *)
365{
366 if (args[0].isString()) {
367 Value v = calc->conv()->asDate (args[0]);
368 if (! v.isError())
369 return calc->conv()->asFloat (v);
370 }
371 return Value::errorVALUE();
372}
373
374// Function: timevalue
375// same result would be obtained by applying number format on a time value
376Value func_timevalue (valVector args, ValueCalc *calc, FuncExtra *)
377{
378 if (args[0].isString()) {
379 Value v = calc->conv()->asTime (args[0]);
380 if (! v.isError())
381 return calc->conv()->asFloat (v);
382 }
383 return Value::errorVALUE();
384}
385
386// Function: years
387Value func_years (valVector args, ValueCalc *calc, FuncExtra *)
388{
389 TQDate date1 = calc->conv()->asDate (args[0]).asDate();
390 TQDate date2 = calc->conv()->asDate (args[1]).asDate();
391 if (!date1.isValid() || !date2.isValid())
392 return Value::errorVALUE();
393
394 int type = calc->conv()->asInteger (args[2]).asInteger();
395 if (type == 0)
396 {
397 // max. possible years between both dates
398 int years = date2.year() - date1.year();
399
400 if (date2.month() < date1.month())
401 --years;
402 else if ( (date2.month() == date1.month()) && (date2.day() < date1.day()) )
403 --years;
404
405 return Value (years);
406 }
407
408 // type is non-zero now
409 // the number of full years in between, starting on 1/1/XXXX
410 if ( date1.year() == date2.year() )
411 return Value (0);
412
413 if ( (date1.month() != 1) || (date1.day() != 1) )
414 date1.setYMD(date1.year() + 1, 1, 1);
415 date2.setYMD(date2.year(), 1, 1);
416
417 return Value (date2.year() - date1.year());
418}
419
420// Function: months
421Value func_months (valVector args, ValueCalc *calc, FuncExtra *)
422{
423 TQDate date1 = calc->conv()->asDate (args[0]).asDate();
424 TQDate date2 = calc->conv()->asDate (args[1]).asDate();
425 if (!date1.isValid() || !date2.isValid())
426 return Value::errorVALUE();
427
428 int type = calc->conv()->asInteger (args[2]).asInteger();
429 if (type == 0)
430 {
431 int months = (date2.year() - date1.year()) * 12;
432 months += date2.month() - date1.month();
433
434 if (date2.day() < date1.day())
435 if (date2.day() != date2.daysInMonth())
436 --months;
437
438 return Value (months);
439 }
440
441 // type is now non-zero
442 // the number of full months in between, starting on 1/XX/XXXX
443 if (date1.month() == 12)
444 date1.setYMD(date1.year() + 1, 1, 1);
445 else
446 date1.setYMD(date1.year(), date1.month() + 1, 1);
447 date2.setYMD(date2.year(), date2.month(), 1);
448
449 int months = (date2.year() - date1.year()) * 12;
450 months += date2.month() - date1.month();
451
452 return Value (months);
453}
454
455// Function: weeks
456Value func_weeks (valVector args, ValueCalc *calc, FuncExtra *)
457{
458 TQDate date1 = calc->conv()->asDate (args[0]).asDate();
459 TQDate date2 = calc->conv()->asDate (args[1]).asDate();
460 if (!date1.isValid() || !date2.isValid())
461 return Value::errorVALUE();
462
463 int type = calc->conv()->asInteger (args[2]).asInteger();
464 int days = date1.daysTo (date2);
465 if (type == 0)
466 // just the number of full weeks between
467 return Value ((int) (days / 7));
468
469 // the number of full weeks between starting on mondays
470 int weekStartDay = calc->conv()->locale()->weekStartDay();
471
472 int dow1 = date1.dayOfWeek();
473 int dow2 = date2.dayOfWeek();
474
475 days -= (7 + (weekStartDay % 7) - dow1);
476 days -= ((dow2 - weekStartDay) % 7);
477
478 return Value ((int) (days / 7));
479}
480
481// Function: days
482Value func_days (valVector args, ValueCalc *calc, FuncExtra *)
483{
484 TQDate date1 = calc->conv()->asDate (args[0]).asDate();
485 TQDate date2 = calc->conv()->asDate (args[1]).asDate();
486 if (!date1.isValid() || !date2.isValid())
487 return Value::errorVALUE();
488
489 return Value (date1.daysTo (date2));
490}
491
492// Function: date
493Value func_date (valVector args, ValueCalc *calc, FuncExtra *)
494{
495 int y = calc->conv()->asInteger (args[0]).asInteger();
496 int m = calc->conv()->asInteger (args[1]).asInteger();
497 int d = calc->conv()->asInteger (args[2]).asInteger();
498
499 TQDate _date;
500 if( _date.setYMD (y, m, d))
501 return Value (_date);
502 return Value::errorVALUE();
503}
504
505// Function: day
506Value func_dayname (valVector args, ValueCalc *calc, FuncExtra *)
507{
508 int number = calc->conv()->asInteger (args[0]).asInteger();
509
510 TQString weekName = calc->conv()->locale()->calendar()->weekDayName (number);
511 if (weekName.isNull())
512 return Value::errorVALUE();
513 return Value (weekName);
514}
515
516// Function: monthname
517Value func_monthname (valVector args, ValueCalc *calc, FuncExtra *)
518{
519 int number = calc->conv()->asInteger (args[0]).asInteger();
520
521 TQString monthName = calc->conv()->locale()->calendar()->monthName (number,
522 TQDate::currentDate().year());
523 if (monthName.isNull())
524 return Value::errorVALUE();
525 return Value (monthName);
526}
527
528// Function: time
529Value func_time (valVector args, ValueCalc *calc, FuncExtra *)
530{
531 int h = calc->conv()->asInteger (args[0]).asInteger();
532 int m = calc->conv()->asInteger (args[1]).asInteger();
533 int s = calc->conv()->asInteger (args[2]).asInteger();
534
535 /* normalize the data */
536 m += s / 60;
537 s = s % 60;
538 h += m / 60;
539 m = m % 60;
540 // we'll lose hours data that carries over into days
541 h = h % 24;
542
543 // now carry down hours/minutes for negative minutes/seconds
544 if (s < 0) {
545 s += 60;
546 m -= 1;
547 }
548 if (m < 0) {
549 m += 60;
550 h -= 1;
551 }
552 if (h < 0)
553 h += 24;
554
555 return Value (TQTime (h, m, s));
556}
557
558// Function: currentDate
559Value func_currentDate (valVector, ValueCalc *, FuncExtra *)
560{
561 return Value (TQDate::currentDate ());
562}
563
564// Function: currentTime
565Value func_currentTime (valVector, ValueCalc *, FuncExtra *)
566{
567 return Value (TQTime::currentTime ());
568}
569
570// Function: currentDateTime
571Value func_currentDateTime (valVector, ValueCalc *, FuncExtra *)
572{
573 return Value (TQDateTime::currentDateTime ());
574}
575
576// Function: dayOfYear
577Value func_dayOfYear (valVector args, ValueCalc *calc, FuncExtra *)
578{
579 Value date = func_date (args, calc, 0);
580 if (date.isError()) return date;
581 return Value (date.asDate().dayOfYear());
582}
583
584// Function: daysInMonth
585Value func_daysInMonth (valVector args, ValueCalc *calc, FuncExtra *)
586{
587 int y = calc->conv()->asInteger (args[0]).asInteger();
588 int m = calc->conv()->asInteger (args[1]).asInteger();
589 TQDate date (y, m, 1);
590 return Value (date.daysInMonth());
591}
592
593// Function: isLeapYear
594Value func_isLeapYear (valVector args, ValueCalc *calc, FuncExtra *)
595{
596 int y = calc->conv()->asInteger (args[0]).asInteger();
597 return Value (TQDate::leapYear (y));
598}
599
600// Function: daysInYear
601Value func_daysInYear (valVector args, ValueCalc *calc, FuncExtra *)
602{
603 int y = calc->conv()->asInteger (args[0]).asInteger();
604 return Value (TQDate::leapYear (y) ? 366 : 365);
605}
606
607// Function: weeksInYear
608Value func_weeksInYear (valVector args, ValueCalc *calc, FuncExtra *)
609{
610 int y = calc->conv()->asInteger (args[0]).asInteger();
611 TQDate date (y, 12, 31); // last day of the year
612 return Value (date.weekNumber ());
613}
614
615// Function: easterSunday
616Value func_easterSunday (valVector args, ValueCalc *calc, FuncExtra *)
617{
618 int nDay, nMonth;
619 int nYear = calc->conv()->asInteger (args[0]).asInteger();
620
621 // (Tomas) the person who wrote this should be hanged :>
622 int B,C,D,E,F,G,H,I,K,L,M,N,O;
623 N = nYear % 19;
624 B = int(nYear / 100);
625 C = nYear % 100;
626 D = int(B / 4);
627 E = B % 4;
628 F = int((B + 8) / 25);
629 G = int((B - F + 1) / 3);
630 H = (19 * N + B - D - G + 15) % 30;
631 I = int(C / 4);
632 K = C % 4;
633 L = (32 + 2 * E + 2 * I - H - K) % 7;
634 M = int((N + 11 * H + 22 * L) / 451);
635 O = H + L - 7 * M + 114;
636 nDay = O % 31 + 1;
637 nMonth = int(O / 31);
638
639 return Value (TQDate (nYear, nMonth, nDay));
640}
641
642// Function: isoWeekNum
643Value func_isoWeekNum (valVector args, ValueCalc *calc, FuncExtra *)
644{
645 TQDate date = calc->conv()->asDate (args[0]).asDate();
646 if (!date.isValid())
647 return Value::errorVALUE();
648
649 return Value (date.weekNumber());
650}
The ValueCalc class is used to perform all sorts of calculations.
Definition valuecalc.h:69
Provides a wrapper for cell value.
TQDate asDate() const
Returns the date representation of this value.
long asInteger() const
Returns the integer value of this value.
static const Value & errorVALUE()
Returns constant reference to VALUE!
bool isError() const
Returns true if this value holds error information.
TQTime asTime() const
Returns the time representation of this value.
bool asBoolean() const
Returns the boolean value of this value.
The KSpread namespace.
Definition commands.h:39