|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2006 Jaroslaw Staniek <js@iidea.pl>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this program; see the file COPYING. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kexidatetimeformatter.h"
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kglobal.h>
|
|
|
|
#include <kdatepicker.h>
|
|
|
|
#include <kdatetbl.h>
|
|
|
|
#include <klineedit.h>
|
|
|
|
#include <kpopupmenu.h>
|
|
|
|
#include <kdatewidget.h>
|
|
|
|
|
|
|
|
KexiDateFormatter::KexiDateFormatter()
|
|
|
|
{
|
|
|
|
// use "short date" format system settings
|
|
|
|
//! @todo allow to override the format using column property and/or global app settings
|
|
|
|
TQString df( KGlobal::locale()->dateFormatShort() );
|
|
|
|
if (df.length()>2)
|
|
|
|
m_separator = df.mid(2,1);
|
|
|
|
else
|
|
|
|
m_separator = "-";
|
|
|
|
const int separatorLen = m_separator.length();
|
|
|
|
TQString yearMask("9999");
|
|
|
|
TQString yearDateFormat("yyyy"),
|
|
|
|
monthDateFormat("MM"),
|
|
|
|
dayDateFormat("dd"); //for setting up m_dateFormat
|
|
|
|
bool ok = df.length()>=8;
|
|
|
|
int yearpos, monthpos, daypos; //result of df.find()
|
|
|
|
if (ok) {//look at % variables
|
|
|
|
//! @todo more variables are possible here, see void KLocale::setDateFormatShort() docs
|
|
|
|
//! http://developer.kde.org/documentation/library/3.5-api/tdelibs-apidocs/tdecore/html/classKLocale.html#a59
|
|
|
|
yearpos = df.find("%y", 0, false); //&y or %y
|
|
|
|
m_longYear = !(yearpos>=0 && df.mid(yearpos+1, 1)=="y");
|
|
|
|
if (!m_longYear) {
|
|
|
|
yearMask = "99";
|
|
|
|
yearDateFormat = "yy";
|
|
|
|
}
|
|
|
|
monthpos = df.find("%m", 0, true); //%m or %n
|
|
|
|
m_monthWithLeadingZero = true;
|
|
|
|
if (monthpos<0) {
|
|
|
|
monthpos = df.find("%n", 0, false);
|
|
|
|
m_monthWithLeadingZero = false;
|
|
|
|
monthDateFormat = "M";
|
|
|
|
}
|
|
|
|
daypos = df.find("%d", 0, true);//%d or %e
|
|
|
|
m_dayWithLeadingZero = true;
|
|
|
|
if (daypos<0) {
|
|
|
|
daypos = df.find("%e", 0, false);
|
|
|
|
m_dayWithLeadingZero = false;
|
|
|
|
dayDateFormat = "d";
|
|
|
|
}
|
|
|
|
ok = (yearpos>=0 && monthpos>=0 && daypos>=0);
|
|
|
|
}
|
|
|
|
m_order = TQDateEdit::YMD; //default
|
|
|
|
if (ok) {
|
|
|
|
if (yearpos<monthpos && monthpos<daypos) {
|
|
|
|
//will be set in "default: YMD"
|
|
|
|
}
|
|
|
|
else if (yearpos<daypos && daypos<monthpos) {
|
|
|
|
m_order = TQDateEdit::YDM;
|
|
|
|
//! @todo use TQRegExp (to replace %Y by %1, etc.) instead of hardcoded "%1%299%399"
|
|
|
|
//! because df may contain also other characters
|
|
|
|
m_inputMask = TQString("%1%299%399").arg(yearMask).arg(m_separator).arg(m_separator);
|
|
|
|
m_qtFormat = yearDateFormat+m_separator+dayDateFormat+m_separator+monthDateFormat;
|
|
|
|
m_yearpos = 0;
|
|
|
|
m_daypos = yearMask.length()+separatorLen;
|
|
|
|
m_monthpos = m_daypos+2+separatorLen;
|
|
|
|
}
|
|
|
|
else if (daypos<monthpos && monthpos<yearpos) {
|
|
|
|
m_order = TQDateEdit::DMY;
|
|
|
|
m_inputMask = TQString("99%199%2%3").arg(m_separator).arg(m_separator).arg(yearMask);
|
|
|
|
m_qtFormat = dayDateFormat+m_separator+monthDateFormat+m_separator+yearDateFormat;
|
|
|
|
m_daypos = 0;
|
|
|
|
m_monthpos = 2+separatorLen;
|
|
|
|
m_yearpos = m_monthpos+2+separatorLen;
|
|
|
|
}
|
|
|
|
else if (monthpos<daypos && daypos<yearpos) {
|
|
|
|
m_order = TQDateEdit::MDY;
|
|
|
|
m_inputMask = TQString("99%199%2%3").arg(m_separator).arg(m_separator).arg(yearMask);
|
|
|
|
m_qtFormat = monthDateFormat+m_separator+dayDateFormat+m_separator+yearDateFormat;
|
|
|
|
m_monthpos = 0;
|
|
|
|
m_daypos = 2+separatorLen;
|
|
|
|
m_yearpos = m_daypos+2+separatorLen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (!ok || m_order == TQDateEdit::YMD) {//default: YMD
|
|
|
|
m_inputMask = TQString("%1%299%399").arg(yearMask).arg(m_separator).arg(m_separator);
|
|
|
|
m_qtFormat = yearDateFormat+m_separator+monthDateFormat+m_separator+dayDateFormat;
|
|
|
|
m_yearpos = 0;
|
|
|
|
m_monthpos = yearMask.length()+separatorLen;
|
|
|
|
m_daypos = m_monthpos+2+separatorLen;
|
|
|
|
}
|
|
|
|
m_inputMask += ";_";
|
|
|
|
}
|
|
|
|
|
|
|
|
KexiDateFormatter::~KexiDateFormatter()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDate KexiDateFormatter::stringToDate( const TQString& str ) const
|
|
|
|
{
|
|
|
|
bool ok = true;
|
|
|
|
int year = str.mid(m_yearpos, m_longYear ? 4 : 2).toInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
return TQDate();
|
|
|
|
if (year < 30) {//2000..2029
|
|
|
|
year = 2000 + year;
|
|
|
|
}
|
|
|
|
else if (year < 100) {//1930..1999
|
|
|
|
year = 1900 + year;
|
|
|
|
}
|
|
|
|
|
|
|
|
int month = str.mid(m_monthpos, 2).toInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
return TQDate();
|
|
|
|
|
|
|
|
int day = str.mid(m_daypos, 2).toInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
return TQDate();
|
|
|
|
|
|
|
|
TQDate date(year, month, day);
|
|
|
|
if (!date.isValid())
|
|
|
|
return TQDate();
|
|
|
|
return date;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQVariant KexiDateFormatter::stringToVariant( const TQString& str ) const
|
|
|
|
{
|
|
|
|
if (isEmpty(str))
|
|
|
|
return TQVariant();
|
|
|
|
const TQDate date( stringToDate( str ) );
|
|
|
|
if (date.isValid())
|
|
|
|
return date;
|
|
|
|
return TQVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KexiDateFormatter::isEmpty( const TQString& str ) const
|
|
|
|
{
|
|
|
|
TQString s(str);
|
|
|
|
return s.replace(m_separator,"").stripWhiteSpace().isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KexiDateFormatter::dateToString( const TQDate& date ) const
|
|
|
|
{
|
|
|
|
return date.toString(m_qtFormat);
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
KexiTimeFormatter::KexiTimeFormatter()
|
|
|
|
: m_hmsRegExp( new TQRegExp("(\\d*):(\\d*):(\\d*).*( am| pm){,1}", false/*!CS*/) )
|
|
|
|
, m_hmRegExp( new TQRegExp("(\\d*):(\\d*).*( am| pm){,1}", false/*!CS*/) )
|
|
|
|
{
|
|
|
|
TQString tf( KGlobal::locale()->timeFormat() );
|
|
|
|
//m_hourpos, m_minpos, m_secpos; are result of tf.find()
|
|
|
|
TQString hourVariable, minVariable, secVariable;
|
|
|
|
|
|
|
|
//detect position of HOUR section: find %H or %k or %I or %l
|
|
|
|
m_24h = true;
|
|
|
|
m_hoursWithLeadingZero = true;
|
|
|
|
m_hourpos = tf.find("%H", 0, true);
|
|
|
|
if (m_hourpos>=0) {
|
|
|
|
m_24h = true;
|
|
|
|
m_hoursWithLeadingZero = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_hourpos = tf.find("%k", 0, true);
|
|
|
|
if (m_hourpos>=0) {
|
|
|
|
m_24h = true;
|
|
|
|
m_hoursWithLeadingZero = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_hourpos = tf.find("%I", 0, true);
|
|
|
|
if (m_hourpos>=0) {
|
|
|
|
m_24h = false;
|
|
|
|
m_hoursWithLeadingZero = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_hourpos = tf.find("%l", 0, true);
|
|
|
|
if (m_hourpos>=0) {
|
|
|
|
m_24h = false;
|
|
|
|
m_hoursWithLeadingZero = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_minpos = tf.find("%M", 0, true);
|
|
|
|
m_secpos = tf.find("%S", 0, true); //can be -1
|
|
|
|
m_ampmpos = tf.find("%p", 0, true); //can be -1
|
|
|
|
|
|
|
|
if (m_hourpos<0 || m_minpos<0) {
|
|
|
|
//set default: hr and min are needed, sec are optional
|
|
|
|
tf = "%H:%M:%S";
|
|
|
|
m_24h = true;
|
|
|
|
m_hoursWithLeadingZero = false;
|
|
|
|
m_hourpos = 0;
|
|
|
|
m_minpos = 3;
|
|
|
|
m_secpos = m_minpos + 3;
|
|
|
|
m_ampmpos = -1;
|
|
|
|
}
|
|
|
|
hourVariable = tf.mid(m_hourpos, 2);
|
|
|
|
|
|
|
|
m_inputMask = tf;
|
|
|
|
// m_inputMask.replace( hourVariable, "00" );
|
|
|
|
// m_inputMask.replace( "%M", "00" );
|
|
|
|
// m_inputMask.replace( "%S", "00" ); //optional
|
|
|
|
m_inputMask.replace( hourVariable, "99" );
|
|
|
|
m_inputMask.replace( "%M", "99" );
|
|
|
|
m_inputMask.replace( "%S", "00" ); //optional
|
|
|
|
m_inputMask.replace( "%p", "AA" ); //am or pm
|
|
|
|
m_inputMask += ";_";
|
|
|
|
|
|
|
|
m_outputFormat = tf;
|
|
|
|
}
|
|
|
|
|
|
|
|
KexiTimeFormatter::~KexiTimeFormatter()
|
|
|
|
{
|
|
|
|
delete m_hmsRegExp;
|
|
|
|
delete m_hmRegExp;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQTime KexiTimeFormatter::stringToTime( const TQString& str ) const
|
|
|
|
{
|
|
|
|
int hour, min, sec;
|
|
|
|
bool pm = false;
|
|
|
|
|
|
|
|
bool tryWithoutSeconds = true;
|
|
|
|
if (m_secpos>=0) {
|
|
|
|
if (-1 != m_hmsRegExp->search(str)) {
|
|
|
|
hour = m_hmsRegExp->cap(1).toInt();
|
|
|
|
min = m_hmsRegExp->cap(2).toInt();
|
|
|
|
sec = m_hmsRegExp->cap(3).toInt();
|
|
|
|
if (m_ampmpos >= 0 && m_hmsRegExp->numCaptures()>3)
|
|
|
|
pm = m_hmsRegExp->cap(4).stripWhiteSpace().lower()=="pm";
|
|
|
|
tryWithoutSeconds = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tryWithoutSeconds) {
|
|
|
|
if (-1 == m_hmRegExp->search(str))
|
|
|
|
return TQTime(99,0,0);
|
|
|
|
hour = m_hmRegExp->cap(1).toInt();
|
|
|
|
min = m_hmRegExp->cap(2).toInt();
|
|
|
|
sec = 0;
|
|
|
|
if (m_ampmpos >= 0 && m_hmRegExp->numCaptures()>2)
|
|
|
|
pm = m_hmsRegExp->cap(4).lower()=="pm";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pm && hour < 12)
|
|
|
|
hour += 12; //PM
|
|
|
|
return TQTime(hour, min, sec);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQVariant KexiTimeFormatter::stringToVariant( const TQString& str )
|
|
|
|
{
|
|
|
|
if (isEmpty( str ))
|
|
|
|
return TQVariant();
|
|
|
|
const TQTime time( stringToTime( str ) );
|
|
|
|
if (time.isValid())
|
|
|
|
return time;
|
|
|
|
return TQVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KexiTimeFormatter::isEmpty( const TQString& str ) const
|
|
|
|
{
|
|
|
|
TQString s(str);
|
|
|
|
return s.replace(':',"").stripWhiteSpace().isEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KexiTimeFormatter::timeToString( const TQTime& time ) const
|
|
|
|
{
|
|
|
|
if (!time.isValid())
|
|
|
|
return TQString();
|
|
|
|
|
|
|
|
TQString s(m_outputFormat);
|
|
|
|
if (m_24h) {
|
|
|
|
if (m_hoursWithLeadingZero)
|
|
|
|
s.replace( "%H", TQString::fromLatin1(time.hour()<10 ? "0" : "") + TQString::number(time.hour()) );
|
|
|
|
else
|
|
|
|
s.replace( "%k", TQString::number(time.hour()) );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int time12 = (time.hour()>12) ? (time.hour()-12) : time.hour();
|
|
|
|
if (m_hoursWithLeadingZero)
|
|
|
|
s.replace( "%I", TQString::fromLatin1(time12<10 ? "0" : "") + TQString::number(time12) );
|
|
|
|
else
|
|
|
|
s.replace( "%l", TQString::number(time12) );
|
|
|
|
}
|
|
|
|
s.replace( "%M", TQString::fromLatin1(time.minute()<10 ? "0" : "") + TQString::number(time.minute()) );
|
|
|
|
if (m_secpos>=0)
|
|
|
|
s.replace( "%S", TQString::fromLatin1(time.second()<10 ? "0" : "") + TQString::number(time.second()) );
|
|
|
|
if (m_ampmpos>=0)
|
|
|
|
s.replace( "%p", KGlobal::locale()->translate( time.hour()>=12 ? "pm" : "am") );
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------
|
|
|
|
|
|
|
|
TQString dateTimeInputMask(const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter)
|
|
|
|
{
|
|
|
|
TQString mask(dateFormatter.inputMask());
|
|
|
|
mask.truncate(dateFormatter.inputMask().length()-2);
|
|
|
|
return mask + " " + timeFormatter.inputMask();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDateTime stringToDateTime(
|
|
|
|
const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter, const TQString& str)
|
|
|
|
{
|
|
|
|
TQString s( str.stripWhiteSpace() );
|
|
|
|
const int timepos = s.find(" ");
|
|
|
|
const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(s.mid(timepos+1)); //.replace(':',"").stripWhiteSpace().isEmpty();
|
|
|
|
if (emptyTime)
|
|
|
|
s = s.left(timepos);
|
|
|
|
if (timepos>0 && !emptyTime) {
|
|
|
|
return TQDateTime(
|
|
|
|
dateFormatter.stringToDate( s.left(timepos) ),
|
|
|
|
timeFormatter.stringToTime( s.mid(timepos+1) )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return TQDateTime(
|
|
|
|
dateFormatter.stringToDate( s ),
|
|
|
|
TQTime(0,0,0)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dateTimeIsEmpty( const KexiDateFormatter& dateFormatter, const KexiTimeFormatter& timeFormatter,
|
|
|
|
const TQString& str )
|
|
|
|
{
|
|
|
|
int timepos = str.find(" ");
|
|
|
|
const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos+1)); //s.mid(timepos+1).replace(':',"").stripWhiteSpace().isEmpty();
|
|
|
|
return (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) //s.left(timepos).replace(m_dateFormatter.separator(), "").stripWhiteSpace().isEmpty()
|
|
|
|
&& emptyTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dateTimeIsValid( const KexiDateFormatter& dateFormatter,
|
|
|
|
const KexiTimeFormatter& timeFormatter, const TQString& str )
|
|
|
|
{
|
|
|
|
int timepos = str.find(" ");
|
|
|
|
const bool emptyTime = timepos >= 0 && timeFormatter.isEmpty(str.mid(timepos+1)); //s.mid(timepos+1).replace(':',"").stripWhiteSpace().isEmpty();
|
|
|
|
if (timepos >= 0 && dateFormatter.isEmpty(str.left(timepos)) // s.left(timepos).replace(m_dateFormatter.separator(), "").stripWhiteSpace().isEmpty()
|
|
|
|
&& emptyTime)
|
|
|
|
//empty date/time is valid
|
|
|
|
return true;
|
|
|
|
return timepos>=0 && dateFormatter.stringToDate( str.left(timepos) ).isValid()
|
|
|
|
&& (emptyTime /*date without time is also valid*/ || timeFormatter.stringToTime( str.mid(timepos+1) ).isValid());
|
|
|
|
}
|