2 * Utility.cpp - TaskJuggler
4 * Copyright (c) 2001, 2002, 2003, 2004 by Chris Schlaeger <cs@kde.org>
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.
24 #include "TjMessageHandler.h"
25 #include "tjlib-internal.h"
27 #if defined(__SVR4) && defined(__sun)
28 int setenv(const char* var, const char* val, int ignore);
29 int unsetenv (const char *var);
32 static QDict<const char> TZDict;
33 static bool TZDictReady = false;
35 static QString UtilityError;
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. */
47 static long LTHASHTABSIZE;
48 static LtHashTabEntry** LtHashTab = 0;
51 isRichText(const QString& str)
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.
59 for (uint i = 0; i < str.length(); ++i)
65 inTag = hasTags = true;
67 else if (str[i] == '>')
75 return hasTags && !inTag;
79 timezone2tz(const char* tzone)
83 TZDict.setAutoDelete(false);
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");
141 return TZDict[tzone];
144 void initUtility(long dictSize)
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)
157 LtHashTab = new LtHashTabEntry*[LTHASHTABSIZE = dictSize];
158 for (long i = 0; i < LTHASHTABSIZE; ++i)
167 for (long i = 0; i < LTHASHTABSIZE; ++i)
168 for (LtHashTabEntry* htep = LtHashTab[i]; htep; )
170 LtHashTabEntry* tmp = htep->next;
180 suggestTimingResolution()
182 time_t now = time(0);
183 struct tm* tmb = localtime(&now);
184 int minutes = tmb->tm_min;
186 int difference = abs(minutes - tmb->tm_min);
189 else if (difference == 30)
191 else if (difference == 15 || difference == 45)
194 qFatal("Your timezone is not 15, 30 or 60 minutes aligned to UTC!");
199 setTimezone(const char* tZone)
203 if (setenv("TZ", tZone, 1) < 0)
204 qFatal("Ran out of space in environment section while "
205 "setting timezone.");
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
212 if (timezone2tz(tZone) == 0 &&
213 (strcmp(tzname[0], tZone) == 0 ||
214 (strcmp(tZone, "UTC") != 0 && strcmp(tzname[0], "UTC") == 0)))
216 UtilityError = QString(i18n("Illegal timezone '%1'")).arg(tZone);
222 for (long i = 0; i < LTHASHTABSIZE; ++i)
224 for (LtHashTabEntry* htep = LtHashTab[i]; htep; )
226 LtHashTabEntry* tmp = htep->next;
236 const struct tm * const
237 clocaltime(const time_t* t)
239 /* In some cases we haven't initialized the module yet. So we do not use
244 long index = *t % LTHASHTABSIZE;
245 if (LtHashTab[index])
246 for (LtHashTabEntry* htep = LtHashTab[index]; htep;
251 LtHashTabEntry* htep = new LtHashTabEntry;
252 htep->next = LtHashTab[index];
254 htep->tms = new struct tm;
255 memcpy(htep->tms, localtime(t), sizeof(struct tm));
256 LtHashTab[index] = htep;
267 monthAndYear(time_t t)
269 const struct tm* tms = clocaltime(&t);
271 strftime(s, sizeof(s), "%b %Y", tms);
272 return QString::fromLocal8Bit(s);
276 shortMonthName(int mon)
283 strftime(s, sizeof(s), "%b", &tms);
284 return QString::fromLocal8Bit(s);
290 const struct tm* tms = clocaltime(&t);
291 return (tms->tm_wday < 1 || tms->tm_wday > 5);
295 daysLeftInMonth(time_t t)
298 const struct tm* tms = clocaltime(&t);
299 for (int m = tms->tm_mon; tms->tm_mon == m; )
302 t = sameTimeNextDay(t);
303 tms = clocaltime(&t);
309 weeksLeftInMonth(time_t t)
312 const struct tm* tms = clocaltime(&t);
313 for (int m = tms->tm_mon; tms->tm_mon == m; )
316 t = sameTimeNextWeek(t);
317 tms = clocaltime(&t);
323 monthLeftInYear(time_t t)
326 const struct tm* tms = clocaltime(&t);
327 for (int m = tms->tm_year; tms->tm_year == m; )
330 t = sameTimeNextMonth(t);
331 tms = clocaltime(&t);
337 quartersLeftInYear(time_t t)
340 const struct tm* tms = clocaltime(&t);
341 for (int m = tms->tm_year; tms->tm_year == m; )
344 t = sameTimeNextQuarter(t);
345 tms = clocaltime(&t);
351 daysBetween(time_t t1, time_t t2)
354 // TODO: Very slow!!!
355 for (time_t t = t1; t < t2; t = sameTimeNextDay(t))
361 weeksBetween(time_t t1, time_t t2)
364 // TODO: Very slow!!!
365 for (time_t t = t1; t < t2; t = sameTimeNextWeek(t))
371 monthsBetween(time_t t1, time_t t2)
374 // TODO: Very slow!!!
375 for (time_t t = t1; t < t2; t = sameTimeNextMonth(t))
381 quartersBetween(time_t t1, time_t t2)
384 // TODO: Very slow!!!
385 for (time_t t = t1; t < t2; t = sameTimeNextQuarter(t))
391 secondsOfDay(time_t t)
393 const struct tm* tms = clocaltime(&t);
394 return tms->tm_sec + tms->tm_min * 60 + tms->tm_hour * 3600;
400 const struct tm* tms = clocaltime(&t);
407 const struct tm* tms = clocaltime(&t);
412 weekOfYear(time_t t, bool beginOnMonday)
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. */
419 uint weekday1Jan = dayOfWeek(beginOfYear(t), beginOnMonday);
420 const struct tm* tms = clocaltime(&t);
421 int days = tms->tm_yday;
424 days = days - (7 - weekday1Jan);
426 days = days + weekday1Jan;
429 if ((weekday1Jan == 4) ||
430 (dayOfWeek(beginOfYear(beginOfYear(t) - 1), beginOnMonday) == 3))
437 if ((days > 360) && (week > 52))
439 if (weekday1Jan == 3)
441 else if (dayOfWeek(sameTimeNextYear(beginOfYear(t)),
452 monthOfWeek(time_t t, bool beginOnMonday)
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);
460 if (dayOfWeek(t, beginOnMonday) - tm_mday >= 3)
466 else if (tm_mday > lastDayOfMonth - 4)
468 if (tm_mday - dayOfWeek(t, beginOnMonday) > lastDayOfMonth - 4)
478 monthOfYear(time_t t)
480 const struct tm* tms = clocaltime(&t);
481 return tms->tm_mon + 1;
485 quarterOfYear(time_t t)
487 const struct tm* tms = clocaltime(&t);
488 return tms->tm_mon / 3 + 1;
492 dayOfWeek(time_t t, bool beginOnMonday)
494 const struct tm* tms = clocaltime(&t);
496 return tms->tm_wday ? tms->tm_wday - 1 : 6;
502 dayOfWeekName(time_t t)
504 const struct tm* tms = clocaltime(&t);
507 strftime(buf, 63, "%A", tms);
508 return QString::fromLocal8Bit(buf);
514 const struct tm* tms = clocaltime(&t);
515 return tms->tm_yday + 1;
522 const struct tm* tms = clocaltime(&t);
523 return tms->tm_year + 1900;
527 yearOfWeek(time_t t, bool beginOnMonday)
529 const struct tm* tms = clocaltime(&t);
530 int tm_year = tms->tm_year;
532 int lastDayOfYear = dayOfYear(beginOfYear(sameTimeNextYear(t)) - 1);
533 if (dayOfYear(t) < 4)
535 if (dayOfWeek(t, beginOnMonday) - dayOfYear(t) >= 3)
536 return 1900 + tm_year - 1;
538 else if (dayOfYear(t) > lastDayOfYear - 4)
540 if (dayOfYear(t) - dayOfWeek(t, beginOnMonday) > lastDayOfYear - 4)
541 return 1900 + tm_year + 1;
543 return 1900 + tm_year;
547 beginOfHour(time_t t)
549 const struct tm* tms = clocaltime(&t);
551 memcpy(&tmc, tms, sizeof(struct tm));
552 tmc.tm_sec = tmc.tm_min = 0;
560 const struct tm* tms = clocaltime(&t);
562 memcpy(&tmc, tms, sizeof(struct tm));
563 tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
569 beginOfWeek(time_t t, bool beginOnMonday)
571 const struct tm* tms;
572 for (tms = clocaltime(&t) ; tms->tm_wday != (beginOnMonday ? 1 : 0); )
574 t = sameTimeYesterday(t);
575 tms = clocaltime(&t);
578 memcpy(&tmc, tms, sizeof(struct tm));
579 tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
585 beginOfMonth(time_t t)
587 const struct tm* tms = clocaltime(&t);
589 memcpy(&tmc, tms, sizeof(struct tm));
591 tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
597 beginOfQuarter(time_t t)
599 const struct tm* tms = clocaltime(&t);
601 memcpy(&tmc, tms, sizeof(struct tm));
602 tmc.tm_mon = (tmc.tm_mon / 3) * 3;
604 tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
610 beginOfYear(time_t t)
612 const struct tm* tms = clocaltime(&t);
614 memcpy(&tmc, tms, sizeof(struct tm));
617 tmc.tm_sec = tmc.tm_min = tmc.tm_hour = 0;
623 hoursLater(int h, time_t t)
625 // I hope this is correct under all circumstances.
626 return t + h * ONEHOUR;
630 sameTimeNextDay(time_t t)
632 const struct tm* tms = clocaltime(&t);
634 memcpy(&tmc, tms, sizeof(struct tm));
637 if (mktime(&tmc) == -1)
638 qFatal("Error at %s", time2ISO(t).latin1());
643 sameTimeYesterday(time_t t)
645 const struct tm* tms = clocaltime(&t);
647 memcpy(&tmc, tms, sizeof(struct tm));
654 sameTimeNextWeek(time_t t)
656 const struct tm* tms = clocaltime(&t);
657 int weekday = tms->tm_wday;
660 t = sameTimeNextDay(t);
661 tms = clocaltime(&t);
662 } while (tms->tm_wday != weekday);
667 sameTimeLastWeek(time_t t)
669 const struct tm* tms = clocaltime(&t);
670 int weekday = tms->tm_wday;
673 t = sameTimeYesterday(t);
674 tms = clocaltime(&t);
675 } while (tms->tm_wday != weekday);
680 sameTimeNextMonth(time_t t)
682 const struct tm* tms = clocaltime(&t);
684 memcpy(&tmc, tms, sizeof(struct tm));
691 sameTimeNextQuarter(time_t t)
693 const struct tm* tms = clocaltime(&t);
695 memcpy(&tmc, tms, sizeof(struct tm));
702 sameTimeNextYear(time_t t)
704 const struct tm* tms = clocaltime(&t);
706 memcpy(&tmc, tms, sizeof(struct tm));
713 sameTimeLastYear(time_t t)
715 const struct tm* tms = clocaltime(&t);
717 memcpy(&tmc, tms, sizeof(struct tm));
726 const struct tm* tms = clocaltime(&t);
727 static char buf[128];
729 strftime(buf, 127, "%Y-%m-%d %H:%M:%S %Z", tms);
730 return QString::fromLocal8Bit(buf);
736 const struct tm* tms = clocaltime(&t);
737 static char buf[128];
739 strftime(buf, 127, "%Y-%m-%d-%H:%M:%S-%z", tms);
740 return QString::fromLocal8Bit(buf);
744 time2user(time_t t, const QString& timeFormat, bool localtime)
747 return i18n("undefined");
749 const struct tm* tms;
751 tms = clocaltime(&t);
755 static char buf[128];
757 strftime(buf, 127, timeFormat, tms);
758 return QString::fromLocal8Bit(buf);
764 const struct tm* tms = clocaltime(&t);
765 static char buf[128];
767 strftime(buf, 127, "%H:%M %Z", tms);
768 return QString::fromLocal8Bit(buf);
774 const struct tm* tms = clocaltime(&t);
775 static char buf[128];
777 strftime(buf, 127, "%Y-%m-%d", tms);
778 return QString::fromLocal8Bit(buf);
782 time2weekday(time_t t)
784 const struct tm* tms = clocaltime(&t);
785 static char buf[128];
787 strftime(buf, 127, "%A", tms);
788 return QString::fromLocal8Bit(buf);
792 addTimeToDate(time_t day, time_t hour)
795 const struct tm* tms = clocaltime(&day);
798 memcpy(&tmc, tms, sizeof(struct tm));
800 tmc.tm_hour = hour / (60 * 60);
801 tmc.tm_min = (hour / 60) % 60;
802 tmc.tm_sec = hour % 60;
809 date2time(const QString& date)
813 int y, m, d, hour, min, sec;
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)
824 if ((tz = getenv("TZ")) != 0)
828 if ((tz = timezone2tz(tZone)) == 0)
830 UtilityError = QString(i18n("Illegal timezone %1")).arg(tZone);
835 if (setenv("TZ", tz, 1) < 0)
836 qFatal("date2time: Ran out of space in environment section.");
840 else if (sscanf(date, "%d-%d-%d-%d:%d:%d",
841 &y, &m, &d, &hour, &min, &sec) == 6)
843 else if (sscanf(date, "%d-%d-%d-%d:%d", &y, &m, &d, &hour, &min) == 5)
848 else if (sscanf(date, "%d-%d-%d", &y, &m, &d) == 3)
851 hour = min = sec = 0;
855 qFatal("Illegal date: %s", date.latin1());
861 UtilityError = i18n("Year must be larger than 1969");
866 UtilityError = i18n("Month must be between 1 and 12");
871 UtilityError = i18n("Day must be between 1 and 31");
874 if (hour < 0 || hour > 23)
876 UtilityError = i18n("Hour must be between 0 and 23");
879 if (min < 0 || min > 59)
881 UtilityError = i18n("Minutes must be between 0 and 59");
884 if (sec < 0 || sec > 59)
886 UtilityError = i18n("Seconds must be between 0 and 59");
890 #if defined(__CYGWIN__) || (defined(__SVR4) && defined(__sun))
891 struct tm t = { sec, min, hour, d, m - 1, y - 1900, 0, 0, -1 };
893 struct tm t = { sec, min, hour, d, m - 1, y - 1900, 0, 0, -1, 0, 0 };
895 time_t localTime = mktime(&t);
899 if (!savedTZ.empty())
901 if (setenv("TZ", savedTZ.c_str(), 1) < 0)
902 qFatal("date2time: Ran out of space in environment section.");
914 return QDate(year(t), monthOfYear(t), dayOfMonth(t));
918 qdate2time(const QDate& d)
920 #if defined(__CYGWIN__) || (defined(__SVR4) && defined(__sun))
921 struct tm t = { 0, 0, 0, d.day(), d.month() - 1, d.year() - 1900,
924 struct tm t = { 0, 0, 0, d.day(), d.month() - 1, d.year() - 1900,
930 #if defined(__SVR4) && defined(__sun)
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).
938 int setenv(const char* var, const char* val, int ignore)
940 int varLen = strlen(var);
941 int valLen = strlen(val);
944 if ((buffer = static_cast<char*>(malloc(varLen + valLen + 2))) == NULL)
947 sprintf (buffer, "%s=%s", var, val);
949 return putenv(buffer) ? -1 : 0;
952 int unsetenv (const char *var)