2 * alarmtime.cpp - conversion functions for alarm times
4 * Copyright © 2007-2012 by David Jarvie <djarvie@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "alarmtime.h"
22 #include "preferences.h"
24 #include <kalarmcal/datetime.h>
26 #include <ksystemtimezone.h>
30 #include <qapplication.h>
32 using namespace KAlarmCal
;
34 int AlarmTime::mTimeHourPos
= -2;
36 /******************************************************************************
37 * Return the alarm time text in the form "date time".
39 QString
AlarmTime::alarmTimeText(const DateTime
& dateTime
)
41 if (!dateTime
.isValid())
42 return i18nc("@info/plain Alarm never occurs", "Never");
43 KLocale
* locale
= KGlobal::locale();
44 KDateTime kdt
= dateTime
.effectiveKDateTime().toTimeSpec(Preferences::timeZone());
45 QString dateTimeText
= locale
->formatDate(kdt
.date(), KLocale::ShortDate
);
46 if (!dateTime
.isDateOnly()
47 || (!dateTime
.isClockTime() && kdt
.utcOffset() != dateTime
.utcOffset()))
49 // Display the time of day if it's a date/time value, or if it's
50 // a date-only value but it's in a different time zone
51 dateTimeText
+= QLatin1Char(' ');
52 QString time
= locale
->formatTime(kdt
.time());
53 if (mTimeHourPos
== -2)
55 // Initialise the position of the hour within the time string, if leading
56 // zeroes are omitted, so that displayed times can be aligned with each other.
57 mTimeHourPos
= -1; // default = alignment isn't possible/sensible
58 if (QApplication::isLeftToRight()) // don't try to align right-to-left languages
60 QString fmt
= locale
->timeFormat();
61 int i
= fmt
.indexOf(QRegExp("%[kl]")); // check if leading zeroes are omitted
62 if (i
>= 0 && i
== fmt
.indexOf(QLatin1Char('%'))) // and whether the hour is first
63 mTimeHourPos
= i
; // yes, so need to align
66 if (mTimeHourPos
>= 0 && (int)time
.length() > mTimeHourPos
+ 1
67 && time
[mTimeHourPos
].isDigit() && !time
[mTimeHourPos
+ 1].isDigit())
68 dateTimeText
+= QLatin1Char('~'); // improve alignment of times with no leading zeroes
71 return dateTimeText
+ QLatin1Char(' ');
74 /******************************************************************************
75 * Return the time-to-alarm text.
77 QString
AlarmTime::timeToAlarmText(const DateTime
& dateTime
)
79 if (!dateTime
.isValid())
80 return i18nc("@info/plain Alarm never occurs", "Never");
81 KDateTime now
= KDateTime::currentUtcDateTime();
82 if (dateTime
.isDateOnly())
84 int days
= now
.date().daysTo(dateTime
.date());
85 // xgettext: no-c-format
86 return i18nc("@info/plain n days", "%1d", days
);
88 int mins
= (now
.secsTo(dateTime
.effectiveKDateTime()) + 59) / 60;
91 char minutes
[3] = "00";
92 minutes
[0] = (mins
%60) / 10 + '0';
93 minutes
[1] = (mins
%60) % 10 + '0';
95 return i18nc("@info/plain hours:minutes", "%1:%2", mins
/60, minutes
);
96 int days
= mins
/ (24*60);
97 mins
= mins
% (24*60);
98 return i18nc("@info/plain days hours:minutes", "%1d %2:%3", days
, mins
/60, minutes
);
101 /******************************************************************************
102 * Convert a date/time specification string into a local date/time or date value.
104 * timeString = in the form [[[yyyy-]mm-]dd-]hh:mm [TZ] or yyyy-mm-dd [TZ].
105 * dateTime = receives converted date/time value.
106 * defaultDt = default date/time used for missing parts of timeString, or null
107 * to use current date/time.
108 * allowTZ = whether to allow a time zone specifier in timeString.
109 * Reply = true if successful.
111 bool AlarmTime::convertTimeString(const QByteArray
& timeString
, KDateTime
& dateTime
, const KDateTime
& defaultDt
, bool allowTZ
)
113 #define MAX_DT_LEN 19
114 int i
= timeString
.indexOf(' ');
115 if (i
> MAX_DT_LEN
|| (i
>= 0 && !allowTZ
))
117 QString zone
= (i
>= 0) ? QString::fromLatin1(timeString
.mid(i
)) : QString();
118 char timeStr
[MAX_DT_LEN
+1];
119 strcpy(timeStr
, timeString
.left(i
>= 0 ? i
: MAX_DT_LEN
));
120 int dt
[5] = { -1, -1, -1, -1, -1 };
124 // Get the minute value
125 if ((s
= strchr(timeStr
, ':')) == 0)
131 dt
[4] = strtoul(s
, &end
, 10);
132 if (end
== s
|| *end
|| dt
[4] >= 60)
134 // Get the hour value
135 if ((s
= strrchr(timeStr
, '-')) == 0)
139 dt
[3] = strtoul(s
, &end
, 10);
140 if (end
== s
|| *end
|| dt
[3] >= 24)
148 if ((s
= strrchr(timeStr
, '-')) == 0)
152 dt
[2] = strtoul(s
, &end
, 10);
153 if (end
== s
|| *end
|| dt
[2] == 0 || dt
[2] > 31)
157 // Get the month value
158 if ((s
= strrchr(timeStr
, '-')) == 0)
162 dt
[1] = strtoul(s
, &end
, 10);
163 if (end
== s
|| *end
|| dt
[1] == 0 || dt
[1] > 12)
167 // Get the year value
168 dt
[0] = strtoul(timeStr
, &end
, 10);
169 if (end
== timeStr
|| *end
)
177 date
= QDate(dt
[0], dt
[1], dt
[2]);
181 // No time was specified, so the full date must have been specified
182 if (dt
[0] < 0 || !date
.isValid())
184 dateTime
= applyTimeZone(zone
, date
, time
, false, defaultDt
);
188 // Compile the values into a date/time structure
189 time
.setHMS(dt
[3], dt
[4], 0);
192 // Some or all of the date was omitted.
193 // Use the default date/time if provided.
194 if (defaultDt
.isValid())
196 dt
[0] = defaultDt
.date().year();
198 (dt
[1] < 0 ? defaultDt
.date().month() : dt
[1]),
199 (dt
[2] < 0 ? defaultDt
.date().day() : dt
[2]));
202 date
.setYMD(2000, 1, 1); // temporary substitute for date
204 dateTime
= applyTimeZone(zone
, date
, time
, true, defaultDt
);
205 if (!dateTime
.isValid())
209 // Some or all of the date was omitted.
210 // Use the current date in the specified time zone as default.
211 KDateTime now
= KDateTime::currentDateTime(dateTime
.timeSpec());
212 date
= dateTime
.date();
213 date
.setYMD(now
.date().year(),
214 (dt
[1] < 0 ? now
.date().month() : dt
[1]),
215 (dt
[2] < 0 ? now
.date().day() : dt
[2]));
218 if (noDate
&& time
< now
.time())
219 date
= date
.addDays(1);
220 dateTime
.setDate(date
);
223 return dateTime
.isValid();
226 /******************************************************************************
227 * Convert a time zone specifier string and apply it to a given date and/or time.
228 * The time zone specifier is a system time zone name, e.g. "Europe/London",
229 * "UTC" or "Clock". If no time zone is specified, it defaults to the local time
231 * If 'defaultDt' is valid, it supplies the time spec and default date.
233 KDateTime
AlarmTime::applyTimeZone(const QString
& tzstring
, const QDate
& date
, const QTime
& time
,
234 bool haveTime
, const KDateTime
& defaultDt
)
237 KDateTime::Spec spec
= KDateTime::LocalZone
;
238 QString zone
= tzstring
.trimmed();
241 if (zone
== QLatin1String("Clock"))
242 spec
= KDateTime::ClockTime
;
243 else if (zone
== QLatin1String("UTC"))
244 spec
= KDateTime::UTC
;
247 KTimeZone tz
= KSystemTimeZones::zone(zone
);
248 error
= !tz
.isValid();
253 else if (defaultDt
.isValid())
254 spec
= defaultDt
.timeSpec();
261 // It's a time without a date
262 if (defaultDt
.isValid())
263 result
= KDateTime(defaultDt
.date(), time
, spec
);
264 else if (spec
== KDateTime::LocalZone
|| spec
== KDateTime::ClockTime
)
265 result
= KDateTime(KDateTime::currentLocalDate(), time
, spec
);
269 // It's a date and time
270 result
= KDateTime(date
, time
, spec
);
274 // It's a date without a time
275 result
= KDateTime(date
, spec
);