kexidatetimeformatter.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "kexidatetimeformatter.h"
21
22#include <kdebug.h>
23#include <tdelocale.h>
24#include <tdeglobal.h>
25#include <kdatepicker.h>
26#include <kdatetbl.h>
27#include <klineedit.h>
28#include <tdepopupmenu.h>
29#include <kdatewidget.h>
30
32{
33 // use "short date" format system settings
35 TQString df( TDEGlobal::locale()->dateFormatShort() );
36 if (df.length()>2)
37 m_separator = df.mid(2,1);
38 else
39 m_separator = "-";
40 const int separatorLen = m_separator.length();
41 TQString yearMask("9999");
42 TQString yearDateFormat("yyyy"),
43 monthDateFormat("MM"),
44 dayDateFormat("dd"); //for setting up m_dateFormat
45 bool ok = df.length()>=8;
46 int yearpos, monthpos, daypos; //result of df.find()
47 if (ok) {//look at % variables
50 yearpos = df.find("%y", 0, false); //&y or %y
51 m_longYear = !(yearpos>=0 && df.mid(yearpos+1, 1)=="y");
52 if (!m_longYear) {
53 yearMask = "99";
54 yearDateFormat = "yy";
55 }
56 monthpos = df.find("%m", 0, true); //%m or %n
57 m_monthWithLeadingZero = true;
58 if (monthpos<0) {
59 monthpos = df.find("%n", 0, false);
60 m_monthWithLeadingZero = false;
61 monthDateFormat = "M";
62 }
63 daypos = df.find("%d", 0, true);//%d or %e
64 m_dayWithLeadingZero = true;
65 if (daypos<0) {
66 daypos = df.find("%e", 0, false);
67 m_dayWithLeadingZero = false;
68 dayDateFormat = "d";
69 }
70 ok = (yearpos>=0 && monthpos>=0 && daypos>=0);
71 }
72 m_order = TQDateEdit::YMD; //default
73 if (ok) {
74 if (yearpos<monthpos && monthpos<daypos) {
75 //will be set in "default: YMD"
76 }
77 else if (yearpos<daypos && daypos<monthpos) {
78 m_order = TQDateEdit::YDM;
81 m_inputMask = TQString("%1%299%399").arg(yearMask).arg(m_separator).arg(m_separator);
82 m_qtFormat = yearDateFormat+m_separator+dayDateFormat+m_separator+monthDateFormat;
83 m_yearpos = 0;
84 m_daypos = yearMask.length()+separatorLen;
85 m_monthpos = m_daypos+2+separatorLen;
86 }
87 else if (daypos<monthpos && monthpos<yearpos) {
88 m_order = TQDateEdit::DMY;
89 m_inputMask = TQString("99%199%2%3").arg(m_separator).arg(m_separator).arg(yearMask);
90 m_qtFormat = dayDateFormat+m_separator+monthDateFormat+m_separator+yearDateFormat;
91 m_daypos = 0;
92 m_monthpos = 2+separatorLen;
93 m_yearpos = m_monthpos+2+separatorLen;
94 }
95 else if (monthpos<daypos && daypos<yearpos) {
96 m_order = TQDateEdit::MDY;
97 m_inputMask = TQString("99%199%2%3").arg(m_separator).arg(m_separator).arg(yearMask);
98 m_qtFormat = monthDateFormat+m_separator+dayDateFormat+m_separator+yearDateFormat;
99 m_monthpos = 0;
100 m_daypos = 2+separatorLen;
101 m_yearpos = m_daypos+2+separatorLen;
102 }
103 else
104 ok = false;
105 }
106 if (!ok || m_order == TQDateEdit::YMD) {//default: YMD
107 m_inputMask = TQString("%1%299%399").arg(yearMask).arg(m_separator).arg(m_separator);
108 m_qtFormat = yearDateFormat+m_separator+monthDateFormat+m_separator+dayDateFormat;
109 m_yearpos = 0;
110 m_monthpos = yearMask.length()+separatorLen;
111 m_daypos = m_monthpos+2+separatorLen;
112 }
113 m_inputMask += ";_";
114}
115
119
120TQDate KexiDateFormatter::stringToDate( const TQString& str ) const
121{
122 bool ok = true;
123 int year = str.mid(m_yearpos, m_longYear ? 4 : 2).toInt(&ok);
124 if (!ok)
125 return TQDate();
126 if (year < 30) {//2000..2029
127 year = 2000 + year;
128 }
129 else if (year < 100) {//1930..1999
130 year = 1900 + year;
131 }
132
133 int month = str.mid(m_monthpos, 2).toInt(&ok);
134 if (!ok)
135 return TQDate();
136
137 int day = str.mid(m_daypos, 2).toInt(&ok);
138 if (!ok)
139 return TQDate();
140
141 TQDate date(year, month, day);
142 if (!date.isValid())
143 return TQDate();
144 return date;
145}
146
147TQVariant KexiDateFormatter::stringToVariant( const TQString& str ) const
148{
149 if (isEmpty(str))
150 return TQVariant();
151 const TQDate date( stringToDate( str ) );
152 if (date.isValid())
153 return date;
154 return TQVariant();
155}
156
157bool KexiDateFormatter::isEmpty( const TQString& str ) const
158{
159 TQString s(str);
160 return s.replace(m_separator,"").stripWhiteSpace().isEmpty();
161}
162
163TQString KexiDateFormatter::dateToString( const TQDate& date ) const
164{
165 return date.toString(m_qtFormat);
166}
167
168//------------------------------------------------
169
171: m_hmsRegExp( new TQRegExp("(\\d*):(\\d*):(\\d*).*( am| pm){,1}", false) )
172 , m_hmRegExp( new TQRegExp("(\\d*):(\\d*).*( am| pm){,1}", false) )
173{
174 TQString tf( TDEGlobal::locale()->timeFormat() );
175 //m_hourpos, m_minpos, m_secpos; are result of tf.find()
176 TQString hourVariable, minVariable, secVariable;
177
178 //detect position of HOUR section: find %H or %k or %I or %l
179 m_24h = true;
180 m_hoursWithLeadingZero = true;
181 m_hourpos = tf.find("%H", 0, true);
182 if (m_hourpos>=0) {
183 m_24h = true;
184 m_hoursWithLeadingZero = true;
185 }
186 else {
187 m_hourpos = tf.find("%k", 0, true);
188 if (m_hourpos>=0) {
189 m_24h = true;
190 m_hoursWithLeadingZero = false;
191 }
192 else {
193 m_hourpos = tf.find("%I", 0, true);
194 if (m_hourpos>=0) {
195 m_24h = false;
196 m_hoursWithLeadingZero = true;
197 }
198 else {
199 m_hourpos = tf.find("%l", 0, true);
200 if (m_hourpos>=0) {
201 m_24h = false;
202 m_hoursWithLeadingZero = false;
203 }
204 }
205 }
206 }
207 m_minpos = tf.find("%M", 0, true);
208 m_secpos = tf.find("%S", 0, true); //can be -1
209 m_ampmpos = tf.find("%p", 0, true); //can be -1
210
211 if (m_hourpos<0 || m_minpos<0) {
212 //set default: hr and min are needed, sec are optional
213 tf = "%H:%M:%S";
214 m_24h = true;
215 m_hoursWithLeadingZero = false;
216 m_hourpos = 0;
217 m_minpos = 3;
218 m_secpos = m_minpos + 3;
219 m_ampmpos = -1;
220 }
221 hourVariable = tf.mid(m_hourpos, 2);
222
223 m_inputMask = tf;
224// m_inputMask.replace( hourVariable, "00" );
225// m_inputMask.replace( "%M", "00" );
226// m_inputMask.replace( "%S", "00" ); //optional
227 m_inputMask.replace( hourVariable, "99" );
228 m_inputMask.replace( "%M", "99" );
229 m_inputMask.replace( "%S", "00" ); //optional
230 m_inputMask.replace( "%p", "AA" ); //am or pm
231 m_inputMask += ";_";
232
233 m_outputFormat = tf;
234}
235
237{
238 delete m_hmsRegExp;
239 delete m_hmRegExp;
240}
241
242TQTime KexiTimeFormatter::stringToTime( const TQString& str ) const
243{
244 int hour, min, sec;
245 bool pm = false;
246
247 bool tryWithoutSeconds = true;
248 if (m_secpos>=0) {
249 if (-1 != m_hmsRegExp->search(str)) {
250 hour = m_hmsRegExp->cap(1).toInt();
251 min = m_hmsRegExp->cap(2).toInt();
252 sec = m_hmsRegExp->cap(3).toInt();
253 if (m_ampmpos >= 0 && m_hmsRegExp->numCaptures()>3)
254 pm = m_hmsRegExp->cap(4).stripWhiteSpace().lower()=="pm";
255 tryWithoutSeconds = false;
256 }
257 }
258 if (tryWithoutSeconds) {
259 if (-1 == m_hmRegExp->search(str))
260 return TQTime(99,0,0);
261 hour = m_hmRegExp->cap(1).toInt();
262 min = m_hmRegExp->cap(2).toInt();
263 sec = 0;
264 if (m_ampmpos >= 0 && m_hmRegExp->numCaptures()>2)
265 pm = m_hmsRegExp->cap(4).lower()=="pm";
266 }
267
268 if (pm && hour < 12)
269 hour += 12; //PM
270 return TQTime(hour, min, sec);
271}
272
273TQVariant KexiTimeFormatter::stringToVariant( const TQString& str )
274{
275 if (isEmpty( str ))
276 return TQVariant();
277 const TQTime time( stringToTime( str ) );
278 if (time.isValid())
279 return time;
280 return TQVariant();
281}
282
283bool KexiTimeFormatter::isEmpty( const TQString& str ) const
284{
285 TQString s(str);
286 return s.replace(':',"").stripWhiteSpace().isEmpty();
287}
288
289TQString KexiTimeFormatter::timeToString( const TQTime& time ) const
290{
291 if (!time.isValid())
292 return TQString();
293
294 TQString s(m_outputFormat);
295 if (m_24h) {
296 if (m_hoursWithLeadingZero)
297 s.replace( "%H", TQString::fromLatin1(time.hour()<10 ? "0" : "") + TQString::number(time.hour()) );
298 else
299 s.replace( "%k", TQString::number(time.hour()) );
300 }
301 else {
302 int time12 = (time.hour()>12) ? (time.hour()-12) : time.hour();
303 if (m_hoursWithLeadingZero)
304 s.replace( "%I", TQString::fromLatin1(time12<10 ? "0" : "") + TQString::number(time12) );
305 else
306 s.replace( "%l", TQString::number(time12) );
307 }
308 s.replace( "%M", TQString::fromLatin1(time.minute()<10 ? "0" : "") + TQString::number(time.minute()) );
309 if (m_secpos>=0)
310 s.replace( "%S", TQString::fromLatin1(time.second()<10 ? "0" : "") + TQString::number(time.second()) );
311 if (m_ampmpos>=0)
312 s.replace( "%p", TDEGlobal::locale()->translate( time.hour()>=12 ? "pm" : "am") );
313 return s;
314}
315
316//------------------------------------------------
317
318TQString dateTimeInputMask(const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter)
319{
320 TQString mask(dateFormatter.inputMask());
321 mask.truncate(dateFormatter.inputMask().length()-2);
322 return mask + " " + timeFormatter.inputMask();
323}
324
325TQDateTime stringToDateTime(
326 const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter, const TQString& str)
327{
328 TQString s( str.stripWhiteSpace() );
329 const int timepos = s.find(" ");
330 const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(s.mid(timepos+1)); //.replace(':',"").stripWhiteSpace().isEmpty();
331 if (emptyTime)
332 s = s.left(timepos);
333 if (timepos>0 && !emptyTime) {
334 return TQDateTime(
335 dateFormatter.stringToDate( s.left(timepos) ),
336 timeFormatter.stringToTime( s.mid(timepos+1) )
337 );
338 }
339 else {
340 return TQDateTime(
341 dateFormatter.stringToDate( s ),
342 TQTime(0,0,0)
343 );
344 }
345}
346
347bool dateTimeIsEmpty( const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter,
348 const TQString& str )
349{
350 int timepos = str.find(" ");
351 const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos+1)); //s.mid(timepos+1).replace(':',"").stripWhiteSpace().isEmpty();
352 return (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) //s.left(timepos).replace(m_dateFormatter.separator(), "").stripWhiteSpace().isEmpty()
353 && emptyTime);
354}
355
356bool dateTimeIsValid( const KexiDateFormatter& dateFormatter,
357 const KexiTimeFormatter& timeFormatter, const TQString& str )
358{
359 int timepos = str.find(" ");
360 const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos+1)); //s.mid(timepos+1).replace(':',"").stripWhiteSpace().isEmpty();
361 if (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) // s.left(timepos).replace(m_dateFormatter.separator(), "").stripWhiteSpace().isEmpty()
362 && emptyTime)
363 //empty date/time is valid
364 return true;
365 return timepos>=0 && dateFormatter.stringToDate( str.left(timepos) ).isValid()
366 && (emptyTime /*date without time is also valid*/ || timeFormatter.stringToTime( str.mid(timepos+1) ).isValid());
367}
Date formatter used by KexiDateTableEdit and KexiDateTimeTableEdit.
bool m_longYear
4 or 2 digits
TQDateEdit::Order m_order
Order of date sections.
int m_yearpos
Used in stringToDate() to convert string back to TQDate.
TQString m_qtFormat
Date format used in dateToString()
TQString dateToString(const TQDate &date) const
Converts date to string using predefined settings.
TQVariant stringToVariant(const TQString &str) const
bool isEmpty(const TQString &str) const
TQDate stringToDate(const TQString &str) const
Converts string str to date using predefined settings.
TQString m_inputMask
Input mask generated using the formatter settings. Can be used in TQLineEdit::setInputMask().
KexiDateFormatter()
Creates new formatter with KDE setting for "short date".
TQString inputMask() const
~KexiDateFormatter()
Creates new formatter with given settings.
Time formatter used by KexiTimeTableEdit and KexiDateTimeTableEdit Following time formats are allowed...
~KexiTimeFormatter()
Creates new formatter with given settings.
TQTime stringToTime(const TQString &str) const
converts string str to time using predefined settings
TQString timeToString(const TQTime &time) const
converts time to string using predefined settings
bool isEmpty(const TQString &str) const
TQString m_inputMask
Input mask generated using the formatter settings. Can be used in TQLineEdit::setInputMask().
int m_hourpos
Used in stringToTime() to convert string back to TQTime.
TQVariant stringToVariant(const TQString &str)
TQString m_outputFormat
Time format used in timeToString(). Notation from TDELocale::setTimeFormat() is used.
KexiTimeFormatter()
Creates new formatter with KDE setting for time.
TQString inputMask() const