- Fixed bug whereby the "dateformat" configuration variable was being used to display dates, but not parse them.

This commit is contained in:
Paul Beckingham 2008-06-11 01:14:22 -04:00
parent 07d1f63e31
commit 131693f617
11 changed files with 209 additions and 124 deletions

View file

@ -6,7 +6,7 @@ where the x represents a major version number, or architecture. The y
represents a feature release, and the z represents a patch.
1.2.0 (?)
-
+ Bug: "dateformat" configuration variable used to display dates, but not parse them.
------ reality -----------------------------------
@ -127,6 +127,7 @@ represents a feature release, and the z represents a patch.
+ File locking
+ retain deleted tasks
+ "task info ID" report showing all metadata
+ File format v2
[Development hiatus while planning for T, TDB API, new features and the future
of the project. Seeded to two testers for feedback, suggestions.]

View file

@ -57,30 +57,115 @@ Date::Date (const int m, const int d, const int y)
}
////////////////////////////////////////////////////////////////////////////////
Date::Date (const std::string& mdy)
Date::Date (const std::string& mdy, const std::string format /* = "m/d/Y" */)
{
size_t firstSlash = mdy.find ("/");
size_t secondSlash = mdy.find ("/", firstSlash + 1);
if (firstSlash != std::string::npos &&
secondSlash != std::string::npos)
int month = 0;
int day = 0;
int year = 0;
unsigned int i = 0; // Index into mdy.
for (unsigned int f = 0; f < format.length (); ++f)
{
int m = ::atoi (mdy.substr (0, firstSlash ).c_str ());
int d = ::atoi (mdy.substr (firstSlash + 1, secondSlash - firstSlash).c_str ());
int y = ::atoi (mdy.substr (secondSlash + 1, std::string::npos ).c_str ());
if (!valid (m, d, y))
switch (format[f])
{
// Single digit.
case 'm':
if (i >= mdy.length () ||
! ::isdigit (mdy[i]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
month = ::atoi (mdy.substr (i, 1).c_str ());
++i;
break;
case 'd':
if (i >= mdy.length () ||
! ::isdigit (mdy[i]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
day = ::atoi (mdy.substr (i, 1).c_str ());
++i;
break;
// Double digit.
case 'y':
if (i + 1 >= mdy.length () ||
! ::isdigit (mdy[i + 0]) ||
! ::isdigit (mdy[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
year = ::atoi (mdy.substr (i, 2).c_str ()) + 2000;
i += 2;
break;
case 'M':
if (i + 1 >= mdy.length () ||
! ::isdigit (mdy[i + 0]) ||
! ::isdigit (mdy[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
month = ::atoi (mdy.substr (i, 2).c_str ());
i += 2;
break;
case 'D':
if (i + 1 >= mdy.length () ||
! ::isdigit (mdy[i + 0]) ||
! ::isdigit (mdy[i + 1]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
day = ::atoi (mdy.substr (i, 2).c_str ());
i += 2;
break;
// 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]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
year = ::atoi (mdy.substr (i, 4).c_str ());
i += 4;
break;
default:
if (i >= mdy.length () ||
mdy[i] != format[f])
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
++i;
break;
}
}
if (!valid (month, day, year))
throw std::string ("\"") + mdy + "\" is not a valid date.";
// Duplicate Date::Date (const int, const int, const int);
struct tm t = {0};
t.tm_mday = d;
t.tm_mon = m - 1;
t.tm_year = y - 1900;
t.tm_mday = day;
t.tm_mon = month - 1;
t.tm_year = year - 1900;
mT = mktime (&t);
}
else
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
////////////////////////////////////////////////////////////////////////////////
Date::Date (const Date& rhs)
@ -118,15 +203,6 @@ void Date::toMDY (int& m, int& d, int& y)
////////////////////////////////////////////////////////////////////////////////
std::string Date::toString (const std::string& format /*= "m/d/Y"*/)
{
/*
int m, d, y;
toMDY (m, d, y);
char formatted [11];
sprintf (formatted, "%d/%d/%d", m, d, y);
return std::string (formatted);
*/
std::string formatted;
for (unsigned int i = 0; i < format.length (); ++i)
{

View file

@ -37,7 +37,7 @@ public:
Date ();
Date (time_t);
Date (const int, const int, const int);
Date (const std::string&);
Date (const std::string&, const std::string format = "m/d/Y");
Date (const Date&);
virtual ~Date ();

View file

@ -275,20 +275,8 @@ const std::string T::compose () const
int count = 0;
foreach (i, mAttributes)
{
std::string converted = i->second;
// Date attributes may need conversion to epoch.
if (i->first == "due" ||
i->first == "start" ||
i->first == "entry" ||
i->first == "end")
{
if (i->second.find ("/") != std::string::npos)
validDate (converted);
}
line += (count > 0 ? " " : "");
line += i->first + ":" + converted;
line += i->first + ":" + i->second;
++count;
}

View file

@ -659,6 +659,12 @@ void Table::suppressWS ()
mSuppressWS = true;
}
////////////////////////////////////////////////////////////////////////////////
void Table::setDateFormat (const std::string& dateFormat)
{
mDateFormat = dateFormat;
}
////////////////////////////////////////////////////////////////////////////////
int Table::rowCount ()
{
@ -771,8 +777,8 @@ void Table::sort (std::vector <int>& order)
else
{
Date dl ((std::string)*left);
Date dr ((std::string)*right);
Date dl ((std::string)*left, mDateFormat);
Date dr ((std::string)*right, mDateFormat);
if (dl > dr)
SWAP
}
@ -789,8 +795,8 @@ void Table::sort (std::vector <int>& order)
else
{
Date dl ((std::string)*left);
Date dr ((std::string)*right);
Date dl ((std::string)*left, mDateFormat);
Date dr ((std::string)*right, mDateFormat);
if (dl < dr)
SWAP
}

View file

@ -79,6 +79,7 @@ public:
void setCellBg (int, int, Text::color);
void suppressWS ();
void setDateFormat (const std::string&);
int rowCount ();
int columnCount ();
@ -128,6 +129,7 @@ private:
// Misc...
bool mSuppressWS;
std::string mDateFormat;
};
#endif

View file

@ -185,29 +185,13 @@ static bool isCommand (const std::string& candidate)
}
////////////////////////////////////////////////////////////////////////////////
bool validDate (std::string& date)
bool validDate (std::string& date, Config& conf)
{
size_t firstSlash = date.find ("/");
size_t secondSlash = date.find ("/", firstSlash + 1);
if (firstSlash != std::string::npos &&
secondSlash != std::string::npos)
{
int m = ::atoi (date.substr (0, firstSlash ).c_str ());
int d = ::atoi (date.substr (firstSlash + 1, secondSlash - firstSlash).c_str ());
int y = ::atoi (date.substr (secondSlash + 1, std::string::npos ).c_str ());
if (!Date::valid (m, d, y))
throw std::string ("\"") + date + "\" is not a valid date.";
Date test (date, conf.get ("dateformat", "m/d/Y"));
// Convert to epoch form.
Date dt (m, d, y);
time_t t;
dt.toEpoch (t);
char converted[12];
sprintf (converted, "%u", (unsigned int) t);
date = converted;
}
else
throw std::string ("Badly formed date - use the MM/DD/YYYY format");
char epoch[12];
sprintf (epoch, "%d", (int) test.toEpoch ());
date = epoch;
return true;
}
@ -227,7 +211,7 @@ static bool validPriority (std::string& input)
}
////////////////////////////////////////////////////////////////////////////////
static bool validAttribute (std::string& name, std::string& value)
static bool validAttribute (std::string& name, std::string& value, Config& conf)
{
guess ("attribute", attributes, name);
@ -235,7 +219,7 @@ static bool validAttribute (std::string& name, std::string& value)
guess ("color", colors, value);
else if (name == "due" && value != "")
validDate (value);
validDate (value, conf);
else if (name == "priority")
{
@ -335,7 +319,8 @@ static bool validSubstitution (
void parse (
std::vector <std::string>& args,
std::string& command,
T& task)
T& task,
Config& conf)
{
command = "";
@ -369,7 +354,7 @@ void parse (
std::string name = arg.substr (0, colon);
std::string value = arg.substr (colon + 1, std::string::npos);
if (validAttribute (name, value))
if (validAttribute (name, value, conf))
task.setAttribute (name, value);
}

View file

@ -70,6 +70,7 @@ void usage (Config& conf)
table.setColumnWidth (1, Table::minimum);
table.setColumnWidth (2, Table::flexible);
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
int row = table.addRow ();
table.addCell (row, 0, "Usage:");
@ -242,7 +243,7 @@ int main (int argc, char** argv)
std::string command;
T task;
parse (args, command, task);
parse (args, command, task, conf);
if (command == "add") handleAdd (tdb, task, conf);
else if (command == "projects") handleProjects (tdb, task, conf);
@ -326,6 +327,7 @@ void handleProjects (const TDB& tdb, T& task, Config& conf)
table.setColumnUnderline (1);
table.setColumnJustification (1, Table::right);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
foreach (i, unique)
{
@ -442,6 +444,8 @@ void handleList (const TDB& tdb, T& task, Config& conf)
table.sortOn (2, Table::descendingPriority);
table.sortOn (1, Table::ascendingCharacter);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
@ -581,6 +585,7 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("ID");
table.addColumn ("Project");
table.addColumn ("Pri");
@ -737,6 +742,7 @@ void handleCompleted (const TDB& tdb, T& task, Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("Done");
table.addColumn ("Project");
table.addColumn ("Description");
@ -852,6 +858,7 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("Name");
table.addColumn ("Value");
@ -1024,6 +1031,7 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("ID");
table.addColumn ("Project");
table.addColumn ("Pri");
@ -1279,6 +1287,7 @@ void handleReportSummary (const TDB& tdb, T& task, Config& conf)
table.setColumnJustification (3, Table::right);
table.sortOn (0, Table::ascendingCharacter);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
int barWidth = 30;
foreach (i, allProjects)
@ -1390,6 +1399,7 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("ID");
table.addColumn ("Project");
table.addColumn ("Pri");
@ -1638,6 +1648,7 @@ void handleReportHistory (const TDB& tdb, T& task, Config& conf)
// Now build the table.
Table table;
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("Year");
table.addColumn ("Month");
table.addColumn ("Added");
@ -1740,7 +1751,7 @@ void handleReportUsage (const TDB& tdb, T& task, Config& conf)
{
T task;
std::string commandName;
parse (args, commandName, task);
parse (args, commandName, task, conf);
usage[commandName]++;
}
@ -1759,6 +1770,7 @@ void handleReportUsage (const TDB& tdb, T& task, Config& conf)
table.setColumnUnderline (1);
table.setColumnJustification (1, Table::right);
table.sortOn (1, Table::descendingNumeric);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
foreach (i, usage)
{
@ -1788,6 +1800,7 @@ std::string renderMonth (
Config& conf)
{
Table table;
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn (" ");
table.addColumn ("Su");
table.addColumn ("Mo");
@ -1934,6 +1947,7 @@ void handleReportActive (const TDB& tdb, T& task, Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("ID");
table.addColumn ("Project");
table.addColumn ("Pri");
@ -2052,6 +2066,7 @@ void handleReportOverdue (const TDB& tdb, T& task, Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("ID");
table.addColumn ("Project");
table.addColumn ("Pri");
@ -2241,6 +2256,7 @@ void handleVersion (Config& conf)
// Create a table for output.
Table table;
table.setTableWidth (width);
table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
table.addColumn ("Config variable");
table.addColumn ("Value");
table.setColumnUnderline (0);

View file

@ -49,8 +49,8 @@ for (typeof (c) *foreach_p = & (c); \
++i)
// parse.cpp
void parse (std::vector <std::string>&, std::string&, T&);
bool validDate (std::string&);
void parse (std::vector <std::string>&, std::string&, T&, Config&);
bool validDate (std::string&, Config&);
// task.cpp
void handleAdd (const TDB&, T&, Config&);

View file

@ -1,5 +1,6 @@
t.t
tdb.t
date.t
pending.data
completed.data

View file

@ -9,77 +9,87 @@
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (46);
plan (52);
Date now;
Date yesterday;
yesterday -= 1;
t.ok (yesterday <= now, "yesterday <= now");
t.ok (yesterday < now, "yesterday < now");
t.notok (yesterday == now, "!(yesterday == now)");
t.ok (yesterday != now, "yesterday != now");
t.ok (now >= yesterday, "now >= yesterday");
t.ok (now > yesterday, "now > yesterday");
ok (yesterday <= now, "yesterday <= now");
ok (yesterday < now, "yesterday < now");
notok (yesterday == now, "!(yesterday == now)");
ok (yesterday != now, "yesterday != now");
ok (now >= yesterday, "now >= yesterday");
ok (now > yesterday, "now > yesterday");
t.ok (Date::valid (2, 29, 2008), "valid: 2/29/2008");
t.notok (Date::valid (2, 29, 2007), "invalid: 2/29/2007");
ok (Date::valid (2, 29, 2008), "valid: 2/29/2008");
notok (Date::valid (2, 29, 2007), "invalid: 2/29/2007");
t.ok (Date::leapYear (2008), "2008 is a leap year");
t.notok (Date::leapYear (2007), "2007 is not a leap year");
ok (Date::leapYear (2008), "2008 is a leap year");
notok (Date::leapYear (2007), "2007 is not a leap year");
t.is (Date::daysInMonth (2, 2008), 29, "29 days in February 2008");
t.is (Date::daysInMonth (2, 2007), 28, "28 days in February 2007");
is (Date::daysInMonth (2, 2008), 29, "29 days in February 2008");
is (Date::daysInMonth (2, 2007), 28, "28 days in February 2007");
t.is (Date::monthName (1), "January", "1 = January");
t.is (Date::monthName (2), "February", "2 = February");
t.is (Date::monthName (3), "March", "3 = March");
t.is (Date::monthName (4), "April", "4 = April");
t.is (Date::monthName (5), "May", "5 = May");
t.is (Date::monthName (6), "June", "6 = June");
t.is (Date::monthName (7), "July", "7 = July");
t.is (Date::monthName (8), "August", "8 = August");
t.is (Date::monthName (9), "September", "9 = September");
t.is (Date::monthName (10), "October", "10 = October");
t.is (Date::monthName (11), "November", "11 = November");
t.is (Date::monthName (12), "December", "12 = December");
is (Date::monthName (1), "January", "1 = January");
is (Date::monthName (2), "February", "2 = February");
is (Date::monthName (3), "March", "3 = March");
is (Date::monthName (4), "April", "4 = April");
is (Date::monthName (5), "May", "5 = May");
is (Date::monthName (6), "June", "6 = June");
is (Date::monthName (7), "July", "7 = July");
is (Date::monthName (8), "August", "8 = August");
is (Date::monthName (9), "September", "9 = September");
is (Date::monthName (10), "October", "10 = October");
is (Date::monthName (11), "November", "11 = November");
is (Date::monthName (12), "December", "12 = December");
t.is (Date::dayName (0), "Sunday", "0 == Sunday");
t.is (Date::dayName (1), "Monday", "1 == Monday");
t.is (Date::dayName (2), "Tuesday", "2 == Tuesday");
t.is (Date::dayName (3), "Wednesday", "3 == Wednesday");
t.is (Date::dayName (4), "Thursday", "4 == Thursday");
t.is (Date::dayName (5), "Friday", "5 == Friday");
t.is (Date::dayName (6), "Saturday", "6 == Saturday");
is (Date::dayName (0), "Sunday", "0 == Sunday");
is (Date::dayName (1), "Monday", "1 == Monday");
is (Date::dayName (2), "Tuesday", "2 == Tuesday");
is (Date::dayName (3), "Wednesday", "3 == Wednesday");
is (Date::dayName (4), "Thursday", "4 == Thursday");
is (Date::dayName (5), "Friday", "5 == Friday");
is (Date::dayName (6), "Saturday", "6 == Saturday");
Date happyNewYear (1, 1, 2008);
t.is (happyNewYear.dayOfWeek (), 2, "1/1/2008 == Tuesday");
t.is (happyNewYear.month (), 1, "1/1/2008 == January");
t.is (happyNewYear.day (), 1, "1/1/2008 == 1");
t.is (happyNewYear.year (), 2008, "1/1/2008 == 2008");
is (happyNewYear.dayOfWeek (), 2, "1/1/2008 == Tuesday");
is (happyNewYear.month (), 1, "1/1/2008 == January");
is (happyNewYear.day (), 1, "1/1/2008 == 1");
is (happyNewYear.year (), 2008, "1/1/2008 == 2008");
t.is (now - yesterday, 1, "today - yesterday == 1");
is (now - yesterday, 1, "today - yesterday == 1");
t.is (happyNewYear.toString (), "1/1/2008", "toString 1/1/2008");
is (happyNewYear.toString (), "1/1/2008", "toString 1/1/2008");
int m, d, y;
happyNewYear.toMDY (m, d, y);
t.is (m, 1, "1/1/2008 == January");
t.is (d, 1, "1/1/2008 == 1");
t.is (y, 2008, "1/1/2008 == 2008");
is (m, 1, "1/1/2008 == January");
is (d, 1, "1/1/2008 == 1");
is (y, 2008, "1/1/2008 == 2008");
Date epoch (9, 8, 2001);
t.ok ((int)epoch.toEpoch () < 1000000000, "9/8/2001 < 1,000,000,000");
ok ((int)epoch.toEpoch () < 1000000000, "9/8/2001 < 1,000,000,000");
epoch += 86400;
t.ok ((int)epoch.toEpoch () > 1000000000, "9/9/2001 > 1,000,000,000");
ok ((int)epoch.toEpoch () > 1000000000, "9/9/2001 > 1,000,000,000");
Date fromEpoch (epoch.toEpoch ());
t.is (fromEpoch.toString (), epoch.toString (), "ctor (time_t)");
is (fromEpoch.toString (), epoch.toString (), "ctor (time_t)");
Date fromString ("1/1/2008");
t.is (fromString.month (), 1, "ctor (std::string) -> m");
t.is (fromString.day (), 1, "ctor (std::string) -> d");
t.is (fromString.year (), 2008, "ctor (std::string) -> y");
Date fromString1 ("1/1/2008");
is (fromString1.month (), 1, "ctor (std::string) -> m");
is (fromString1.day (), 1, "ctor (std::string) -> d");
is (fromString1.year (), 2008, "ctor (std::string) -> y");
Date fromString2 ("1/1/2008", "m/d/Y");
is (fromString2.month (), 1, "ctor (std::string) -> m");
is (fromString2.day (), 1, "ctor (std::string) -> d");
is (fromString2.year (), 2008, "ctor (std::string) -> y");
Date fromString3 ("20080101", "YMD");
is (fromString3.month (), 1, "ctor (std::string) -> m");
is (fromString3.day (), 1, "ctor (std::string) -> d");
is (fromString3.year (), 2008, "ctor (std::string) -> y");
return 0;
}