OSDN Git Service

Automatically align underspecified tasks boundaries on project boundaries when possible.
[tjqt4port/tj2qt4.git] / taskjuggler / Utility.cpp
1 /*
2  * Utility.cpp - TaskJuggler
3  *
4  * Copyright (c) 2001, 2002, 2003, 2004 by Chris Schlaeger <cs@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of version 2 of the GNU General Public License as
8  * published by the Free Software Foundation.
9  *
10  * $Id$
11  */
12
13 #include "Utility.h"
14
15 #include <cstdio>
16 #include <cstdlib>
17 #include <cstring>
18
19 #include <string>
20 #include <stdlib.h>
21
22 #include <qdict.h>
23
24 #include "TjMessageHandler.h"
25 #include "tjlib-internal.h"
26
27 #if defined(__SVR4) && defined(__sun)
28 int setenv(const char* var, const char* val, int ignore);
29 int unsetenv (const char *var);
30 #endif
31
32 static QDict<const char> TZDict;
33 static bool TZDictReady = false;
34
35 static QString UtilityError;
36
37 /* localtime() calls are fairly expensive, so we implement a hashtable based
38  * cache to avoid redundant calls for the same value. Changing the timezone
39  * invalidates the cache though. */
40 struct LtHashTabEntry
41 {
42     time_t t;
43     struct tm* tms;
44     LtHashTabEntry* next;
45 } ;
46
47 static long LTHASHTABSIZE;
48 static LtHashTabEntry** LtHashTab = 0;
49
50 bool
51 isRichText(const QString& str)
52 {
53     /* This function tries to guess whether a string is a rich-text string or
54      * not. It looks for xml tags marks and does a simple validation on them.
55      */
56
57     bool hasTags = false;
58     bool inTag = false;
59     for (uint i = 0; i < str.length(); ++i)
60     {
61         if (str[i] == '<')
62         {
63             if (inTag)
64                 return false;
65             inTag = hasTags = true;
66         }
67         else if (str[i] == '>')
68         {
69             if (!inTag)
70                 return false;
71             inTag = false;
72         }
73     }
74
75     return hasTags && !inTag;
76 }
77
78 const char*
79 timezone2tz(const char* tzone)
80 {
81     if (!TZDictReady)
82     {
83         TZDict.setAutoDelete(false);
84
85         // Let's start with generic timezones
86         TZDict.insert("+1400", "GMT-14:00");
87         TZDict.insert("+1300", "GMT-13:00");
88         TZDict.insert("+1245", "GMT-12:45");
89         TZDict.insert("+1200", "GMT-12:00");
90         TZDict.insert("+1130", "GMT-11:30");
91         TZDict.insert("+1100", "GMT-11:00");
92         TZDict.insert("+1030", "GMT-10:30");
93         TZDict.insert("+1000", "GMT-10:00");
94         TZDict.insert("+0930", "GMT-9:30");
95         TZDict.insert("+0900", "GMT-9:00");
96         TZDict.insert("+0845", "GMT-8:45");
97         TZDict.insert("+0800", "GMT-8:00");
98         TZDict.insert("+0700", "GMT-7:00");
99         TZDict.insert("+0630", "GMT-6:30");
100         TZDict.insert("+0600", "GMT-6:00");
101         TZDict.insert("+0545", "GMT-5:45");
102         TZDict.insert("+0530", "GMT-5:30");
103         TZDict.insert("+0500", "GMT-5:00");
104         TZDict.insert("+0400", "GMT-4:00");
105         TZDict.insert("+0330", "GMT-3:30");
106         TZDict.insert("+0300", "GMT-3:00");
107         TZDict.insert("+0200", "GMT-2:00");
108         TZDict.insert("+0100", "GMT-1:00");
109         TZDict.insert("+0000", "GMT-0:00");
110         TZDict.insert("-0100", "GMT+1:00");
111         TZDict.insert("-0200", "GMT+2:00");
112         TZDict.insert("-0300", "GMT+3:00");
113         TZDict.insert("-0330", "GMT+3:30");
114         TZDict.insert("-0400", "GMT+4:00");
115         TZDict.insert("-0430", "GMT+4:30");
116         TZDict.insert("-0500", "GMT+5:00");
117         TZDict.insert("-0600", "GMT+6:00");
118         TZDict.insert("-0700", "GMT+7:00");
119         TZDict.insert("-0800", "GMT+8:00");
120         TZDict.insert("-0900", "GMT+9:00");
121         TZDict.insert("-1000", "GMT+10:00");
122         TZDict.insert("-1100", "GMT+11:00");
123         TZDict.insert("-1200", "GMT+12:00");
124         // Now some conveniance timezones. There will be more in the future.
125         TZDict.insert("PST", "GMT+8:00");
126         TZDict.insert("PDT", "GMT+7:00");
127         TZDict.insert("MST", "GMT+7:00");
128         TZDict.insert("MDT", "GMT+6:00");
129         TZDict.insert("CST", "GMT+6:00");
130         TZDict.insert("CDT", "GMT+5:00");
131         TZDict.insert("EST", "GMT+5:00");
132         TZDict.insert("EDT", "GMT+4:00");
133         TZDict.insert("GMT", "GMT");
134         TZDict.insert("UTC", "GMT");
135         TZDict.insert("CET", "GMT-1:00");
136         TZDict.insert("CEDT", "GMT-2:00");
137
138         TZDictReady = true;
139     }
140
141     return TZDict[tzone];
142 }
143
144 void initUtility(long dictSize)
145 {
146     if (LtHashTab)
147         exitUtility();
148
149     /* Find a prime number that is equal or bigger than dictSize. */
150     for (long i = 2; i < (dictSize / 2); i++)
151         if (dictSize % i == 0)
152         {
153             dictSize++;
154             i = 1;
155         }
156
157     LtHashTab = new LtHashTabEntry*[LTHASHTABSIZE = dictSize];
158     for (long i = 0; i < LTHASHTABSIZE; ++i)
159         LtHashTab[i] = 0;
160 }
161
162 void exitUtility()
163 {
164     if (!LtHashTab)
165         return;
166
167     for (long i = 0; i < LTHASHTABSIZE; ++i)
168         for (LtHashTabEntry* htep = LtHashTab[i]; htep; )
169         {
170             LtHashTabEntry* tmp = htep->next;
171             delete htep->tms;
172             htep = tmp;
173         }
174
175     delete [] LtHashTab;
176     LtHashTab = 0;
177 }
178
179 int
180 suggestTimingResolution()
181 {
182      time_t now = time(0);
183      struct tm* tmb = localtime(&now);
184      int minutes = tmb->tm_min;
185      tmb = gmtime(&now);
186      int difference = abs(minutes - tmb->tm_min);
187      if (difference == 0)
188          return 60 * 60;
189      else if (difference == 30)
190          return 60 * 30;
191      else if (difference == 15 || difference == 45)
192          return 60 * 15;
193
194      qFatal("Your timezone is not 15, 30 or 60 minutes aligned to UTC!");
195      return -1;
196 }
197
198 bool
199 setTimezone(const char* tZone)
200 {
201     UtilityError = "";
202
203     if (setenv("TZ", tZone, 1) < 0)
204         qFatal("Ran out of space in environment section while "
205                "setting timezone.");
206
207     /* To validate the tZone value we call tzset(). It will convert the zone
208      * into a three-letter acronym in case the tZone value is good. If not, it
209      * will just copy the wrong value to tzname[0] (glibc < 2.5) or fall back
210      * to UTC. */
211     tzset();
212     if (timezone2tz(tZone) == 0 &&
213         (strcmp(tzname[0], tZone) == 0 ||
214          (strcmp(tZone, "UTC") != 0 && strcmp(tzname[0], "UTC") == 0)))
215     {
216         UtilityError = QString(i18n("Illegal timezone '%1'")).arg(tZone);
217         return false;
218     }
219
220     if (!LtHashTab)
221         return true;
222     for (long i = 0; i < LTHASHTABSIZE; ++i)
223     {
224         for (LtHashTabEntry* htep = LtHashTab[i]; htep; )
225         {
226             LtHashTabEntry* tmp = htep->next;
227             delete htep->tms;
228             htep = tmp;
229         }
230         if (LtHashTab[i])
231             LtHashTab[i] = 0;
232     }
233     return true;
234 }
235
236 const struct tm * const
237 clocaltime(const time_t* t)
238 {
239     /* In some cases we haven't initialized the module yet. So we do not use
240      * the cache. */
241     if (!LtHashTab)
242         return localtime(t);
243
244     long index = *t % LTHASHTABSIZE;
245     if (LtHashTab[index])
246         for (LtHashTabEntry* htep = LtHashTab[index]; htep;
247              htep = htep->next)
248             if (htep->t == *t)
249                 return htep->tms;
250
251     LtHashTabEntry* htep = new LtHashTabEntry;
252     htep->next = LtHashTab[index];
253     htep->t = *t;
254     htep->tms = new struct tm;
255     memcpy(htep->tms, localtime(t), sizeof(struct tm));
256     LtHashTab[index] = htep;
257     return htep->tms;
258 }
259
260 const QString&
261 getUtilityError()
262 {
263     return UtilityError;
264 }
265
266 QString
267 monthAndYear(time_t t)
268 {
269     const struct tm* tms = clocaltime(&t);
270     static char s[32];
271     strftime(s, sizeof(s), "%b %Y", tms);
272     return QString::fromLocal8Bit(s);
273 }
274
275 QString
276 shortMonthName(int mon)
277 {
278     struct tm tms;
279     tms.tm_mday = 1;
280     tms.tm_mon = mon;
281     tms.tm_year = 2000;
282     static char s[32];
283     strftime(s, sizeof(s), "%b", &tms);
284     return QString::fromLocal8Bit(s);
285 }
286
287 bool
288 isWeekend(time_t t)
289 {
290     const struct tm* tms = clocaltime(&t);
291     return (tms->tm_wday < 1 || tms->tm_wday > 5);
292 }
293
294 int
295 daysLeftInMonth(time_t t)
296 {
297     int left = 0;
298     const struct tm* tms = clocaltime(&t);
299     for (int m = tms->tm_mon; tms->tm_mon == m; )
300     {
301         left++;
302         t = sameTimeNextDay(t);
303         tms = clocaltime(&t);
304     }
305     return left;
306 }
307
308 int
309 weeksLeftInMonth(time_t t)
310 {
311     int left = 0;
312     const struct tm* tms = clocaltime(&t);
313     for (int m = tms->tm_mon; tms->tm_mon == m; )
314     {
315         left++;
316         t = sameTimeNextWeek(t);
317         tms = clocaltime(&t);
318     }
319     return left;
320 }
321
322 int
323 monthLeftInYear(time_t t)
324 {
325     int left = 0;
326     const struct tm* tms = clocaltime(&t);
327     for (int m = tms->tm_year; tms->tm_year == m; )
328     {
329         left++;
330         t = sameTimeNextMonth(t);
331         tms = clocaltime(&t);
332     }
333     return left;
334 }
335
336 int
337 quartersLeftInYear(time_t t)
338 {
339     int left = 0;
340     const struct tm* tms = clocaltime(&t);
341     for (int m = tms->tm_year; tms->tm_year == m; )
342     {
343         left++;
344         t = sameTimeNextQuarter(t);
345         tms = clocaltime(&t);
346     }
347     return left;
348 }
349
350 int
351 daysBetween(time_t t1, time_t t2)
352 {
353     int days = 0;
354     // TODO: Very slow!!!
355     for (time_t t = t1; t < t2; t = sameTimeNextDay(t))
356         days++;
357     return days;
358 }
359
360 int
361 weeksBetween(time_t t1, time_t t2)
362 {
363     int days = 0;
364     // TODO: Very slow!!!
365     for (time_t t = t1; t < t2; t = sameTimeNextWeek(t))
366         days++;
367     return days;
368 }
369
370 int
371 monthsBetween(time_t t1, time_t t2)
372 {
373     int months = 0;
374     // TODO: Very slow!!!
375     for (time_t t = t1; t < t2; t = sameTimeNextMonth(t))
376         months++;
377     return months;
378 }
379
380 int
381 quartersBetween(time_t t1, time_t t2)
382 {
383     int quarters = 0;
384     // TODO: Very slow!!!
385     for (time_t t = t1; t < t2; t = sameTimeNextQuarter(t))
386         quarters++;
387     return quarters;
388 }
389
390 int
391 secondsOfDay(time_t t)
392 {
393     const struct tm* tms = clocaltime(&t);
394     return tms->tm_sec + tms->tm_min * 60 + tms->tm_hour * 3600;
395 }
396
397 int
398 hourOfDay(time_t t)
399 {
400     const struct tm* tms = clocaltime(&t);
401     return tms->tm_hour;
402 }
403
404 int
405 dayOfMonth(time_t t)
406 {
407     const struct tm* tms = clocaltime(&t);
408     return tms->tm_mday;
409 }
410
411 int
412 weekOfYear(time_t t, bool beginOnMonday)
413 {
414     /* The  ISO 8601:1988 week number of the current year as a decimal
415      * number, range 1 to 53, where  week  1 is  the first week that has at
416      * least 4 days in the current year, and with Monday as the first day
417      * of the week. This is also compliant with DIN 1355. */
418     uint week = 0;
419     uint weekday1Jan = dayOfWeek(beginOfYear(t), beginOnMonday);
420     const struct tm* tms = clocaltime(&t);
421     int days = tms->tm_yday;
422
423     if (weekday1Jan > 3)
424         days = days - (7 - weekday1Jan);
425     else
426         days = days + weekday1Jan;
427
428     if (days < 0)
429         if ((weekday1Jan == 4) ||
430             (dayOfWeek(beginOfYear(beginOfYear(t) - 1), beginOnMonday) == 3))
431             week = 53;
432         else
433             week = 52;
434     else
435         week = days / 7 + 1;
436
437     if ((days > 360) && (week > 52))
438     {
439         if (weekday1Jan == 3)
440             week = 53;
441         else if (dayOfWeek(sameTimeNextYear(beginOfYear(t)),
442                            beginOnMonday) == 4)
443             week = 53;
444         else
445             week = 1;
446     }
447
448     return week;
449 }
450
451 int
452 monthOfWeek(time_t t, bool beginOnMonday)
453 {
454     const struct tm* tms = clocaltime(&t);
455     int tm_mon = tms->tm_mon;
456     int tm_mday = tms->tm_mday;
457     int lastDayOfMonth = dayOfMonth(beginOfMonth(sameTimeNextMonth(t)) - 1);
458     if (tm_mday < 4)
459     {
460         if (dayOfWeek(t, beginOnMonday) - tm_mday >= 3)
461             if (tm_mon == 0)
462                 return 12;
463             else
464                 return tm_mon;
465     }
466     else if (tm_mday > lastDayOfMonth - 4)
467     {
468         if (tm_mday - dayOfWeek(t, beginOnMonday) > lastDayOfMonth - 4)
469             if (tm_mon == 11)
470                 return 1;
471             else
472                 return tm_mon + 2;
473     }
474     return tm_mon + 1;
475 }
476
477 int
478 monthOfYear(time_t t)
479 {
480     const struct tm* tms = clocaltime(&t);
481     return tms->tm_mon + 1;
482 }
483
484 int
485 quarterOfYear(time_t t)
486 {
487     const struct tm* tms = clocaltime(&t);
488     return tms->tm_mon / 3 + 1;
489 }
490
491 int
492 dayOfWeek(time_t t, bool beginOnMonday)
493 {
494     const struct tm* tms = clocaltime(&t);
495     if (beginOnMonday)
496         return tms->tm_wday ? tms->tm_wday - 1 : 6;
497     else
498         return tms->tm_wday;
499 }
500
501 QString
502 dayOfWeekName(time_t t)
503 {
504     const struct tm* tms = clocaltime(&t);
505     static char buf[64];
506
507     strftime(buf, 63, "%A", tms);
508     return QString::fromLocal8Bit(buf);
509 }
510
511 int
512 dayOfYear(time_t t)
513 {
514     const struct tm* tms = clocaltime(&t);
515     return tms->tm_yday + 1;
516 }
517
518
519 int
520 year(time_t t)
521 {
522     const struct tm* tms = clocaltime(&t);
523     return tms->tm_year + 1900;
524 }
525
526 int
527 yearOfWeek(time_t t, bool beginOnMonday)
528 {
529     const struct tm* tms = clocaltime(&t);
530     int tm_year = tms->tm_year;
531
532     int lastDayOfYear = dayOfYear(beginOfYear(sameTimeNextYear(t)) - 1);
533     if (dayOfYear(t) < 4)
534     {
535         if (dayOfWeek(t, beginOnMonday) - dayOfYear(t) >= 3)
536             return 1900 + tm_year - 1;
537     }
538     else if (dayOfYear(t) > lastDayOfYear - 4)
539     {
540         if (dayOfYear(t) - dayOfWeek(t, beginOnMonday) > lastDayOfYear - 4)
541             return 1900 + tm_year + 1;
542     }
543     return 1900 + tm_year;
544 }
545
546 time_t
547 beginOfHour(time_t t)
548 {
549     const struct tm* tms = clocaltime(&t);
550     struct tm tmc;
551     memcpy(&tmc, tms, sizeof(struct tm));
552     tmc.tm_sec = tmc.tm_min = 0;
553     tmc.tm_isdst = -1;
554     return mktime(&tmc);
555 }
556
557 time_t
558 midnight(time_t t)
559 {
560     const struct tm* tms = clocaltime(&t);
561     struct tm tmc;
562     memcpy(&tmc, tms, sizeof(struct tm));
563     tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
564     tmc.tm_isdst = -1;
565     return mktime(&tmc);
566 }
567
568 time_t
569 beginOfWeek(time_t t, bool beginOnMonday)
570 {
571     const struct tm* tms;
572     for (tms = clocaltime(&t) ; tms->tm_wday != (beginOnMonday ? 1 : 0); )
573     {
574         t = sameTimeYesterday(t);
575         tms = clocaltime(&t);
576     }
577     struct tm tmc;
578     memcpy(&tmc, tms, sizeof(struct tm));
579     tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
580     tmc.tm_isdst = -1;
581     return mktime(&tmc);
582 }
583
584 time_t
585 beginOfMonth(time_t t)
586 {
587     const struct tm* tms = clocaltime(&t);
588     struct tm tmc;
589     memcpy(&tmc, tms, sizeof(struct tm));
590     tmc.tm_mday = 1;
591     tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
592     tmc.tm_isdst = -1;
593     return mktime(&tmc);
594 }
595
596 time_t
597 beginOfQuarter(time_t t)
598 {
599     const struct tm* tms = clocaltime(&t);
600     struct tm tmc;
601     memcpy(&tmc, tms, sizeof(struct tm));
602     tmc.tm_mon = (tmc.tm_mon / 3) * 3;
603     tmc.tm_mday = 1;
604     tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
605     tmc.tm_isdst = -1;
606     return mktime(&tmc);
607 }
608
609 time_t
610 beginOfYear(time_t t)
611 {
612     const struct tm* tms = clocaltime(&t);
613     struct tm tmc;
614     memcpy(&tmc, tms, sizeof(struct tm));
615     tmc.tm_mon = 0;
616     tmc.tm_mday = 1;
617     tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
618     tmc.tm_isdst = -1;
619     return mktime(&tmc);
620 }
621
622 time_t
623 hoursLater(int h, time_t t)
624 {
625     // I hope this is correct under all circumstances.
626     return t + h * ONEHOUR;
627 }
628
629 time_t
630 sameTimeNextDay(time_t t)
631 {
632     const struct tm* tms = clocaltime(&t);
633     struct tm tmc;
634     memcpy(&tmc, tms, sizeof(struct tm));
635     tmc.tm_mday++;
636     tmc.tm_isdst = -1;
637     if (mktime(&tmc) == -1)
638         qFatal("Error at %s", time2ISO(t).latin1());
639     return mktime(&tmc);
640 }
641
642 time_t
643 sameTimeYesterday(time_t t)
644 {
645     const struct tm* tms = clocaltime(&t);
646     struct tm tmc;
647     memcpy(&tmc, tms, sizeof(struct tm));
648     tmc.tm_mday--;
649     tmc.tm_isdst = -1;
650     return mktime(&tmc);
651 }
652
653 time_t
654 sameTimeNextWeek(time_t t)
655 {
656     const struct tm* tms = clocaltime(&t);
657     int weekday = tms->tm_wday;
658     do
659     {
660         t = sameTimeNextDay(t);
661         tms = clocaltime(&t);
662     } while (tms->tm_wday != weekday);
663     return t;
664 }
665
666 time_t
667 sameTimeLastWeek(time_t t)
668 {
669     const struct tm* tms = clocaltime(&t);
670     int weekday = tms->tm_wday;
671     do
672     {
673         t = sameTimeYesterday(t);
674         tms = clocaltime(&t);
675     } while (tms->tm_wday != weekday);
676     return t;
677 }
678
679 time_t
680 sameTimeNextMonth(time_t t)
681 {
682     const struct tm* tms = clocaltime(&t);
683     struct tm tmc;
684     memcpy(&tmc, tms, sizeof(struct tm));
685     tmc.tm_mon++;
686     tmc.tm_isdst = -1;
687     return mktime(&tmc);
688 }
689
690 time_t
691 sameTimeNextQuarter(time_t t)
692 {
693     const struct tm* tms = clocaltime(&t);
694     struct tm tmc;
695     memcpy(&tmc, tms, sizeof(struct tm));
696     tmc.tm_mon += 3;
697     tmc.tm_isdst = -1;
698     return mktime(&tmc);
699 }
700
701 time_t
702 sameTimeNextYear(time_t t)
703 {
704     const struct tm* tms = clocaltime(&t);
705     struct tm tmc;
706     memcpy(&tmc, tms, sizeof(struct tm));
707     tmc.tm_year++;
708     tmc.tm_isdst = -1;
709     return mktime(&tmc);
710 }
711
712 time_t
713 sameTimeLastYear(time_t t)
714 {
715     const struct tm* tms = clocaltime(&t);
716     struct tm tmc;
717     memcpy(&tmc, tms, sizeof(struct tm));
718     tmc.tm_year--;
719     tmc.tm_isdst = -1;
720     return mktime(&tmc);
721 }
722
723 QString
724 time2ISO(time_t t)
725 {
726     const struct tm* tms = clocaltime(&t);
727     static char buf[128];
728
729     strftime(buf, 127, "%Y-%m-%d %H:%M:%S %Z", tms);
730     return QString::fromLocal8Bit(buf);
731 }
732
733 QString
734 time2tjp(time_t t)
735 {
736     const struct tm* tms = clocaltime(&t);
737     static char buf[128];
738
739     strftime(buf, 127, "%Y-%m-%d-%H:%M:%S-%z", tms);
740     return QString::fromLocal8Bit(buf);
741 }
742
743 QString
744 time2user(time_t t, const QString& timeFormat, bool localtime)
745 {
746     if (t == 0)
747         return i18n("undefined");
748
749     const struct tm* tms;
750     if (localtime)
751         tms = clocaltime(&t);
752     else
753         tms = gmtime(&t);
754
755     static char buf[128];
756
757     strftime(buf, 127, timeFormat, tms);
758     return QString::fromLocal8Bit(buf);
759 }
760
761 QString
762 time2time(time_t t)
763 {
764     const struct tm* tms = clocaltime(&t);
765     static char buf[128];
766
767     strftime(buf, 127, "%H:%M %Z", tms);
768     return QString::fromLocal8Bit(buf);
769 }
770
771 QString
772 time2date(time_t t)
773 {
774     const struct tm* tms = clocaltime(&t);
775     static char buf[128];
776
777     strftime(buf, 127, "%Y-%m-%d", tms);
778     return QString::fromLocal8Bit(buf);
779 }
780
781 QString
782 time2weekday(time_t t)
783 {
784     const struct tm* tms = clocaltime(&t);
785     static char buf[128];
786
787     strftime(buf, 127, "%A", tms);
788     return QString::fromLocal8Bit(buf);
789 }
790
791 time_t
792 addTimeToDate(time_t day, time_t hour)
793 {
794     day = midnight(day);
795     const struct tm* tms = clocaltime(&day);
796
797     struct tm tmc;
798     memcpy(&tmc, tms, sizeof(struct tm));
799
800     tmc.tm_hour = hour / (60 * 60);
801     tmc.tm_min = (hour / 60) % 60;
802     tmc.tm_sec = hour % 60;
803
804     tmc.tm_isdst = -1;
805     return mktime(&tmc);
806 }
807
808 time_t
809 date2time(const QString& date)
810 {
811     UtilityError = "";
812
813     int y, m, d, hour, min, sec;
814     char tZone[64] = "";
815     std::string savedTZ;
816     bool restoreTZ = false;
817     if (sscanf(date, "%d-%d-%d-%d:%d:%d-%s",
818                &y, &m, &d, &hour, &min, &sec, tZone) == 7 ||
819         (sec = 0) ||    // set sec to 0
820         sscanf(date, "%d-%d-%d-%d:%d-%s",
821                &y, &m, &d, &hour, &min, tZone) == 6)
822     {
823         const char* tz;
824         if ((tz = getenv("TZ")) != 0)
825         {
826             savedTZ = tz;
827         }
828         if ((tz = timezone2tz(tZone)) == 0)
829         {
830             UtilityError = QString(i18n("Illegal timezone %1")).arg(tZone);
831             return 0;
832         }
833         else
834         {
835             if (setenv("TZ", tz, 1) < 0)
836                 qFatal("date2time: Ran out of space in environment section.");
837             restoreTZ = true;
838         }
839     }
840     else if (sscanf(date, "%d-%d-%d-%d:%d:%d",
841                     &y, &m, &d, &hour, &min, &sec) == 6)
842         tZone[0] = '\0';
843     else if (sscanf(date, "%d-%d-%d-%d:%d", &y, &m, &d, &hour, &min) == 5)
844     {
845         sec = 0;
846         tZone[0] = '\0';
847     }
848     else if (sscanf(date, "%d-%d-%d", &y, &m, &d) == 3)
849     {
850         tZone[0] = '\0';
851         hour = min = sec = 0;
852     }
853     else
854     {
855         qFatal("Illegal date: %s", date.latin1());
856         return 0;
857     }
858
859     if (y < 1970)
860     {
861         UtilityError = i18n("Year must be larger than 1969");
862         return 0;
863     }
864     if (m < 1 || m > 12)
865     {
866         UtilityError = i18n("Month must be between 1 and 12");
867         return 0;
868     }
869     if (d < 1 || d > 31)
870     {
871         UtilityError = i18n("Day must be between 1 and 31");
872         return 0;
873     }
874     if (hour < 0 || hour > 23)
875     {
876         UtilityError = i18n("Hour must be between 0 and 23");
877         return 0;
878     }
879     if (min < 0 || min > 59)
880     {
881         UtilityError = i18n("Minutes must be between 0 and 59");
882         return 0;
883     }
884     if (sec < 0 || sec > 59)
885     {
886         UtilityError = i18n("Seconds must be between 0 and 59");
887         return 0;
888     }
889
890 #if defined(__CYGWIN__) || (defined(__SVR4) && defined(__sun))
891     struct tm t = { sec, min, hour, d, m - 1, y - 1900, 0, 0, -1 };
892 #else
893     struct tm t = { sec, min, hour, d, m - 1, y - 1900, 0, 0, -1, 0, 0 };
894 #endif
895     time_t localTime = mktime(&t);
896
897     if (restoreTZ)
898     {
899         if (!savedTZ.empty())
900         {
901             if (setenv("TZ", savedTZ.c_str(), 1) < 0)
902                 qFatal("date2time: Ran out of space in environment section.");
903         }
904         else
905             unsetenv("TZ");
906     }
907
908     return localTime;
909 }
910
911 QDate
912 time2qdate(time_t t)
913 {
914     return QDate(year(t), monthOfYear(t), dayOfMonth(t));
915 }
916
917 time_t
918 qdate2time(const QDate& d)
919 {
920 #if defined(__CYGWIN__) || (defined(__SVR4) && defined(__sun))
921     struct tm t = { 0, 0, 0, d.day(), d.month() - 1, d.year() - 1900,
922                     0, 0, -1 };
923 #else
924     struct tm t = { 0, 0, 0, d.day(), d.month() - 1, d.year() - 1900,
925                     0, 0, -1, 0, 0 };
926 #endif
927     return mktime(&t);
928 }
929
930 #if defined(__SVR4) && defined(__sun)
931 /*
932  * Note: a proper implementation of a "setenv" function for Solaris
933  *       would take a map where the "variable-name" is linked to a
934  *       pointer. This would allow freeing the memory allocated here
935  *       (a kind of garbage collection).
936  */
937
938 int setenv(const char* var, const char* val, int ignore)
939 {
940    int varLen = strlen(var);
941    int valLen = strlen(val);
942    char *buffer = NULL;
943
944    if ((buffer = static_cast<char*>(malloc(varLen + valLen + 2))) == NULL)
945       return -1;
946
947    sprintf (buffer, "%s=%s", var, val);
948
949    return putenv(buffer) ? -1 : 0;
950 }
951
952 int unsetenv (const char *var)
953 {
954    return 0;    /* SKIP */
955 }
956 #endif
957