- more work to avoid QDate. helper::date is improved.

This commit is contained in:
dmytro.bogovych 2019-03-26 16:32:48 +02:00
parent 8c13869e9c
commit fbbb98cb8d
4 changed files with 138 additions and 60 deletions

View File

@ -13,12 +13,12 @@
char* __strlcpy_chk (char* dest, const char* src, int len, int destcapacity) 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) char* __strlcat_chk (char* dest, const char* src, int len, int destcapacity)
{ {
return NULL; return nullptr;
} }
#endif #endif
@ -62,11 +62,16 @@ time_t date::toTimestamp() const
return mktime(&t); return mktime(&t);
} }
date date::fromTimestamp(time_t timestamp) date date::fromTimestamp(time_t timestamp, int options)
{ {
struct tm t; struct tm t;
memset(&t, 0, sizeof t); memset(&t, 0, sizeof t);
t = *localtime(&timestamp); switch (options)
{
case To_GmtTime: t = *gmtime(&timestamp); break;
case To_LocalTime: t = *localtime(&timestamp); break;
}
date r; date r;
r.mDay = t.tm_mday; r.mDay = t.tm_mday;
r.mMonth = t.tm_mon + 1; r.mMonth = t.tm_mon + 1;
@ -74,6 +79,31 @@ date date::fromTimestamp(time_t timestamp)
return r; 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) std::string chrono::secondsToDisplay(int seconds, bool showSeconds)
{ {
int hours = seconds / 3600; int hours = seconds / 3600;
@ -98,7 +128,8 @@ std::string chrono::timeToStr(time_t timestamp)
std::string chrono::timeToLocalStr(time_t timestamp) std::string chrono::timeToLocalStr(time_t timestamp)
{ {
char buf[128]; char buf[128];
strftime(buf, sizeof buf, "%FT%TZ", localtime(&timestamp)); struct tm t = localtime(timestamp);
strftime(buf, sizeof buf, "%FT%TZ", &t);
return buf; return buf;
} }

View File

@ -23,8 +23,22 @@ namespace helper
// Timestamp is always UTC! But date is always local! // Timestamp is always UTC! But date is always local!
time_t toTimestamp() const; 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 class chrono
{ {
@ -34,6 +48,7 @@ namespace helper
static std::string timeToLocalStr(time_t timestamp); static std::string timeToLocalStr(time_t timestamp);
static time_t strToTime(const std::string& s); static time_t strToTime(const std::string& s);
static struct tm localtime(time_t timestamp); static struct tm localtime(time_t timestamp);
static int daysInMonth(int m, int y);
}; };

View File

@ -9,6 +9,8 @@
# include <uuid/uuid.h> # include <uuid/uuid.h>
#endif #endif
using namespace helper;
// -------- WorldId ------ // -------- WorldId ------
WorldId::WorldId() WorldId::WorldId()
{ {
@ -72,7 +74,7 @@ TimeRecord::TimeRecord()
{} {}
TimeRecord::TimeRecord(const time_t &startTime, const time_t &endTime, Id taskId) 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::TimeLine() TimeLine::TimeLine()
:mTaskId(0), mActiveTimeRecord(nullptr), mTotalTime(0), mActive(false) :mTaskId(0), mActive(false), mActiveTimeRecord(nullptr), mTotalTime(0)
{} {}
TimeLine::~TimeLine() TimeLine::~TimeLine()
@ -457,7 +459,7 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc)
if (mActiveTimeRecord) if (mActiveTimeRecord)
{ {
int delta = currentUtc - mActiveTimeRecord->endTime(); long delta = currentUtc - mActiveTimeRecord->endTime();
mActiveTimeRecord->setEndTime(currentUtc); mActiveTimeRecord->setEndTime(currentUtc);
TimeRecord* tr = hasTimePoint(currentUtc); TimeRecord* tr = hasTimePoint(currentUtc);
@ -485,7 +487,7 @@ void TimeLine::flush(bool saveToDb, time_t currentUtc)
void TimeLine::load() 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"); 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()) while (q.executeStep())
{ {
time_t start = helper::chrono::strToTime(q.getColumn(1).getText()); time_t start = helper::chrono::strToTime(q.getColumn(1).getText());
@ -526,82 +528,93 @@ TimeArray& TimeLine::data()
return mData; return mData;
} }
void TimeLine::getYears(std::set<int>& years) std::set<int> TimeLine::getYears()
{ {
std::set<int> r;
if (mData.empty()) if (mData.empty())
return; return r;
time_t starttime_1 = mData.front().startTime(), starttime_2 = mData.back().startTime(); date t1 = date::fromTimestamp(mData.front().startTime(), date::To_LocalTime),
struct tm t1 = *localtime(&starttime_1), t2 = *localtime(&starttime_2); t2 = date::fromTimestamp(mData.back().startTime(), date::To_LocalTime);
// Find lower bound of years - it is first time record r.insert(t1.mYear);
int year1 = t1.tm_year + 1900;
years.insert(year1);
// Find higher bound of years - it is last time record // Find higher bound of years - it is last time record
int year2 = t2.tm_year + 1900; if (t1.mYear == t2.mYear)
return r;
if (year1 == year2)
return;
// Try to find next year by binary search // 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, TimeArray::iterator iter = std::lower_bound(mData.begin(), mData.end(), yearStart,
[&](const TimeRecord& lhs, time_t rhs) [&](const TimeRecord& lhs, date rhs)
{ return lhs.endTime() < rhs; }); {
return date::fromTimestamp(lhs.endTime(), date::To_LocalTime) < rhs;
});
if (iter != mData.end()) if (iter != mData.end())
{ {
// Get current year // Get found year
struct tm ct = helper::chrono::localtime(iter->startTime()); date found_date = date::fromTimestamp(iter->startTime(), date::To_LocalTime);
if (ct.tm_year + 1900 <= year) if (found_date.mYear <= year)
years.insert(year); r.insert(year);
} }
} }
return r;
} }
void TimeLine::getMonthes(int year, std::set<int>& result) std::set<int> TimeLine::getMonthes(int year)
{ {
std::set<int> r;
for (int month = 1; month <= 12; month++) for (int month = 1; month <= 12; month++)
{ {
QDate monthBegin(year, month, 1); date monthBegin(year, month, 1);
QDate monthEnd(year, month, monthBegin.daysInMonth()); date monthEnd(year, month, date::daysInMonth(year, month));
// Find range for every month in year [lowest, higher) // Find range for every month in year [lowest, higher)
TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), monthBegin, TimeArray::iterator lowest = std::lower_bound(mData.begin(), mData.end(), monthBegin,
[] (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;
});
//TimeArray::iterator higher = std::upper_bound(mData.begin(), mData.end(), monthEnd, [] (const TimeRecord& tr, const QDate& d) { return tr.startTime().toLocalTime().date() < 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()) if (lowest != mData.end())
{ {
// Current date is local time! // 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) if (currentDate.mYear > year)
continue; continue;
if (currentDate.mYear == year && currentDate.mMonth > month) if (currentDate.mYear == year && currentDate.mMonth > month)
continue; continue;
result.insert(month); r.insert(month);
} }
} }
return r;
} }
void TimeLine::getDays(int year, int month, std::set<int>& result) std::set<int> TimeLine::getDays(int year, int month)
{ {
QDate monthBegin(year, month, 1); std::set<int> r;
for (int day = 1; day <= monthBegin.daysInMonth(); day++) 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, 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()) 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) if (startDate.mYear > year)
continue; continue;
if (startDate.mYear == year && startDate.mMonth > month) if (startDate.mYear == year && startDate.mMonth > month)
@ -609,9 +622,10 @@ void TimeLine::getDays(int year, int month, std::set<int>& result)
if (startDate.mYear == year && startDate.mMonth == month && startDate.mDay > day) if (startDate.mYear == year && startDate.mMonth == month && startDate.mDay > day)
continue; continue;
result.insert(day); r.insert(day);
} }
} }
return r;
} }
int TimeLine::getTime(int year, int month, int day, std::vector<TimeRecord>* intervals) int TimeLine::getTime(int year, int month, int day, std::vector<TimeRecord>* intervals)
@ -630,7 +644,7 @@ int TimeLine::getTime(int year, int month, int day, std::vector<TimeRecord>* int
{ {
TimeRecord& tr = *lowest; 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) if (startDate.mYear > year)
break; break;
@ -670,31 +684,36 @@ int TimeLine::today()
{ {
int result = 0; int result = 0;
TimeArray::iterator lowIter = std::lower_bound(mData.begin(), mData.end(), QDate::currentDate(), // Find starting record related to today
[] (const TimeRecord& lhs, const QDate& rhs) // 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++) 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 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)); // Both are GMT time
QDateTime dayEnd(QDate::currentDate(), QTime(23, 59, 59)); 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) if (secondsTo > 0)
dayBegin = lowIter->startTime(); dayBegin = lowIter->startTime();
int secondsFrom = dayEnd.secsTo(lowIter->endTime().toLocalTime()); int64_t secondsFrom = lowIter->endTime() - dayEnd;
if (secondsFrom < 0) if (secondsFrom < 0)
dayEnd = lowIter->endTime().toLocalTime(); dayEnd = lowIter->endTime();
int secondsSpent = dayBegin.secsTo(dayEnd); int secondsSpent = dayEnd - dayBegin;
result += secondsSpent; result += secondsSpent;
} }
} }

View File

@ -12,7 +12,7 @@
#include <map> #include <map>
#include "SQLiteCpp/Database.h" #include "SQLiteCpp/Database.h"
typedef uint64_t Id; typedef int64_t Id;
class WorldId class WorldId
{ {
@ -92,14 +92,27 @@ public:
void setTaskId(Id id); void setTaskId(Id id);
// These methods work with local time // These methods work with local time
void getYears(std::set<int>& result); // Returns set of available years in timeline
void getMonthes(int year, std::set<int>& result); std::set<int> getYears();
void getDays(int year, int month, std::set<int>& result);
// Returns set of availables monthes in timeline for specified year
std::set<int> getMonthes(int year);
// Returns set of available days in timeline for specified year & month
std::set<int> getDays(int year, int month);
int getTime(int year, int month, int day, std::vector<TimeRecord>* intervals); int getTime(int year, int month, int day, std::vector<TimeRecord>* intervals);
// Returns number of seconds spent today
int today(); int today();
// Returns number of seconds spent in current month
int month(); int month();
// Returns number of seconds spent in interval
int getSum(const QDate& start, const QDate& finish); int getSum(const QDate& start, const QDate& finish);
// Checks if there duplicate & overllaping time intervals
bool duplicateDetected() const; bool duplicateDetected() const;
// Checks if specified interval has intersection // Checks if specified interval has intersection