mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
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.
This commit is contained in:
parent
a57326a026
commit
1a34a29b7a
16 changed files with 232 additions and 444 deletions
|
@ -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 ------------------------------
|
||||
|
|
8
NEWS
8
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:
|
||||
|
|
|
@ -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. "
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
void all (std::vector <std::string>&);
|
||||
|
||||
std::string checkForDeprecatedColor ();
|
||||
std::string checkForDeprecatedColumns ();
|
||||
|
||||
public:
|
||||
File original_file;
|
||||
|
|
214
src/Date.cpp
214
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -698,6 +698,9 @@ void Table::sort (std::vector <int>& order)
|
|||
|
||||
case ascendingDate:
|
||||
{
|
||||
if (*left == *right)
|
||||
break;
|
||||
|
||||
if ((std::string)*left != "" && (std::string)*right == "")
|
||||
break;
|
||||
|
||||
|
@ -716,6 +719,9 @@ void Table::sort (std::vector <int>& order)
|
|||
|
||||
case descendingDate:
|
||||
{
|
||||
if (*left == *right)
|
||||
break;
|
||||
|
||||
if ((std::string)*left != "" && (std::string)*right == "")
|
||||
break;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 <std::string>& 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" &&
|
||||
|
|
94
src/tests/bug.438.t
Executable file
94
src/tests/bug.438.t
Executable file
|
@ -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;
|
||||
|
|
@ -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";
|
||||
|
|
|
@ -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 -> <end_time>');
|
||||
}
|
||||
else
|
||||
{
|
||||
pass ('format-end_time hook end_time -> <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;
|
||||
|
|
@ -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 -> <entry_time>');
|
||||
}
|
||||
else
|
||||
{
|
||||
pass ('format-entry_time hook entry_time -> <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;
|
||||
|
|
@ -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 -> <start_time>');
|
||||
}
|
||||
else
|
||||
{
|
||||
pass ('format-start_time hook start_time -> <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;
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue