From 1a34a29b7a721d59dcc1691e341bb5ae65016ce8 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 20 Jul 2010 16:37:47 -0700 Subject: [PATCH] Bug #438 - Reports sorting by end_time, start_time, and entry_time are ordered incorrectly - Fixed bug #438, correcting the sorting of the entry_time, start_time and end_time columns (thanks to Michelle Crane). - Reordered ChangeLog so that bugs, features are in sequence. Don't know why I did this. Some inner compulsion. - Deprecated silly start_time, end_time and entry_time columns, which are now (and were) superseded by start, end and entry columns with time formats. - Config.cpp now detects use of these deprecated fields and complains to the show command. - Date.cpp now uses the variable 'input' instead of 'mdy', which was confusing and implied that it contained a date without a time. - Obsoleted and removed Date::toStringWithTime, which ignored requested formats. - When checking for an epoch, Date::isEpoch just looked for strings of more than 8 digits. The additional restriction of less than or equal to 10 digits was added. This was breaking unit tests using the dateformat YMDHNS, which is reasonable. - Removed the obsolete field format hooks format-entry_time, format-start_time and format-end_time - Removed the obsolete field format hook unit tests hook.format-entry_time.t, hook.format-start_time.t and hook.format-end_time.t. - Removed use of deprecated field in hook.format-countdown_compact.t. - Added missing shortcut comparisons in Table::sort that was causing an unnecessary full parse of dates even if they were identical as strings. - Coded entry_time as a synonym for entry. Ditto for start_time and end_time. - Marked the new synonyms as deprecated. - Added bug.438.t unit test. - Added deprecated fields to the NEWS file. --- ChangeLog | 6 +- NEWS | 8 + src/Config.cpp | 7 +- src/Config.h | 1 + src/Date.cpp | 214 ++++++++++------------ src/Date.h | 1 - src/Hooks.cpp | 3 - src/TDB.cpp | 2 +- src/Table.cpp | 6 + src/command.cpp | 4 +- src/custom.cpp | 79 +------- src/tests/bug.438.t | 94 ++++++++++ src/tests/hook.format-countdown_compact.t | 2 +- src/tests/hook.format-end_time.t | 84 --------- src/tests/hook.format-entry_time.t | 82 --------- src/tests/hook.format-start_time.t | 83 --------- 16 files changed, 232 insertions(+), 444 deletions(-) create mode 100755 src/tests/bug.438.t delete mode 100755 src/tests/hook.format-end_time.t delete mode 100755 src/tests/hook.format-entry_time.t delete mode 100755 src/tests/hook.format-start_time.t diff --git a/ChangeLog b/ChangeLog index b29a18f97..9a0ca0392 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,11 +5,11 @@ + Added feature #189, that records the start and stop times as an annotation for a task. + Added feature #423, now custom report filters allow rc overrides. + + Added feature #428, preparing the new structure for the NEWS file. + Added feature #429, which improves the 'all' report to exclude deleted. tasks, provide a new sort order and include the 'end' column. + Added feature #431, which improves feedback after running the 'log' command. - + Added feature #428, preparing the new structure for the NEWS file. + New 'depends' column for custom reports. + New 'blocked' report for showing blocked tasks. + Improved man pages (thanks to Andy Lester). @@ -18,7 +18,9 @@ + Fixed bug #427, preventing the task edit command to parse annotation dates with spaces. + Fixed bug #433, making task command output more consistent. - + Fixed bug #434, allowing users to complete tasks with status 'waiting'. + + Fixed bug #434, allowing users to complete tasks with status 'waiting' + + Fixed bug #438, correcting the sorting of the entry_time, start_time + and end_time columns (thanks to Michelle Crane). + Fixed bug #439, which ignored dateformat.annotation for sparse annotations. ------ old releases ------------------------------ diff --git a/NEWS b/NEWS index b0787dfb4..5f58edfb4 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,14 @@ New configuration options in task 1.9.3 - journal.time, journal.time.start.annotation, journal.time.stop.annotation - +Newly deprecated features in task 1.9.3 + + - entry_time, start_time and end_time are now synonyms for the entry, start + and end fields. Recent enhancements to date formatting render these fields + obsolete. The 'task show' command warns of the use of these deprecated + fields. The synonyms will be removed in a future version of task. + + --- Task has been built and tested on the following configurations: diff --git a/src/Config.cpp b/src/Config.cpp index 23d7df651..a42ddaebc 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -486,7 +486,6 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data) contents << "# [Created by " << PACKAGE_STRING << " " -// << now.toStringWithTime () << now.toString ("m/d/Y H:N:S") << "]\n" << defaults.substr (0, loc + 14) @@ -641,12 +640,16 @@ std::string Config::checkForDeprecatedColumns () if (i->first.find ("report") == 0) { std::string value = get (i->first); - if (value.find ("entry_time") != std::string::npos) + if (value.find ("entry_time") != std::string::npos || + value.find ("start_time") != std::string::npos || + value.find ("end_time") != std::string::npos) deprecated.push_back (i->first); } } std::stringstream out; + out << std::endl; + if (deprecated.size ()) { out << "Your .taskrc file contains reports with deprecated columns. " diff --git a/src/Config.h b/src/Config.h index 925b72c21..036529d89 100644 --- a/src/Config.h +++ b/src/Config.h @@ -60,6 +60,7 @@ public: void all (std::vector &); std::string checkForDeprecatedColor (); + std::string checkForDeprecatedColumns (); public: File original_file; diff --git a/src/Date.cpp b/src/Date.cpp index 83a168a42..c4faad56c 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -82,7 +82,7 @@ Date::Date (const int m, const int d, const int y, } //////////////////////////////////////////////////////////////////////////////// -Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) +Date::Date (const std::string& input, const std::string& format /* = "m/d/Y" */) { int month = 0; int day = 0; @@ -92,14 +92,14 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) int second = 0; // Perhaps it is an epoch date, in string form? - if (isEpoch (mdy)) + if (isEpoch (input)) return; // Before parsing according to "format", perhaps this is a relative date? - if (isRelativeDate (mdy)) + if (isRelativeDate (input)) return; - unsigned int i = 0; // Index into mdy. + unsigned int i = 0; // Index into input. for (unsigned int f = 0; f < format.length (); ++f) { @@ -107,90 +107,90 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) { // Single or double digit. case 'm': - if (i >= mdy.length () || - ! isdigit (mdy[i])) + if (i >= input.length () || + ! isdigit (input[i])) { - throw std::string ("\"") + mdy + "\" is not a valid date (m)."; + throw std::string ("\"") + input + "\" is not a valid date (m)."; } - if (i + 1 < mdy.length () && - (mdy[i + 0] == '0' || mdy[i + 0] == '1') && - isdigit (mdy[i + 1])) + if (i + 1 < input.length () && + (input[i + 0] == '0' || input[i + 0] == '1') && + isdigit (input[i + 1])) { - month = atoi (mdy.substr (i, 2).c_str ()); + month = atoi (input.substr (i, 2).c_str ()); i += 2; } else { - month = atoi (mdy.substr (i, 1).c_str ()); + month = atoi (input.substr (i, 1).c_str ()); ++i; } break; case 'd': - if (i >= mdy.length () || - ! isdigit (mdy[i])) + if (i >= input.length () || + ! isdigit (input[i])) { - throw std::string ("\"") + mdy + "\" is not a valid date (d)."; + throw std::string ("\"") + input + "\" is not a valid date (d)."; } - if (i + 1 < mdy.length () && - (mdy[i + 0] == '0' || mdy[i + 0] == '1' || mdy[i + 0] == '2' || mdy[i + 0] == '3') && - isdigit (mdy[i + 1])) + if (i + 1 < input.length () && + (input[i + 0] == '0' || input[i + 0] == '1' || input[i + 0] == '2' || input[i + 0] == '3') && + isdigit (input[i + 1])) { - day = atoi (mdy.substr (i, 2).c_str ()); + day = atoi (input.substr (i, 2).c_str ()); i += 2; } else { - day = atoi (mdy.substr (i, 1).c_str ()); + day = atoi (input.substr (i, 1).c_str ()); ++i; } break; // Double digit. case 'y': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (y)."; + throw std::string ("\"") + input + "\" is not a valid date (y)."; } - year = atoi (mdy.substr (i, 2).c_str ()) + 2000; + year = atoi (input.substr (i, 2).c_str ()) + 2000; i += 2; break; case 'M': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (M)."; + throw std::string ("\"") + input + "\" is not a valid date (M)."; } - month = atoi (mdy.substr (i, 2).c_str ()); + month = atoi (input.substr (i, 2).c_str ()); i += 2; break; case 'D': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (D)."; + throw std::string ("\"") + input + "\" is not a valid date (D)."; } - day = atoi (mdy.substr (i, 2).c_str ()); + day = atoi (input.substr (i, 2).c_str ()); i += 2; break; case 'V': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (V)."; + throw std::string ("\"") + input + "\" is not a valid date (V)."; } i += 2; @@ -198,134 +198,134 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) // Quadruple digit. case 'Y': - if (i + 3 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1]) || - ! isdigit (mdy[i + 2]) || - ! isdigit (mdy[i + 3])) + if (i + 3 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1]) || + ! isdigit (input[i + 2]) || + ! isdigit (input[i + 3])) { - throw std::string ("\"") + mdy + "\" is not a valid date (Y)."; + throw std::string ("\"") + input + "\" is not a valid date (Y)."; } - year = atoi (mdy.substr (i, 4).c_str ()); + year = atoi (input.substr (i, 4).c_str ()); i += 4; break; // Short names with 3 characters case 'a': - if (i + 2 >= mdy.length () || - isdigit (mdy[i + 0]) || - isdigit (mdy[i + 1]) || - isdigit (mdy[i + 2])) + if (i + 2 >= input.length () || + isdigit (input[i + 0]) || + isdigit (input[i + 1]) || + isdigit (input[i + 2])) { - throw std::string ("\"") + mdy + "\" is not a valid date (a)."; + throw std::string ("\"") + input + "\" is not a valid date (a)."; } i += 3; break; case 'b': - if (i + 2 >= mdy.length () || - isdigit (mdy[i + 0]) || - isdigit (mdy[i + 1]) || - isdigit (mdy[i + 2])) + if (i + 2 >= input.length () || + isdigit (input[i + 0]) || + isdigit (input[i + 1]) || + isdigit (input[i + 2])) { - throw std::string ("\"") + mdy + "\" is not a valid date (b)."; + throw std::string ("\"") + input + "\" is not a valid date (b)."; } - month = Date::monthOfYear (mdy.substr (i, 3).c_str()); + month = Date::monthOfYear (input.substr (i, 3).c_str()); i += 3; break; // Long names case 'A': - if (i + 2 >= mdy.length () || - isdigit (mdy[i + 0]) || - isdigit (mdy[i + 1]) || - isdigit (mdy[i + 2])) + if (i + 2 >= input.length () || + isdigit (input[i + 0]) || + isdigit (input[i + 1]) || + isdigit (input[i + 2])) { - throw std::string ("\"") + mdy + "\" is not a valid date (A)."; + throw std::string ("\"") + input + "\" is not a valid date (A)."; } - i += Date::dayName( Date::dayOfWeek (mdy.substr (i, 3).c_str()) ).size(); + i += Date::dayName( Date::dayOfWeek (input.substr (i, 3).c_str()) ).size(); break; case 'B': - if (i + 2 >= mdy.length () || - isdigit (mdy[i + 0]) || - isdigit (mdy[i + 1]) || - isdigit (mdy[i + 2])) + if (i + 2 >= input.length () || + isdigit (input[i + 0]) || + isdigit (input[i + 1]) || + isdigit (input[i + 2])) { - throw std::string ("\"") + mdy + "\" is not a valid date (B)."; + throw std::string ("\"") + input + "\" is not a valid date (B)."; } - month = Date::monthOfYear (mdy.substr (i, 3).c_str()); + month = Date::monthOfYear (input.substr (i, 3).c_str()); i += Date::monthName(month).size(); break; // Single or double digit. case 'h': - if (i >= mdy.length () || - ! isdigit (mdy[i])) + if (i >= input.length () || + ! isdigit (input[i])) { - throw std::string ("\"") + mdy + "\" is not a valid date (h)."; + throw std::string ("\"") + input + "\" is not a valid date (h)."; } - if (i + 1 < mdy.length () && - (mdy[i + 0] == '0' || mdy[i + 0] == '1' || mdy[i + 0] == '2') && - isdigit (mdy[i + 1])) + if (i + 1 < input.length () && + (input[i + 0] == '0' || input[i + 0] == '1' || input[i + 0] == '2') && + isdigit (input[i + 1])) { - hour = atoi (mdy.substr (i, 2).c_str ()); + hour = atoi (input.substr (i, 2).c_str ()); i += 2; } else { - hour = atoi (mdy.substr (i, 1).c_str ()); + hour = atoi (input.substr (i, 1).c_str ()); ++i; } break; case 'H': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (H)."; + throw std::string ("\"") + input + "\" is not a valid date (H)."; } - hour = atoi (mdy.substr (i, 2).c_str ()); + hour = atoi (input.substr (i, 2).c_str ()); i += 2; break; case 'N': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (N)."; + throw std::string ("\"") + input + "\" is not a valid date (N)."; } - minute = atoi (mdy.substr (i, 2).c_str ()); + minute = atoi (input.substr (i, 2).c_str ()); i += 2; break; case 'S': - if (i + 1 >= mdy.length () || - ! isdigit (mdy[i + 0]) || - ! isdigit (mdy[i + 1])) + if (i + 1 >= input.length () || + ! isdigit (input[i + 0]) || + ! isdigit (input[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date (S)."; + throw std::string ("\"") + input + "\" is not a valid date (S)."; } - second = atoi (mdy.substr (i, 2).c_str ()); + second = atoi (input.substr (i, 2).c_str ()); i += 2; break; default: - if (i >= mdy.length () || - mdy[i] != format[f]) + if (i >= input.length () || + input[i] != format[f]) { - throw std::string ("\"") + mdy + "\" is not a valid date (DEFAULT)."; + throw std::string ("\"") + input + "\" is not a valid date (DEFAULT)."; } ++i; break; @@ -340,11 +340,11 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) year = default_year->tm_year + 1900; } - if (i < mdy.length ()) - throw std::string ("\"") + mdy + "\" is not a valid date in " + format + " format."; + if (i < input.length ()) + throw std::string ("\"") + input + "\" is not a valid date in " + format + " format."; if (!valid (month, day, year)) - throw std::string ("\"") + mdy + "\" is not a valid date (VALID)."; + throw std::string ("\"") + input + "\" is not a valid date (VALID)."; // Convert to epoch. struct tm t = {0}; @@ -457,19 +457,6 @@ 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) { @@ -846,8 +833,9 @@ time_t Date::operator- (const Date& rhs) //////////////////////////////////////////////////////////////////////////////// bool Date::isEpoch (const std::string& input) { - if (digitsOnly (input) && - input.length () > 8) + if (digitsOnly (input) && + input.length () > 8 && + input.length () <= 10 ) { mT = (time_t) atoi (input.c_str ()); return true; diff --git a/src/Date.h b/src/Date.h index 91635204c..0523e4366 100644 --- a/src/Date.h +++ b/src/Date.h @@ -50,7 +50,6 @@ public: std::string toISO (); 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, const int, const int, const int); static bool valid (const int, const int, const int); diff --git a/src/Hooks.cpp b/src/Hooks.cpp index ffc972f5a..de7ac7f07 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -226,11 +226,8 @@ Hooks::Hooks () validFieldEvents.push_back ("format-priority"); validFieldEvents.push_back ("format-priority_long"); validFieldEvents.push_back ("format-entry"); - validFieldEvents.push_back ("format-entry_time"); validFieldEvents.push_back ("format-start"); - validFieldEvents.push_back ("format-start_time"); validFieldEvents.push_back ("format-end"); - validFieldEvents.push_back ("format-end_time"); validFieldEvents.push_back ("format-due"); validFieldEvents.push_back ("format-countdown"); validFieldEvents.push_back ("format-countdown_compact"); diff --git a/src/TDB.cpp b/src/TDB.cpp index 3eadfdb07..d28c7ec86 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -838,7 +838,7 @@ void TDB::undo () row = table.addRow (); table.addCell (row, 0, "+++ current state "); // Note trailing space. - table.addCell (row, 1, "Change made " + lastChange.toStringWithTime (context.config.get ("dateformat"))); + table.addCell (row, 1, "Change made " + lastChange.toString (context.config.get ("dateformat"))); table.setRowColor (row, color_green); table.addRow (); diff --git a/src/Table.cpp b/src/Table.cpp index a7496fb8a..d4ebb8860 100644 --- a/src/Table.cpp +++ b/src/Table.cpp @@ -698,6 +698,9 @@ void Table::sort (std::vector & order) case ascendingDate: { + if (*left == *right) + break; + if ((std::string)*left != "" && (std::string)*right == "") break; @@ -716,6 +719,9 @@ void Table::sort (std::vector & order) case descendingDate: { + if (*left == *right) + break; + if ((std::string)*left != "" && (std::string)*right == "") break; diff --git a/src/command.cpp b/src/command.cpp index c3f521ab0..63acfb3f5 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -776,14 +776,13 @@ int handleShow (std::string &outs) table.addCell (row, 0, *i); table.addCell (row, 1, context.config.get (*i)); + // Look for unrecognized. if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) if (std::find (unrecognized.begin (), unrecognized.end (), *i) != unrecognized.end ()) table.setRowColor (row, error); } } - Color bold ("bold"); - out << std::endl << table.render () << (table.rowCount () == 0 ? "No matching configuration variables.\n" : "") @@ -806,6 +805,7 @@ int handleShow (std::string &outs) } out << context.config.checkForDeprecatedColor (); + out << context.config.checkForDeprecatedColumns (); // TODO Check for referenced but missing theme files. // TODO Check for referenced but missing string files. // TODO Check for referenced but missing tips files. diff --git a/src/custom.cpp b/src/custom.cpp index 5e3d4a25f..1dcb49db7 100644 --- a/src/custom.cpp +++ b/src/custom.cpp @@ -235,7 +235,7 @@ int handleCustomReport (const std::string& report, std::string &outs) } } - else if (*col == "entry") + else if (*col == "entry" || *col == "entry_time") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added"); table.setColumnWidth (columnCount, Table::minimum); @@ -255,27 +255,7 @@ int handleCustomReport (const std::string& report, std::string &outs) } } - 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")); - context.hooks.trigger ("format-entry_time", "entry_time", entered); - table.addCell (row, columnCount, entered); - } - } - } - - else if (*col == "start") + else if (*col == "start" || *col == "start_time") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started"); table.setColumnWidth (columnCount, Table::minimum); @@ -295,27 +275,7 @@ int handleCustomReport (const std::string& report, std::string &outs) } } - 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")); - context.hooks.trigger ("format-start_time", "start_time", started); - table.addCell (row, columnCount, started); - } - } - } - - else if (*col == "end") + else if (*col == "end" || *col == "end_time") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed"); table.setColumnWidth (columnCount, Table::minimum); @@ -335,28 +295,6 @@ int handleCustomReport (const std::string& report, std::string &outs) } } - else if (*col == "end_time") - { - table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Completed"); - table.setColumnWidth (columnCount, Table::minimum); - table.setColumnJustification (columnCount, Table::right); - - std::string format = context.config.get ("dateformat"); - - std::string ended; - for (unsigned int row = 0; row < tasks.size(); ++row) - { - ended = tasks[row].get ("end"); - if (ended.length ()) - { - Date dt (::atoi (ended.c_str ())); - ended = dt.toStringWithTime (format); - context.hooks.trigger ("format-end_time", "end_time", ended); - table.addCell (row, columnCount, ended); - } - } - } - else if (*col == "due") { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due"); @@ -645,8 +583,9 @@ int handleCustomReport (const std::string& report, std::string &outs) Table::ascendingPriority : Table::descendingPriority)); - else if (column == "entry" || column == "start" || column == "wait" || - column == "until" || column == "end") + else if (column == "entry" || column == "start" || column == "wait" || + column == "until" || column == "end" || column == "entry_time" || + column == "start_time" || column == "end_time") table.sortOn (columnIndex[column], (direction == '+' ? Table::ascendingDate : @@ -753,11 +692,11 @@ void validReportColumns (const std::vector & columns) *it != "priority" && *it != "priority_long" && *it != "entry" && - *it != "entry_time" && + *it != "entry_time" && // TODO Deprecated *it != "start" && - *it != "start_time" && + *it != "start_time" && // TODO Deprecated *it != "end" && - *it != "end_time" && + *it != "end_time" && // TODO Deprecated *it != "due" && *it != "countdown" && *it != "countdown_compact" && diff --git a/src/tests/bug.438.t b/src/tests/bug.438.t new file mode 100755 index 000000000..6fa97dfc8 --- /dev/null +++ b/src/tests/bug.438.t @@ -0,0 +1,94 @@ +#! /usr/bin/perl +################################################################################ +## task - a command line task list manager. +## +## Copyright 2006 - 2010, Paul Beckingham. +## All rights reserved. +## +## This program is free software; you can redistribute it and/or modify it under +## the terms of the GNU 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 General Public License for more +## details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, write to the +## +## Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, +## Boston, MA +## 02110-1301 +## USA +## +################################################################################ + +use strict; +use warnings; +use Test::More tests => 11; + +# Create the rc file. +if (open my $fh, '>', 'bug.rc') +{ + print $fh "data.location=.\n", + "dateformat=SNHDMY\n", + "report.foo.columns=entry,start,end,description\n", + "report.foo.dateformat=SNHDMY\n"; + close $fh; + ok (-r 'bug.rc', 'Created bug.rc'); +} + +# Bug #438: Reports sorting by end, start, and entry are ordered incorrectly, if +# time is included. + +# Ensure the two tasks have a 1 second delta in entry. +qx{../task rc:bug.rc add older}; +sleep 1; +qx{../task rc:bug.rc add newer}; + +my $output = qx{../task rc:bug.rc rc.report.foo.sort:entry+ foo}; +like ($output, qr/older.+newer/ms, 'sort:entry+ -> older newer'); + +$output = qx{../task rc:bug.rc rc.report.foo.sort:entry- foo}; +like ($output, qr/newer.+older/ms, 'sort:entry- -> newer older'); + +# Ensure the two tasks have a 1 second delta in start. +qx{../task rc:bug.rc start 1}; +sleep 1; +qx{../task rc:bug.rc start 2}; + +$output = qx{../task rc:bug.rc rc.report.foo.sort:start+ foo}; +like ($output, qr/older.+newer/ms, 'sort:start+ -> older newer'); + +$output = qx{../task rc:bug.rc rc.report.foo.sort:start- foo}; +like ($output, qr/newer.+older/ms, 'sort:start- -> newer older'); + +# Ensure the two tasks have a 1 second delta in end. +qx{../task rc:bug.rc done 1}; +sleep 1; +qx{../task rc:bug.rc done 2}; + +$output = qx{../task rc:bug.rc rc.report.foo.sort:end+ foo}; +like ($output, qr/older.+newer/ms, 'sort:end+ -> older newer'); + +$output = qx{../task rc:bug.rc rc.report.foo.sort:end- foo}; +like ($output, qr/newer.+older/ms, 'sort:end- -> newer older'); + +# Cleanup. +unlink 'pending.data'; +ok (!-r 'pending.data', 'Removed pending.data'); + +unlink 'completed.data'; +ok (!-r 'completed.data', 'Removed completed.data'); + +unlink 'undo.data'; +ok (!-r 'undo.data', 'Removed undo.data'); + +unlink 'bug.rc'; +ok (!-r 'bug.rc', 'Removed bug.rc'); + +exit 0; + diff --git a/src/tests/hook.format-countdown_compact.t b/src/tests/hook.format-countdown_compact.t index 6240e317c..08e0a46bd 100755 --- a/src/tests/hook.format-countdown_compact.t +++ b/src/tests/hook.format-countdown_compact.t @@ -34,7 +34,7 @@ use Test::More tests => 7; if (open my $fh, '>', 'hook.rc') { print $fh "data.location=.\n", - "report.long.columns=id,project,priority,entry,start_time,due,", + "report.long.columns=id,project,priority,entry,start,due,", "recur,countdown_compact,age,depends,tags,description\n", "hooks=on\n", "hook.format-countdown_compact=" . $ENV{'PWD'} . "/hook:countdown_compact\n"; diff --git a/src/tests/hook.format-end_time.t b/src/tests/hook.format-end_time.t deleted file mode 100755 index 76de1ce33..000000000 --- a/src/tests/hook.format-end_time.t +++ /dev/null @@ -1,84 +0,0 @@ -#! /usr/bin/perl -################################################################################ -## task - a command line task list manager. -## -## Copyright 2006 - 2010, Paul Beckingham. -## All rights reserved. -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU 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 General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, write to the -## -## Free Software Foundation, Inc., -## 51 Franklin Street, Fifth Floor, -## Boston, MA -## 02110-1301 -## USA -## -################################################################################ - -use strict; -use warnings; -use Test::More tests => 7; - -# Create the rc file. -if (open my $fh, '>', 'hook.rc') -{ - print $fh "data.location=.\n", - "report.completed.columns=end_time,project,priority,age,description\n", - "report.completed.sort=end_time+,priority-,project+\n", - "hooks=on\n", - "hook.format-end_time=" . $ENV{'PWD'} . "/hook:end_time\n"; - close $fh; - ok (-r 'hook.rc', 'Created hook.rc'); -} - -# Create the hook functions. -if (open my $fh, '>', 'hook') -{ - print $fh "function end_time (name, value)\n", - " value = '<' .. value .. '>'\n", - " return value, 0, nil\n", - "end\n"; - close $fh; - ok (-r 'hook', 'Created hook'); -} - -my $output = qx{../task rc:hook.rc version}; -if ($output =~ /PUC-Rio/) -{ - qx{../task rc:hook.rc add foo}; - qx{../task rc:hook.rc do 1}; - $output = qx{../task rc:hook.rc completed}; - - like ($output, qr/<\d+\/\d+\/\d+\s\d+:\d+:\d+>/, 'format-end_time hook end_time -> '); -} -else -{ - pass ('format-end_time hook end_time -> - skip: no Lua support'); -} - -# Cleanup. -unlink 'pending.data'; -ok (!-r 'pending.data', 'Removed pending.data'); - -unlink 'undo.data'; -ok (!-r 'undo.data', 'Removed undo.data'); - -unlink 'hook'; -ok (!-r 'hook', 'Removed hook'); - -unlink 'hook.rc'; -ok (!-r 'hook.rc', 'Removed hook.rc'); - -exit 0; - diff --git a/src/tests/hook.format-entry_time.t b/src/tests/hook.format-entry_time.t deleted file mode 100755 index 86ab54536..000000000 --- a/src/tests/hook.format-entry_time.t +++ /dev/null @@ -1,82 +0,0 @@ -#! /usr/bin/perl -################################################################################ -## task - a command line task list manager. -## -## Copyright 2006 - 2010, Paul Beckingham. -## All rights reserved. -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU 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 General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, write to the -## -## Free Software Foundation, Inc., -## 51 Franklin Street, Fifth Floor, -## Boston, MA -## 02110-1301 -## USA -## -################################################################################ - -use strict; -use warnings; -use Test::More tests => 7; - -# Create the rc file. -if (open my $fh, '>', 'hook.rc') -{ - print $fh "data.location=.\n", - "report.long.columns=id,project,priority,entry_time,start,due,recur,countdown,age,depends,tags,description\n", - "hooks=on\n", - "hook.format-entry_time=" . $ENV{'PWD'} . "/hook:entry_time\n"; - close $fh; - ok (-r 'hook.rc', 'Created hook.rc'); -} - -# Create the hook functions. -if (open my $fh, '>', 'hook') -{ - print $fh "function entry_time (name, value)\n", - " value = '<' .. value .. '>'\n", - " return value, 0, nil\n", - "end\n"; - close $fh; - ok (-r 'hook', 'Created hook'); -} - -my $output = qx{../task rc:hook.rc version}; -if ($output =~ /PUC-Rio/) -{ - qx{../task rc:hook.rc add foo}; - $output = qx{../task rc:hook.rc long}; - - like ($output, qr/<\d+\/\d+\/\d+\s\d+:\d+:\d+>/, 'format-entry_time hook entry_time -> '); -} -else -{ - pass ('format-entry_time hook entry_time -> - skip: no Lua support'); -} - -# Cleanup. -unlink 'pending.data'; -ok (!-r 'pending.data', 'Removed pending.data'); - -unlink 'undo.data'; -ok (!-r 'undo.data', 'Removed undo.data'); - -unlink 'hook'; -ok (!-r 'hook', 'Removed hook'); - -unlink 'hook.rc'; -ok (!-r 'hook.rc', 'Removed hook.rc'); - -exit 0; - diff --git a/src/tests/hook.format-start_time.t b/src/tests/hook.format-start_time.t deleted file mode 100755 index fa5d472ef..000000000 --- a/src/tests/hook.format-start_time.t +++ /dev/null @@ -1,83 +0,0 @@ -#! /usr/bin/perl -################################################################################ -## task - a command line task list manager. -## -## Copyright 2006 - 2010, Paul Beckingham. -## All rights reserved. -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU 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 General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, write to the -## -## Free Software Foundation, Inc., -## 51 Franklin Street, Fifth Floor, -## Boston, MA -## 02110-1301 -## USA -## -################################################################################ - -use strict; -use warnings; -use Test::More tests => 7; - -# Create the rc file. -if (open my $fh, '>', 'hook.rc') -{ - print $fh "data.location=.\n", - "report.long.columns=id,project,priority,entry,start_time,due,recur,countdown,age,depends,tags,description\n", - "hooks=on\n", - "hook.format-start_time=" . $ENV{'PWD'} . "/hook:start_time\n"; - close $fh; - ok (-r 'hook.rc', 'Created hook.rc'); -} - -# Create the hook functions. -if (open my $fh, '>', 'hook') -{ - print $fh "function start_time (name, value)\n", - " value = '<' .. value .. '>'\n", - " return value, 0, nil\n", - "end\n"; - close $fh; - ok (-r 'hook', 'Created hook'); -} - -my $output = qx{../task rc:hook.rc version}; -if ($output =~ /PUC-Rio/) -{ - qx{../task rc:hook.rc add foo}; - qx{../task rc:hook.rc start 1}; - $output = qx{../task rc:hook.rc long}; - - like ($output, qr/<\d+\/\d+\/\d+\s\d+:\d+:\d+>/, 'format-start_time hook start_time -> '); -} -else -{ - pass ('format-start_time hook start_time -> - skip: no Lua support'); -} - -# Cleanup. -unlink 'pending.data'; -ok (!-r 'pending.data', 'Removed pending.data'); - -unlink 'undo.data'; -ok (!-r 'undo.data', 'Removed undo.data'); - -unlink 'hook'; -ok (!-r 'hook', 'Removed hook'); - -unlink 'hook.rc'; -ok (!-r 'hook.rc', 'Removed hook.rc'); - -exit 0; -