diff --git a/client/helper.cpp b/client/helper.cpp index eb3b70d..e5e390c 100644 --- a/client/helper.cpp +++ b/client/helper.cpp @@ -13,12 +13,12 @@ char* __strlcpy_chk (char* dest, const char* src, int len, int destcapacity) { - return NULL; + return nullptr; } char* __strlcat_chk (char* dest, const char* src, int len, int destcapacity) { - return NULL; + return nullptr; } #endif @@ -62,11 +62,16 @@ time_t date::toTimestamp() const return mktime(&t); } -date date::fromTimestamp(time_t timestamp) +date date::fromTimestamp(time_t timestamp, int options) { struct tm t; memset(&t, 0, sizeof t); - t = *localtime(×tamp); + switch (options) + { + case To_GmtTime: t = *gmtime(×tamp); break; + case To_LocalTime: t = *localtime(×tamp); break; + } + date r; r.mDay = t.tm_mday; r.mMonth = t.tm_mon + 1; @@ -74,6 +79,31 @@ date date::fromTimestamp(time_t timestamp) return r; } +int date::daysInMonth(int y, int m) +{ + switch (m) + { + case 2: + if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) + return 29; + else + return 28; + + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + return 31; + + default: + return 30; + } +} + + std::string chrono::secondsToDisplay(int seconds, bool showSeconds) { int hours = seconds / 3600; @@ -98,7 +128,8 @@ std::string chrono::timeToStr(time_t timestamp) std::string chrono::timeToLocalStr(time_t timestamp) { char buf[128]; - strftime(buf, sizeof buf, "%FT%TZ", localtime(×tamp)); + struct tm t = localtime(timestamp); + strftime(buf, sizeof buf, "%FT%TZ", &t); return buf; } diff --git a/client/helper.h b/client/helper.h index 1b53782..e009fbd 100644 --- a/client/helper.h +++ b/client/helper.h @@ -23,9 +23,23 @@ namespace helper // Timestamp is always UTC! But date is always local! time_t toTimestamp() const; - static date fromTimestamp(time_t timestamp); + + enum + { + To_LocalTime, + To_GmtTime + }; + static date fromTimestamp(time_t timestamp, int options); + static date today(); + static date fromTm(struct tm& t); + static int daysInMonth(int y, int m); }; + bool operator < (const date& lhs, const date& rhs); + bool operator > (const date& lhs, const date& rhs); + bool operator == (const date& lhs, const date& rhs); + bool operator >= (const date& lhs, const date& rhs); + class chrono { public: @@ -34,6 +48,7 @@ namespace helper static std::string timeToLocalStr(time_t timestamp); static time_t strToTime(const std::string& s); static struct tm localtime(time_t timestamp); + static int daysInMonth(int m, int y); }; diff --git a/client/task.cpp b/client/task.cpp index c334b87..16660eb 100644 --- a/client/task.cpp +++ b/client/task.cpp @@ -9,6 +9,8 @@ # include #endif +using namespace helper; + // -------- WorldId ------ WorldId::WorldId() { @@ -72,7 +74,7 @@ TimeRecord::TimeRecord() {} TimeRecord::TimeRecord(const time_t &startTime, const time_t &endTime, Id taskId) - :mId(0), mTaskId(taskId), mSaved(false), mStartTime(startTime), mEndTime(endTime) + :mId(0), mTaskId(taskId), mStartTime(startTime), mEndTime(endTime), mSaved(false) { } @@ -168,7 +170,7 @@ void TimeRecord::deleteRecord() // -------------------- TimeLine -------------------- TimeLine::TimeLine() - :mTaskId(0), mActiveTimeRecord(nullptr), mTotalTime(0), mActive(false) + :mTaskId(0), mActive(false), mActiveTimeRecord(nullptr), mTotalTime(0) {} TimeLine::~TimeLine() @@ -457,7 +459,7 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc) if (mActiveTimeRecord) { - int delta = currentUtc - mActiveTimeRecord->endTime(); + long delta = currentUtc - mActiveTimeRecord->endTime(); mActiveTimeRecord->setEndTime(currentUtc); TimeRecord* tr = hasTimePoint(currentUtc); @@ -485,7 +487,7 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc) void TimeLine::load() { SQLite::Statement q(Storage::instance().database(), "select id, starttime, endtime from timeline where (taskid = :taskid) and ((removed is null) or (removed != 1)) order by id asc"); - q.bind(":taskid", (sqlite3_int64)mTaskId); + q.bind(":taskid", mTaskId); while (q.executeStep()) { time_t start = helper::chrono::strToTime(q.getColumn(1).getText()); @@ -526,82 +528,93 @@ TimeArray& TimeLine::data() return mData; } -void TimeLine::getYears(std::set& years) +std::set TimeLine::getYears() { + std::set r; if (mData.empty()) - return; + return r; - time_t starttime_1 = mData.front().startTime(), starttime_2 = mData.back().startTime(); - struct tm t1 = *localtime(&starttime_1), t2 = *localtime(&starttime_2); + date t1 = date::fromTimestamp(mData.front().startTime(), date::To_LocalTime), + t2 = date::fromTimestamp(mData.back().startTime(), date::To_LocalTime); - // Find lower bound of years - it is first time record - int year1 = t1.tm_year + 1900; - years.insert(year1); + r.insert(t1.mYear); // Find higher bound of years - it is last time record - int year2 = t2.tm_year + 1900; - - if (year1 == year2) - return; + if (t1.mYear == t2.mYear) + return r; // Try to find next year by binary search - for (int year = year1+1; year <= year2; year++) + for (int year = t1.mYear + 1; year <= t2.mYear; year++) { - QDate yearStart(year, 1, 1); + date yearStart(year, 1, 1); TimeArray::iterator iter = std::lower_bound(mData.begin(), mData.end(), yearStart, - [&](const TimeRecord& lhs, time_t rhs) - { return lhs.endTime() < rhs; }); + [&](const TimeRecord& lhs, date rhs) + { + return date::fromTimestamp(lhs.endTime(), date::To_LocalTime) < rhs; + }); + if (iter != mData.end()) { - // Get current year - struct tm ct = helper::chrono::localtime(iter->startTime()); - if (ct.tm_year + 1900 <= year) - years.insert(year); + // Get found year + date found_date = date::fromTimestamp(iter->startTime(), date::To_LocalTime); + if (found_date.mYear <= year) + r.insert(year); } } + + return r; } -void TimeLine::getMonthes(int year, std::set& result) +std::set TimeLine::getMonthes(int year) { + std::set r; + for (int month = 1; month <= 12; month++) { - QDate monthBegin(year, month, 1); - QDate monthEnd(year, month, monthBegin.daysInMonth()); + date monthBegin(year, month, 1); + date monthEnd(year, month, date::daysInMonth(year, month)); // 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() < QDateTime(d).toTime_t();}); + [] (const TimeRecord& tr, const date& d) + + { + return date::fromTimestamp(tr.endTime(), date::To_LocalTime) < d; + }); + //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()) { // Current date is local time! - helper::date currentDate = helper::date::fromTimestamp(lowest->startTime()); + helper::date currentDate = helper::date::fromTimestamp(lowest->startTime(), helper::date::To_LocalTime); if (currentDate.mYear > year) continue; if (currentDate.mYear == year && currentDate.mMonth > month) continue; - result.insert(month); + r.insert(month); } } + + return r; } -void TimeLine::getDays(int year, int month, std::set& result) +std::set TimeLine::getDays(int year, int month) { - QDate monthBegin(year, month, 1); - for (int day = 1; day <= monthBegin.daysInMonth(); day++) + std::set r; + date monthBegin(year, month, 1); + for (int day = 1; day <= date::daysInMonth(year, month); day++) { - QDate currentDay(year, month, day); + date currentDay(year, month, day); TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), currentDay, - [] (const TimeRecord& tr, const QDate& d) + [] (const TimeRecord& tr, const date& d) { - return tr.endTime() < QDateTime(d).toTime_t(); + return date::fromTimestamp(tr.endTime(), date::To_LocalTime) < d; }); if (lowest != mData.end()) { - helper::date startDate = helper::date::fromTimestamp(lowest->startTime()); + helper::date startDate = helper::date::fromTimestamp(lowest->startTime(), helper::date::To_LocalTime); if (startDate.mYear > year) continue; if (startDate.mYear == year && startDate.mMonth > month) @@ -609,9 +622,10 @@ void TimeLine::getDays(int year, int month, std::set& result) if (startDate.mYear == year && startDate.mMonth == month && startDate.mDay > day) continue; - result.insert(day); + r.insert(day); } } + return r; } int TimeLine::getTime(int year, int month, int day, std::vector* intervals) @@ -630,7 +644,7 @@ int TimeLine::getTime(int year, int month, int day, std::vector* int { TimeRecord& tr = *lowest; - helper::date startDate = helper::date::fromTimestamp(tr.startTime()); + helper::date startDate = helper::date::fromTimestamp(tr.startTime(), date::To_LocalTime); if (startDate.mYear > year) break; @@ -670,31 +684,36 @@ int TimeLine::today() { int result = 0; - TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), QDate::currentDate(), - [] (const TimeRecord& lhs, const QDate& rhs) + // Find starting record related to today + // Today is local time! + date today = date::today(); + + TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), today, + [] (const TimeRecord& lhs, const date& rhs) { - return lhs.endTime().toLocalTime().date() < rhs; + return date::fromTimestamp(lhs.endTime(), date::To_LocalTime) < rhs; }); for (;lowIter != mData.end(); lowIter++) { - if (lowIter->startTime().toLocalTime().date() > QDate::currentDate()) + if (date::fromTimestamp(lowIter->startTime(), date::To_LocalTime) > today) break; // quit the loop - if (lowIter->endTime().toLocalTime().date() >= QDate::currentDate()) + if (date::fromTimestamp(lowIter->endTime(), date::To_LocalTime) >= today) { - QDateTime dayBegin(QDate::currentDate(), QTime(0,0)); - QDateTime dayEnd(QDate::currentDate(), QTime(23, 59, 59)); + // Both are GMT time + time_t dayBegin = date::today().toTimestamp(); + time_t dayEnd = dayBegin + 86359; - int secondsTo = dayBegin.secsTo(lowIter->startTime().toLocalTime()); + int64_t secondsTo = lowIter->startTime() - dayBegin; if (secondsTo > 0) dayBegin = lowIter->startTime(); - int secondsFrom = dayEnd.secsTo(lowIter->endTime().toLocalTime()); + int64_t secondsFrom = lowIter->endTime() - dayEnd; if (secondsFrom < 0) - dayEnd = lowIter->endTime().toLocalTime(); + dayEnd = lowIter->endTime(); - int secondsSpent = dayBegin.secsTo(dayEnd); + int secondsSpent = dayEnd - dayBegin; result += secondsSpent; } } diff --git a/client/task.h b/client/task.h index 22c591d..01ea9d3 100644 --- a/client/task.h +++ b/client/task.h @@ -12,7 +12,7 @@ #include #include "SQLiteCpp/Database.h" -typedef uint64_t Id; +typedef int64_t Id; class WorldId { @@ -92,14 +92,27 @@ public: void setTaskId(Id id); // These methods work with local time - void getYears(std::set& result); - void getMonthes(int year, std::set& result); - void getDays(int year, int month, std::set& result); + // Returns set of available years in timeline + std::set getYears(); + + // Returns set of availables monthes in timeline for specified year + std::set getMonthes(int year); + + // Returns set of available days in timeline for specified year & month + std::set getDays(int year, int month); + int getTime(int year, int month, int day, std::vector* intervals); + // Returns number of seconds spent today int today(); + + // Returns number of seconds spent in current month int month(); + + // Returns number of seconds spent in interval int getSum(const QDate& start, const QDate& finish); + + // Checks if there duplicate & overllaping time intervals bool duplicateDetected() const; // Checks if specified interval has intersection