diff --git a/client/helper.cpp b/client/helper.cpp index 9ef1419..eb3b70d 100644 --- a/client/helper.cpp +++ b/client/helper.cpp @@ -44,6 +44,35 @@ void theme::applyCurrent(Settings& settings) qApp->setStyleSheet(""); } +date::date() +{} + +date::date(int y, int m, int d) + :mYear(y), mMonth(m), mDay(d) +{ +} + +time_t date::toTimestamp() const +{ + struct tm t; + memset(&t, 0, sizeof t); + t.tm_year = mYear - 1900; + t.tm_mon = mMonth - 1; + t.tm_mday = mDay; + return mktime(&t); +} + +date date::fromTimestamp(time_t timestamp) +{ + struct tm t; + memset(&t, 0, sizeof t); + t = *localtime(×tamp); + date r; + r.mDay = t.tm_mday; + r.mMonth = t.tm_mon + 1; + r.mYear = t.tm_year + 1900; + return r; +} std::string chrono::secondsToDisplay(int seconds, bool showSeconds) { @@ -66,6 +95,13 @@ std::string chrono::timeToStr(time_t timestamp) return buf; } +std::string chrono::timeToLocalStr(time_t timestamp) +{ + char buf[128]; + strftime(buf, sizeof buf, "%FT%TZ", localtime(×tamp)); + return buf; +} + time_t chrono::strToTime(const std::string& s) { struct tm t; diff --git a/client/helper.h b/client/helper.h index 10514a0..1b53782 100644 --- a/client/helper.h +++ b/client/helper.h @@ -13,12 +13,17 @@ namespace helper static void applyCurrent(Settings& settings); }; + // date is always local. year, month & day number is natural (NO start from zero, NO delta with 1900 year etc.) struct date { int mYear = -1, mMonth = -1, mDay = -1; - time_t toTimestamp(); - static date fromTimestamp(); + date(); + date(int y, int m, int d); + + // Timestamp is always UTC! But date is always local! + time_t toTimestamp() const; + static date fromTimestamp(time_t timestamp); }; class chrono @@ -26,7 +31,9 @@ namespace helper public: static std::string secondsToDisplay(int seconds, bool showSeconds); static std::string timeToStr(time_t timestamp); + static std::string timeToLocalStr(time_t timestamp); static time_t strToTime(const std::string& s); + static struct tm localtime(time_t timestamp); }; diff --git a/client/main.cpp b/client/main.cpp index bf6e729..5310ac2 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -7,7 +7,7 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); - ThemeHelper::applyCurrentTheme(Settings::instance()); + helper::theme::applyCurrent(Settings::instance()); MainWindow w; w.layout()->invalidate(); diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index cf76ddd..77a207c 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -36,7 +36,7 @@ MainWindow::MainWindow(QWidget *parent) : { mSettings = QSharedPointer(new Settings()); - ThemeHelper::applyCurrentTheme(*mSettings); + helper::theme::applyCurrent(*mSettings); mAttachmentsAction = nullptr; mAttachmentsLabel = nullptr; @@ -51,7 +51,7 @@ MainWindow::MainWindow(QWidget *parent) : // Hide Find line edit for now ui->mFindFrame->setVisible(false); - EscapeKeyEventFilter* eventFilter = new EscapeKeyEventFilter(ui->mFindEdit); + helper::EscapeKeyEventFilter* eventFilter = new helper::EscapeKeyEventFilter(ui->mFindEdit); connect(eventFilter, SIGNAL(escapePressed(QObject*)), this, SLOT(findRejected(QObject*))); ui->mFindEdit->installEventFilter(eventFilter); @@ -854,7 +854,7 @@ void MainWindow::startTracking() // Trigger permission dialog if needed if (mSettings->data()[KEY_SMART_STOP].toBool()) { - if (!ActivityTrackerHelper::ensureSmartTrackingIsPossible()) + if (!helper::activityTracker::ensureSmartTrackingIsPossible()) mTrayIcon->showMessage(tr("No smart tracking stop/start"), tr("Problem with obtaining permissions"), QSystemTrayIcon::Warning); } @@ -992,7 +992,7 @@ void MainWindow::updateData() if (saveToDb) mLogger->log("Flushing timeline to DB start"); - mCurrentTask->timeline()->flush(saveToDb, QDateTime::currentDateTimeUtc()); + mCurrentTask->timeline()->flush(saveToDb, QDateTime::currentDateTimeUtc().toTime_t()); if (saveToDb) { mLastTimelineFlush = QDateTime::currentDateTimeUtc(); @@ -1236,8 +1236,8 @@ void MainWindow::showTimeForSelectedTask() int spentSecondsToday = t->timeline()->today(); int spentSecondsMonth = t->timeline()->month(); - ui->mTodaySpentTimeLabel->setText(TimeHelper::secondsToDisplay(spentSecondsToday, showSeconds)); - ui->mThisMonthSpentTimeLabel->setText(TimeHelper::secondsToDisplay(spentSecondsMonth, showSeconds)); + ui->mTodaySpentTimeLabel->setText(QString::fromStdString(helper::chrono::secondsToDisplay(spentSecondsToday, showSeconds))); + ui->mThisMonthSpentTimeLabel->setText(QString::fromStdString(helper::chrono::secondsToDisplay(spentSecondsMonth, showSeconds))); } } @@ -1256,7 +1256,7 @@ void MainWindow::showTimeForTrackingTask() t = t->parent(); } int spentSecondsToday = mCurrentTask->timeline()->today(); - QString timeString = TimeHelper::secondsToDisplay(spentSecondsToday, showSeconds); + QString timeString = QString::fromStdString(helper::chrono::secondsToDisplay(spentSecondsToday, showSeconds)); path += " : " + timeString; mCurrentIntervalLabel->setText(path); @@ -1301,7 +1301,7 @@ void MainWindow::updateTrayIcon(TrayShowMessage flag) { bool showSeconds = mSettings->data()[KEY_SHOW_SECONDS].toBool(); int spentSecondsToday = mCurrentTask->timeline()->today(); - QString timeString = TimeHelper::secondsToDisplay(spentSecondsToday, showSeconds); + QString timeString = helper::chrono::secondsToDisplay(spentSecondsToday, showSeconds); tooltip = tr("Litt is tracking ") + mCurrentTask->title() + ".\n" + tr("Time spent today for this task is ") + timeString; } diff --git a/client/preferencesdlg.cpp b/client/preferencesdlg.cpp index 35d6418..23838b7 100644 --- a/client/preferencesdlg.cpp +++ b/client/preferencesdlg.cpp @@ -35,7 +35,7 @@ PreferencesDlg::PreferencesDlg(QWidget *parent, Settings& settings) : if (settings.data().value(KEY_DB_FILENAME_SPECIFIED).toBool()) ui->mDatabaseLocation->setText(settings.data().value(KEY_DB_FILENAME).toString()); else - ui->mDatabaseLocation->setText(PathHelper::pathToDatabase()); + ui->mDatabaseLocation->setText(helper::path::pathToDatabase()); // Use stop on idle ? ui->mSmartStopTracking->setChecked(GET_BOOL(KEY_SMART_STOP)); @@ -61,7 +61,7 @@ PreferencesDlg::~PreferencesDlg() void PreferencesDlg::selectDatabase() { - QFileDialog dlg(this, tr("Select database to use"), PathHelper::pathToDesktop()); + QFileDialog dlg(this, tr("Select database to use"), helper::path::pathToDesktop()); dlg.setAcceptMode(QFileDialog::AcceptSave); dlg.setFileMode(QFileDialog::AnyFile); if (dlg.exec() == QDialog::Accepted) @@ -95,7 +95,7 @@ void PreferencesDlg::smartStopSettingChanged(bool v) { if (v) { - if (!ActivityTrackerHelper::ensureSmartTrackingIsPossible()) + if (!helper::activityTracker::ensureSmartTrackingIsPossible()) ui->mSmartStopTracking->setChecked(false); } allowStartAfterIdleControls(); @@ -127,5 +127,5 @@ void PreferencesDlg::allowStartAfterIdleControls() void PreferencesDlg::applyTheme() { - ThemeHelper::applyCurrentTheme(mSettings); + helper::theme::applyCurrent(mSettings); } diff --git a/client/settings.cpp b/client/settings.cpp index 43a6e2e..c2cc9e4 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -20,7 +20,7 @@ QVariantMap& Settings::data() void Settings::save() { - QSettings settings(PathHelper::pathToSettings(), QSettings::IniFormat); + QSettings settings(helper::path::pathToSettings(), QSettings::IniFormat); settings.clear(); for (const QString& e: data().keys()) { @@ -30,7 +30,7 @@ void Settings::save() void Settings::load() { - QSettings settings(PathHelper::pathToSettings(), QSettings::IniFormat); + QSettings settings(helper::path::pathToSettings(), QSettings::IniFormat); mData.clear(); const QStringList keys = settings.allKeys(); for (const QString& k: keys) diff --git a/client/task.cpp b/client/task.cpp index 5e5e7ae..c334b87 100644 --- a/client/task.cpp +++ b/client/task.cpp @@ -101,7 +101,7 @@ void TimeRecord::setStartTime(const time_t &startTime) int TimeRecord::length() { - return (int)(mEndTime - mStartTime); + return static_cast(mEndTime - mStartTime); } Id TimeRecord::id() const @@ -140,10 +140,9 @@ void TimeRecord::save() { SQLite::Statement q(Storage::instance().database(), "insert into timeline(id, starttime, endtime, taskid, removed) values (NULL, :starttime, :endtime, :taskid, :removed)"); - - q.bind(":starttime", TimeHelper::timeToStr(mStartTime)); - q.bind(":endtime", TimeHelper::timeToStr(mEndTime)); - q.bind(":taskid", (sqlite3_int64)mTaskId); + q.bind(":starttime", helper::chrono::timeToStr(mStartTime)); + q.bind(":endtime", helper::chrono::timeToStr(mEndTime)); + q.bind(":taskid", static_cast(mTaskId)); q.bind(":removed", 0); if (q.exec()) mId = Storage::instance().database().getLastInsertRowid(); @@ -152,10 +151,10 @@ void TimeRecord::save() { SQLite::Statement q(Storage::instance().database(), "update timeline set starttime = :starttime, endtime = :endtime, taskid = :taskid, removed = 0 where id = :id"); - q.bind(":starttime", TimeHelper::timeToStr(mStartTime)); - q.bind(":endtime", TimeHelper::timeToStr(mEndTime)); - q.bind(":taskid", (sqlite3_int64)mTaskId); - q.bind(":id", (sqlite3_int64)mId); + q.bind(":starttime", helper::chrono::timeToStr(mStartTime)); + q.bind(":endtime", helper::chrono::timeToStr(mEndTime)); + q.bind(":taskid", static_cast(mTaskId)); + q.bind(":id", static_cast(mId)); q.exec(); } } @@ -163,7 +162,7 @@ void TimeRecord::save() void TimeRecord::deleteRecord() { SQLite::Statement q(Storage::instance().database(), "update timeline set removed = 1 where id = :id"); - q.bind(":id", (sqlite3_int64)mId); + q.bind(":id", static_cast(mId)); q.exec(); } @@ -178,9 +177,9 @@ TimeLine::~TimeLine() int TimeLine::findTotalTime() { int result = 0; - for(auto& t: mData.begin()) + for(auto& t: mData) { - int delta = t.endTime() - t.startTime(); + int delta = static_cast(t.endTime() - t.startTime()); result += delta + 1; } return result; @@ -253,7 +252,7 @@ TimeRecord* TimeLine::hasTimePoint(time_t t) }); if (result == mData.end()) - tr = &mData.last(); + tr = &mData.back(); else if (result != mData.begin()) tr = &(*(--result)); @@ -281,7 +280,7 @@ bool TimeLine::hasIntersection(const TimeRecord &interval) if (result == mData.end()) { // There is time record which start point is lesser than interval.startTime() - return (mData.last().endTime() >= interval.startTime()); + return (mData.back().endTime() >= interval.startTime()); } else if (result != mData.begin()) @@ -304,7 +303,7 @@ bool TimeLine::hasIntersection(const TimeRecord &interval) void TimeLine::insertInterval(const TimeRecord &interval) { mData.push_back(interval); - mData.last().save(); + mData.back().save(); sortData(); } @@ -323,7 +322,7 @@ bool TimeLine::removeInterval(const TimeRecord &interval) if (result == mData.end()) { // There is time record which start point is lesser than interval.startTime() - if (mData.last().id() == interval.id()) + if (mData.back().id() == interval.id()) { // Remove from DB mData.back().deleteRecord(); @@ -489,8 +488,8 @@ void TimeLine::load() q.bind(":taskid", (sqlite3_int64)mTaskId); while (q.executeStep()) { - time_t start = TimeHelper::strToTime(q.getColumn(1).getText()); - time_t stop = TimeHelper::strToTime(q.getColumn(2).getText()); + time_t start = helper::chrono::strToTime(q.getColumn(1).getText()); + time_t stop = helper::chrono::strToTime(q.getColumn(2).getText()); TimeRecord tr; tr.setId(q.getColumn(0).getInt64()); @@ -532,9 +531,8 @@ void TimeLine::getYears(std::set& years) if (mData.empty()) return; - struct tm t1, t2; - localtime(&mData.front().startTime(), &t1); - localtime(&mData.back().startTime(), &t2); + time_t starttime_1 = mData.front().startTime(), starttime_2 = mData.back().startTime(); + struct tm t1 = *localtime(&starttime_1), t2 = *localtime(&starttime_2); // Find lower bound of years - it is first time record int year1 = t1.tm_year + 1900; @@ -556,7 +554,7 @@ void TimeLine::getYears(std::set& years) if (iter != mData.end()) { // Get current year - struct tm ct; localtime(&iter->startTime(), &ct); + struct tm ct = helper::chrono::localtime(iter->startTime()); if (ct.tm_year + 1900 <= year) years.insert(year); } @@ -571,15 +569,17 @@ void TimeLine::getMonthes(int year, std::set& result) QDate monthEnd(year, month, monthBegin.daysInMonth()); // Find range for every month in year [lowest, higher) - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), monthBegin, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), monthBegin, + [] (const TimeRecord& tr, const QDate& d) + { return tr.endTime() < QDateTime(d).toTime_t();}); //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), monthEnd, [] (const TimeRecord& tr, const QDate& d) { return tr.startTime().toLocalTime().date() < d;}); if (lowest != mData.end()) { - int currentYear = lowest->startTime().toLocalTime().date().year(); - int currentMonth = lowest->startTime().toLocalTime().date().month(); - if (currentYear > year) + // Current date is local time! + helper::date currentDate = helper::date::fromTimestamp(lowest->startTime()); + if (currentDate.mYear > year) continue; - if (currentYear == year && currentMonth > month) + if (currentDate.mYear == year && currentDate.mMonth > month) continue; result.insert(month); @@ -594,17 +594,19 @@ void TimeLine::getDays(int year, int month, std::set& result) { QDate currentDay(year, month, day); - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), currentDay, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), currentDay, + [] (const TimeRecord& tr, const QDate& d) + { + return tr.endTime() < QDateTime(d).toTime_t(); + }); if (lowest != mData.end()) { - int startYear = lowest->startTime().toLocalTime().date().year(); - int startMonth = lowest->startTime().toLocalTime().date().month(); - int startDay = lowest->startTime().toLocalTime().date().day(); - if (startYear > year) + helper::date startDate = helper::date::fromTimestamp(lowest->startTime()); + if (startDate.mYear > year) continue; - if (startYear == year && startMonth > month) + if (startDate.mYear == year && startDate.mMonth > month) continue; - if (startYear == year && startMonth == month && startDay > day) + if (startDate.mYear == year && startDate.mMonth == month && startDate.mDay > day) continue; result.insert(day); @@ -618,30 +620,36 @@ int TimeLine::getTime(int year, int month, int day, std::vector* int QDate d(year, month, day); // Find range of related records [lowest, higher) - TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), d, [] (const TimeRecord& tr, const QDate& d) { return tr.endTime().toLocalTime().date() < d;}); + TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), d, [] (const TimeRecord& tr, const QDate& d) + { + return tr.endTime() < QDateTime(d).toTime_t(); + }); //TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), d, [] (const QDate& d, const TimeRecord& tr) { return tr.startTime().toLocalTime().date() < d;}); for (;lowest != mData.end();/*&& lowest != higher;*/ lowest++) { TimeRecord& tr = *lowest; - if (tr.startTime().toLocalTime().date().year() > year) + + helper::date startDate = helper::date::fromTimestamp(tr.startTime()); + + if (startDate.mYear > year) break; - if (tr.startTime().toLocalTime().date().year() == year && tr.startTime().toLocalTime().date().month() > month) + if (startDate.mYear == year && startDate.mMonth > month) break; - if (tr.startTime().toLocalTime().date().year() == year && - tr.startTime().toLocalTime().date().month() == month && - tr.startTime().toLocalTime().date().day() > day) + if (startDate.mYear == year && + startDate.mMonth == month && + startDate.mDay > day) break; - QDateTime dayBegin(d, QTime(0, 0, 0)); - QDateTime dayEnd(d, QTime(23, 59, 59)); + time_t dayBegin = helper::date{year, month, day}.toTimestamp(); + time_t dayEnd = dayBegin + 86399; - if (tr.startTime().toLocalTime().secsTo(dayBegin) < 0) - dayBegin = tr.startTime().toLocalTime(); // Time record begin is later than begin of the day + if (tr.startTime() > dayBegin) + dayBegin = tr.startTime(); // Time record begin is later than begin of the day - if (tr.endTime().toLocalTime().secsTo(dayEnd) > 0) - dayEnd = tr.endTime().toLocalTime(); + if (tr.endTime() < dayEnd) + dayEnd = tr.endTime(); if (intervals) { diff --git a/client/task.h b/client/task.h index 91369be..22c591d 100644 --- a/client/task.h +++ b/client/task.h @@ -63,7 +63,7 @@ protected: bool mSaved; }; -typedef QVector TimeArray; +typedef std::vector TimeArray; typedef std::set HoursSet; typedef std::map > DaysMap; diff --git a/client/timereportwizard.cpp b/client/timereportwizard.cpp index dcfc3c9..e5772e1 100644 --- a/client/timereportwizard.cpp +++ b/client/timereportwizard.cpp @@ -140,14 +140,14 @@ void ReportViewPage::generateReport() { int individualTime = t->getReportedTime(); bool showSeconds = mSettings.data()[KEY_SHOW_SECONDS].toBool(); - QString l1 = TimeHelper::secondsToDisplay(individualTime, showSeconds); + QString l1 = helper::chrono::secondsToDisplay(individualTime, showSeconds); report += t->path() + " : " + l1; //if (mSettings.data()[KEY_CUMULATIVE_REPORT].toBool()) { int childrenTime = t->getChildrenReportedTime(); if (childrenTime) { - QString l2 = TimeHelper::secondsToDisplay(individualTime + childrenTime, showSeconds); + QString l2 = helper::chrono::secondsToDisplay(individualTime + childrenTime, showSeconds); report += ". Including subtasks time: " + l2; } } diff --git a/client/timetreemodel.cpp b/client/timetreemodel.cpp index 0ed661c..9c0e7bc 100644 --- a/client/timetreemodel.cpp +++ b/client/timetreemodel.cpp @@ -305,7 +305,7 @@ QVariant TimeTreeModel::data(const QModelIndex &index, int role) const tr = intervals[index.row()]; // Intervals are in local time already - return QString("%1 - %2").arg(tr.startTime().time().toString(mTimeFormat), tr.endTime().time().toString(mTimeFormat)); + return QString("%1 - %2").arg(helper::chronotr.startTime().time().toString(mTimeFormat), tr.endTime().time().toString(mTimeFormat)); default: return QVariant();