diff --git a/ChangeLog b/ChangeLog index 9465eedf5..eb34d5bee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,9 @@ + The 'version' command now complains about use of deprecated color names and duplicate entries. + Task now supports nested .taskrc files using the "include /path" directive. + + The 'entry', 'start' and 'end' columns now have equivalents that include the + time, and are called 'entry_time', 'start_time', and 'end_time', for use in + custom reports. + Fixed bug that showed a calendar for the year 2037 when 'task calendar due' was run, and there are no tasks with due dates. + Fixed bug #316 which caused the timesheet report to display an oddly sorted diff --git a/NEWS b/NEWS index 9866619c4..8deead819 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,10 @@ New Features in task 1.9 - 256-color support - Support for coloring alternate lines of output. Remember that old green and white fan-fold printer paper? + - Supports nested .taskrc files with the new "include" statement" + - New columns that include the time as well as date + - New attribute modifiers + - Improved .taskrc validation Please refer to the ChangeLog file for full details. There are too many to list here. diff --git a/src/Date.cpp b/src/Date.cpp index ddfccadc9..ba6294952 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -271,6 +271,19 @@ const std::string Date::toString (const std::string& format /*= "m/d/Y" */) cons return formatted; } +//////////////////////////////////////////////////////////////////////////////// +const std::string Date::toStringWithTime (const std::string& format /*= "m/d/Y" */) const +{ + // Format as above. + std::string formatted = toString (format); + + char buffer[12]; + sprintf (buffer, " %d:%02d:%02d", hour (), minute (), second ()); + formatted += buffer; + + return formatted; +} + //////////////////////////////////////////////////////////////////////////////// bool Date::valid (const std::string& input, const std::string& format) { @@ -456,6 +469,27 @@ int Date::year () const return t->tm_year + 1900; } +//////////////////////////////////////////////////////////////////////////////// +int Date::hour () const +{ + struct tm* t = localtime (&mT); + return t->tm_hour; +} + +//////////////////////////////////////////////////////////////////////////////// +int Date::minute () const +{ + struct tm* t = localtime (&mT); + return t->tm_min; +} + +//////////////////////////////////////////////////////////////////////////////// +int Date::second () const +{ + struct tm* t = localtime (&mT); + return t->tm_sec; +} + //////////////////////////////////////////////////////////////////////////////// bool Date::operator== (const Date& rhs) { diff --git a/src/Date.h b/src/Date.h index 212df458f..2785e0e98 100644 --- a/src/Date.h +++ b/src/Date.h @@ -47,6 +47,7 @@ public: std::string toEpochString (); void toMDY (int&, int&, int&); const std::string toString (const std::string& format = "m/d/Y") const; + const std::string toStringWithTime (const std::string& format = "m/d/Y") const; static bool valid (const std::string&, const std::string& format = "m/d/Y"); static bool valid (const int, const int, const int); @@ -63,6 +64,9 @@ public: int year () const; int weekOfYear (int) const; int dayOfWeek () const; + int hour () const; + int minute () const; + int second () const; bool operator== (const Date&); bool operator!= (const Date&); diff --git a/src/custom.cpp b/src/custom.cpp index 160f3214f..bc2476f19 100644 --- a/src/custom.cpp +++ b/src/custom.cpp @@ -237,6 +237,25 @@ int runCustomReport ( } } + else if (*col == "entry_time") + { + table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added"); + table.setColumnWidth (columnCount, Table::minimum); + table.setColumnJustification (columnCount, Table::right); + + std::string entered; + for (unsigned int row = 0; row < tasks.size(); ++row) + { + entered = tasks[row].get ("entry"); + if (entered.length ()) + { + Date dt (::atoi (entered.c_str ())); + entered = dt.toStringWithTime (context.config.get ("dateformat", "m/d/Y")); + table.addCell (row, columnCount, entered); + } + } + } + else if (*col == "start") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started"); @@ -256,6 +275,25 @@ int runCustomReport ( } } + else if (*col == "start_time") + { + table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started"); + table.setColumnWidth (columnCount, Table::minimum); + table.setColumnJustification (columnCount, Table::right); + + std::string started; + for (unsigned int row = 0; row < tasks.size(); ++row) + { + started = tasks[row].get ("start"); + if (started.length ()) + { + Date dt (::atoi (started.c_str ())); + started = dt.toStringWithTime (context.config.get ("dateformat", "m/d/Y")); + table.addCell (row, columnCount, started); + } + } + } + else if (*col == "end") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed"); @@ -275,6 +313,25 @@ int runCustomReport ( } } + else if (*col == "end_time") + { + table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed"); + table.setColumnWidth (columnCount, Table::minimum); + table.setColumnJustification (columnCount, Table::right); + + std::string started; + for (unsigned int row = 0; row < tasks.size(); ++row) + { + started = tasks[row].get ("end"); + if (started.length ()) + { + Date dt (::atoi (started.c_str ())); + started = dt.toStringWithTime (context.config.get ("dateformat", "m/d/Y")); + table.addCell (row, columnCount, started); + } + } + } + else if (*col == "due") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due"); @@ -576,8 +633,11 @@ void validReportColumns (const std::vector & columns) *it != "project" && *it != "priority" && *it != "entry" && + *it != "entry_time" && *it != "start" && + *it != "start_time" && *it != "end" && + *it != "end_time" && *it != "due" && *it != "age" && *it != "age_compact" &&