Compare commits

...

14 Commits

Author SHA1 Message Date
Paul Beckingham
c393d47cdf - Corrected bogus .gitignore entry that blocked src/tests/Makefile. 2008-06-13 01:51:51 -04:00
Paul Beckingham
3525b6db2c - Added Bruce Israel for his suggestion of subprojects. 2008-06-13 01:25:38 -04:00
Paul Beckingham
d50efe5e27 - Updated versions 2008-06-13 01:18:53 -04:00
Paul Beckingham
2c0a1ddb3a - Added new feature for 1.2.0 2008-06-13 01:15:49 -04:00
Paul Beckingham
d7ac37783c - Updated documentation to include subproject discussions.
- Added Richard Querin to the AUTHORS file for his contribution of ideas and a .deb package.
2008-06-13 01:02:17 -04:00
Paul Beckingham
03bb50c4ea - Corrected copyright display. 2008-06-13 00:19:54 -04:00
Paul Beckingham
1535010ac9 - "Age" column is now optional for the "list" and "next" reports. 2008-06-13 00:18:28 -04:00
Paul Beckingham
8d90035bbc - Now properly parses dates according to specified date format. 2008-06-12 23:58:58 -04:00
Paul Beckingham
fd7bb9daa9 - Support subprojects. 2008-06-11 02:19:00 -04:00
Paul Beckingham
88b12bc66a - Added caseless keyword comparison to the autocolorization rules. 2008-06-11 01:47:11 -04:00
Paul Beckingham
a8ac82ca22 - Added "What's new in 1.2.0" section. 2008-06-11 01:45:07 -04:00
Paul Beckingham
438f3cb134 - Added caseless comparison when searching keywords in the description. 2008-06-11 01:40:01 -04:00
Paul Beckingham
131693f617 - Fixed bug whereby the "dateformat" configuration variable was being used to display dates, but not parse them. 2008-06-11 01:14:22 -04:00
Paul Beckingham
07d1f63e31 - Bumped version to 1.2.0 2008-06-10 22:59:43 -04:00
20 changed files with 533 additions and 219 deletions

View File

@@ -5,4 +5,6 @@ With thanks to:
Eugene Kramer
SK
Damian Glenny
Richard Querin
Bruce Israel

View File

@@ -5,11 +5,22 @@ Version numbers are of the form:
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 (?)
- Command line specification of alternate .taskrc file
------ plans -------------------------------------
Configurable columns in reports
------ reality -----------------------------------
1.2.0 (6/13/2008)
+ Bug: "dateformat" configuration variable used to display dates, but
not parse them
+ "task list x" now performs a caseless comparison between "x" and the
description
+ Task sub projects supported
+ "showage" confguration determines whether "Age" column appears on the
"list" and "next" reports
+ Improved TUTORIAL
1.1.0 (6/7/2008)
+ "blanklines" configuration to stop displaying unnecessary white
space and thus work better on small-screen devices
@@ -127,6 +138,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.]

2
NEWS
View File

@@ -1,4 +1,4 @@
Welcome to Task 1.1.0.
Welcome to Task 1.2.0.
Task has been built and tested on the following configurations:

View File

@@ -1,4 +1,4 @@
Task program tutorial, for version 1.1.0
Task program tutorial, for version 1.2.0
----------------------------------------
This guide shows how to quickly set up the task program, and become proficient
@@ -21,10 +21,10 @@ Build the task program according to the directions in the INSTALL file. This
transcript illustrates a typical installation:
% ls
task-1.1.0.tar.gz
% gunzip task-1.1.0.tar.gz
% tar xf task-1.1.0.tar
% cd task-1.1.0
task-1.2.0.tar.gz
% gunzip task-1.2.0.tar.gz
% tar xf task-1.2.0.tar
% cd task-1.2.0
% ./configure
...
% make
@@ -67,7 +67,7 @@ interface. Let us take a look at those tasks:
% task ls
ID Project Pri Description
ID Project Pri Description
1 Book plane ticket
2 Rent a tux
3 Reserve a rental car
@@ -97,7 +97,7 @@ a project to these tasks:
% task 4 project:Family
% task ls
ID Project Pri Description
ID Project Pri Description
3 Family Send John a birthday card
2 Wedding Reserve a rental car
1 Wedding Book plane ticket
@@ -107,12 +107,46 @@ attributes changed (project, for example), the ids are prone to change. But the
id numbers will remain valid until the next 'ls' command is run. You should
only use the ids from the most recent 'ls' command. The ids change, because
task is always trying to use small numbers so that it is easy for you to enter
them correctly. Now that projects are assigned, we can look at just the Wedding
project tasks:
them correctly.
Subprojects are supported. If you have a project "Wedding", you can specify
that a task is a subproject "Transport" of "Wedding" by assigning the project
"Wedding.Transport". Let's do this:
% task 2 project:Wedding.Transport
% task ls
ID Project Pri Description
3 Family Send John a birthday card
2 Wedding.Transport Reserve a rental car
1 Wedding Book plane ticket
Task matches the leftmost part of the project when searching, so projects
may be abbreviated:
% task ls project:Wedding.Tra
ID Project Pri Description
2 Wedding.Transport Reserve a rental car
This way of matching projects can be used to see all tasks under the
"Wedding" project and all subprojects:
% task ls project:Wedding
ID Project Pri Description
ID Project Pri Description
2 Wedding.Transport Reserve a rental car
1 Wedding Book plane ticket
Let's reassign 2 back to the "Wedding" project:
% task 2 project:Wedding
Now that projects are assigned, we can look at just the Wedding project tasks:
% task ls project:Wedding
ID Project Pri Description
1 Wedding Book plane ticket
2 Wedding Reserve a rental car
@@ -121,14 +155,14 @@ could also have requested:
% task ls ticket plane
ID Project Pri Description
ID Project Pri Description
1 Wedding Book plane ticket
Now let's prioritize. Priorities can be H, M or L (High, Medium, Low).
% task ls
ID Project Pri Description
ID Project Pri Description
3 Family Send John a birthday card
2 Wedding Reserve a rental car
1 Wedding Book plane ticket
@@ -140,7 +174,7 @@ Now let's prioritize. Priorities can be H, M or L (High, Medium, Low).
% task 3 pri:H
% task ls
ID Project Pri Description
ID Project Pri Description
3 Family H Send John a birthday card
1 Wedding H Book plane ticket
2 Wedding M Reserve a rental car
@@ -161,7 +195,7 @@ command provides more:
% task list
ID Project Pri Due Active Age Description
ID Project Pri Due Active Age Description
3 Family H 4 mins Send John a birthday card
1 Wedding H 5 mins Book plane ticket
2 Wedding M 5 mins Reserve a rental car
@@ -173,7 +207,7 @@ sorted by due date, then priority. Let's add due dates:
% task 1 due:7/31/2008
% task list
ID Project Pri Due Active Age Description
ID Project Pri Due Active Age Description
3 Family H 6/25/2008 6 mins Send John a birthday card
1 Wedding H 7/31/2008 7 mins Book plane ticket
2 Wedding M 7 mins Reserve a rental car
@@ -212,7 +246,7 @@ labels.
% task list
ID Project Pri Due Active Age Description
ID Project Pri Due Active Age Description
3 Family H 6/25/2008 8 mins Send John a birthday card
1 Wedding H 7/31/2008 9 mins Book plane ticket
2 Wedding M 9 mins Reserve a rental car
@@ -224,7 +258,7 @@ labels.
% task list +phone
ID Project Pri Due Active Age Description
ID Project Pri Due Active Age Description
1 Wedding H 7/31/2008 9 mins Book plane ticket
2 Wedding M 9 mins Reserve a rental car
@@ -266,7 +300,7 @@ with no arguments will generate a help message that lists all these commands.
% task summary
Project Remaining Avg age Complete 0% 100%
Project Remaining Avg age Complete 0% 100%
Errands 1 3 days 50% XXXXXXXXXXXXXXXX
Birthdays 3 7 mths 0%
Car 2 2 wks 25% XXXXXXXXX
@@ -316,14 +350,14 @@ with no arguments will generate a help message that lists all these commands.
% task list
ID Project Pri Due Active Age Description
ID Project Pri Due Active Age Description
12 Errand L Remember to deposit check
...
% task start 12
% task list
ID Project Pri Due Active Age Description
ID Project Pri Due Active Age Description
12 Errand L * 3 days Remember to deposit check
...
@@ -351,7 +385,7 @@ with no arguments will generate a help message that lists all these commands.
This report shows you an overview of how many tasks were added, completed and
deleted, by month. It looks like this:
% task history
% task history
Year Month Added Completed Deleted Net
2008 March 21 16 0 5
@@ -409,14 +443,14 @@ with no arguments will generate a help message that lists all these commands.
% task ls
ID Project Pri Description
ID Project Pri Description
12 Errand L Remember to deposit chekc
...
% task 12 Remember to deposit bonus check
% task ls
ID Project Pri Description
ID Project Pri Description
12 Errand L Remember to deposit bonus check
...
@@ -430,14 +464,14 @@ with no arguments will generate a help message that lists all these commands.
% task ls
ID Project Pri Description
ID Project Pri Description
12 Errand L Remember to deposit chekc
...
% task 12 /chekc/check/
% task ls
ID Project Pri Description
ID Project Pri Description
12 Errand L Remember to deposit check
...
@@ -650,6 +684,9 @@ Configuring Task
YMD 20080607
m-d-y 6-7-08
showage May be "yes" or "no". Determines whether the "Age"
column appears on the "list" and "next" reports.
color May be "on" or "off". Determines whether task uses
color.
@@ -662,7 +699,7 @@ Configuring Task
color.active below) and one optional background color.
color.tagged
For example, the value may be:
bold_red on_bright_yellow
color.tag.X Colors any task that has the tag X.

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(task, 1.1.0, bugs@beckingham.net)
AC_INIT(task, 1.2.0, bugs@beckingham.net)
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([src/task.cpp])
AC_CONFIG_HEADER([auto.h])

2
src/.gitignore vendored
View File

@@ -1,2 +1,2 @@
Makefile
./Makefile
*.o

View File

@@ -136,7 +136,7 @@ void Config::createDefault (const std::string& file)
set ("data.location", taskDir);
set ("command.logging", "off");
set ("confirmation", "yes");
set ("next", 2);
set ("next", 1);
set ("curses", "on");
set ("color", "on");
set ("color.overdue", "red");

View File

@@ -57,29 +57,134 @@ 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 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))
throw std::string ("\"") + mdy + "\" is not a valid date.";
int month = 0;
int day = 0;
int year = 0;
// 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;
unsigned int i = 0; // Index into mdy.
for (unsigned int f = 0; f < format.length (); ++f)
{
switch (format[f])
{
// Single or double digit.
case 'm':
if (i >= mdy.length () ||
! ::isdigit (mdy[i]))
{
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
if (i + 1 < mdy.length () &&
mdy[i + 0] == '1' &&
(mdy[i + 1] == '0' || mdy[i + 1] == '1' || mdy[i + 1] == '2'))
{
month = ::atoi (mdy.substr (i, 2).c_str ());
i += 2;
}
else
{
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.";
}
if (i + 1 < mdy.length () &&
(mdy[i + 0] == '1' || mdy[i + 0] == '2' || mdy[i + 0] == '3') &&
::isdigit (mdy[i + 1]))
{
day = ::atoi (mdy.substr (i, 2).c_str ());
i += 2;
}
else
{
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 = day;
t.tm_mon = month - 1;
t.tm_year = year - 1900;
mT = mktime (&t);
}
else
throw std::string ("\"") + mdy + "\" is not a valid date.";
}
////////////////////////////////////////////////////////////////////////////////
@@ -118,15 +223,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

@@ -210,8 +210,9 @@ void autoColorize (T& task, Text::color& fg, Text::color& bg)
{
if (it->first.substr (0, 14) == "color.keyword.")
{
std::string value = it->first.substr (14, std::string::npos);
if (task.getDescription ().find (value) != std::string::npos)
std::string value = lowerCase (it->first.substr (14, std::string::npos));
std::string desc = lowerCase (task.getDescription ());
if (desc.find (value) != std::string::npos)
{
fg = gsFg[it->first];
bg = gsBg[it->first];

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)
{
@@ -407,6 +409,8 @@ void handleList (const TDB& tdb, T& task, Config& conf)
initializeColorRules (conf);
bool showAge = conf.get ("showage", true);
// Create a table for output.
Table table;
table.setTableWidth (width);
@@ -415,7 +419,7 @@ void handleList (const TDB& tdb, T& task, Config& conf)
table.addColumn ("Pri");
table.addColumn ("Due");
table.addColumn ("Active");
table.addColumn ("Age");
if (showAge) table.addColumn ("Age");
table.addColumn ("Description");
table.setColumnUnderline (0);
@@ -424,27 +428,29 @@ void handleList (const TDB& tdb, T& task, Config& conf)
table.setColumnUnderline (3);
table.setColumnUnderline (4);
table.setColumnUnderline (5);
table.setColumnUnderline (6);
if (showAge) table.setColumnUnderline (6);
table.setColumnWidth (0, Table::minimum);
table.setColumnWidth (1, Table::minimum);
table.setColumnWidth (2, Table::minimum);
table.setColumnWidth (3, Table::minimum);
table.setColumnWidth (4, Table::minimum);
table.setColumnWidth (5, Table::minimum);
table.setColumnWidth (6, Table::flexible);
if (showAge) table.setColumnWidth (5, Table::minimum);
table.setColumnWidth ((showAge ? 6 : 5), Table::flexible);
table.setColumnJustification (0, Table::right);
table.setColumnJustification (3, Table::right);
table.setColumnJustification (5, Table::right);
if (showAge) table.setColumnJustification (5, Table::right);
table.sortOn (3, Table::ascendingDate);
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 (), ' ');
split (descWords, lowerCase (task.getDescription ()), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
@@ -460,9 +466,10 @@ void handleList (const TDB& tdb, T& task, Config& conf)
T refTask (tasks[i]);
// Apply description filter.
std::string desc = lowerCase (refTask.getDescription ());
unsigned int matches = 0;
for (unsigned int w = 0; w < descWords.size (); ++w)
if (refTask.getDescription ().find (descWords[w]) != std::string::npos)
if (desc.find (descWords[w]) != std::string::npos)
++matches;
if (matches == descWords.size ())
@@ -470,8 +477,16 @@ void handleList (const TDB& tdb, T& task, Config& conf)
// Apply attribute filter.
matches = 0;
foreach (a, attrList)
if (a->second == refTask.getAttribute (a->first))
{
if (a->first == "project")
{
if (a->second.length () <= refTask.getAttribute (a->first).length ())
if (a->second == refTask.getAttribute (a->first).substr (0, a->second.length ()))
++matches;
}
else if (a->second == refTask.getAttribute (a->first))
++matches;
}
if (matches == attrList.size ())
{
@@ -488,7 +503,7 @@ void handleList (const TDB& tdb, T& task, Config& conf)
bool overdue = false;
Date now;
std::string due = refTask.getAttribute ("due");
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -504,7 +519,7 @@ void handleList (const TDB& tdb, T& task, Config& conf)
std::string age;
std::string created = refTask.getAttribute ("entry");
if (created.length () && created.find ("/") == std::string::npos)
if (created.length ())
{
Date dt (::atoi (created.c_str ()));
formatTimeDeltaDays (age, (time_t) (now - dt));
@@ -517,8 +532,8 @@ void handleList (const TDB& tdb, T& task, Config& conf)
table.addCell (row, 2, refTask.getAttribute ("priority"));
table.addCell (row, 3, due);
table.addCell (row, 4, active);
table.addCell (row, 5, age);
table.addCell (row, 6, refTask.getDescription ());
if (showAge) table.addCell (row, 5, age);
table.addCell (row, (showAge ? 6 : 5), refTask.getDescription ());
if (conf.get ("color", true))
{
@@ -581,6 +596,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");
@@ -604,7 +620,7 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
split (descWords, lowerCase (task.getDescription ()), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
@@ -620,9 +636,10 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
T refTask (tasks[i]);
// Apply description filter.
std::string desc = lowerCase (refTask.getDescription ());
unsigned int matches = 0;
for (unsigned int w = 0; w < descWords.size (); ++w)
if (refTask.getDescription ().find (descWords[w]) != std::string::npos)
if (desc.find (descWords[w]) != std::string::npos)
++matches;
if (matches == descWords.size ())
@@ -630,8 +647,16 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
// Apply attribute filter.
matches = 0;
foreach (a, attrList)
if (a->second == refTask.getAttribute (a->first))
{
if (a->first == "project")
{
if (a->second.length () <= refTask.getAttribute (a->first).length ())
if (a->second == refTask.getAttribute (a->first).substr (0, a->second.length ()))
++matches;
}
else if (a->second == refTask.getAttribute (a->first))
++matches;
}
if (matches == attrList.size ())
{
@@ -648,7 +673,7 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
bool overdue = false;
Date now;
std::string due = refTask.getAttribute ("due");
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -664,7 +689,7 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
std::string age;
std::string created = refTask.getAttribute ("entry");
if (created.length () && created.find ("/") == std::string::npos)
if (created.length ())
{
Date dt (::atoi (created.c_str ()));
formatTimeDeltaDays (age, (time_t) (now - dt));
@@ -737,6 +762,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");
@@ -757,7 +783,7 @@ void handleCompleted (const TDB& tdb, T& task, Config& conf)
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
split (descWords, lowerCase (task.getDescription ()), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
@@ -773,9 +799,10 @@ void handleCompleted (const TDB& tdb, T& task, Config& conf)
T refTask (tasks[i]);
// Apply description filter.
std::string desc = lowerCase (refTask.getDescription ());
unsigned int matches = 0;
for (unsigned int w = 0; w < descWords.size (); ++w)
if (refTask.getDescription ().find (descWords[w]) != std::string::npos)
if (desc.find (descWords[w]) != std::string::npos)
++matches;
if (matches == descWords.size ())
@@ -783,8 +810,16 @@ void handleCompleted (const TDB& tdb, T& task, Config& conf)
// Apply attribute filter.
matches = 0;
foreach (a, attrList)
if (a->second == refTask.getAttribute (a->first))
{
if (a->first == "project")
{
if (a->second.length () <= refTask.getAttribute (a->first).length ())
if (a->second == refTask.getAttribute (a->first).substr (0, a->second.length ()))
++matches;
}
else if (a->second == refTask.getAttribute (a->first))
++matches;
}
if (matches == attrList.size ())
{
@@ -852,6 +887,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");
@@ -916,10 +952,8 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
table.addCell (row, 1, due);
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
overdue = (dt < now) ? true : false;
now += 7 * 86400;
imminent = dt < now ? true : false;
@@ -976,7 +1010,7 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
std::string age;
std::string created = refTask.getAttribute ("entry");
if (created.length () && created.find ("/") == std::string::npos)
if (created.length ())
{
Date dt (::atoi (created.c_str ()));
formatTimeDeltaDays (age, (time_t) (now - dt));
@@ -1024,6 +1058,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");
@@ -1066,7 +1101,7 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
split (descWords, lowerCase (task.getDescription ()), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
@@ -1082,9 +1117,10 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
T refTask (tasks[i]);
// Apply description filter.
std::string desc = lowerCase (refTask.getDescription ());
unsigned int matches = 0;
for (unsigned int w = 0; w < descWords.size (); ++w)
if (refTask.getDescription ().find (descWords[w]) != std::string::npos)
if (desc.find (descWords[w]) != std::string::npos)
++matches;
if (matches == descWords.size ())
@@ -1092,8 +1128,16 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
// Apply attribute filter.
matches = 0;
foreach (a, attrList)
if (a->second == refTask.getAttribute (a->first))
{
if (a->first == "project")
{
if (a->second.length () <= refTask.getAttribute (a->first).length ())
if (a->second == refTask.getAttribute (a->first).substr (0, a->second.length ()))
++matches;
}
else if (a->second == refTask.getAttribute (a->first))
++matches;
}
if (matches == attrList.size ())
{
@@ -1108,14 +1152,14 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
Date now;
std::string started = refTask.getAttribute ("start");
if (started.length () && started.find ("/") == std::string::npos)
if (started.length ())
{
Date dt (::atoi (started.c_str ()));
started = dt.toString (conf.get ("dateformat", "m/d/Y"));
}
std::string entered = refTask.getAttribute ("entry");
if (entered.length () && entered.find ("/") == std::string::npos)
if (entered.length ())
{
Date dt (::atoi (entered.c_str ()));
entered = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -1125,7 +1169,7 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
bool imminent = false;
bool overdue = false;
std::string due = refTask.getAttribute ("due");
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -1137,7 +1181,7 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
std::string age;
std::string created = refTask.getAttribute ("entry");
if (created.length () && created.find ("/") == std::string::npos)
if (created.length ())
{
Date dt (::atoi (created.c_str ()));
formatTimeDeltaDays (age, (time_t) (now - dt));
@@ -1279,6 +1323,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)
@@ -1387,15 +1432,18 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
initializeColorRules (conf);
bool showAge = conf.get ("showage", true);
// 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");
table.addColumn ("Due");
table.addColumn ("Active");
table.addColumn ("Age");
if (showAge) table.addColumn ("Age");
table.addColumn ("Description");
table.setColumnUnderline (0);
@@ -1404,19 +1452,19 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
table.setColumnUnderline (3);
table.setColumnUnderline (4);
table.setColumnUnderline (5);
table.setColumnUnderline (6);
if (showAge) table.setColumnUnderline (6);
table.setColumnWidth (0, Table::minimum);
table.setColumnWidth (1, Table::minimum);
table.setColumnWidth (2, Table::minimum);
table.setColumnWidth (3, Table::minimum);
table.setColumnWidth (4, Table::minimum);
table.setColumnWidth (5, Table::minimum);
table.setColumnWidth (6, Table::flexible);
if (showAge) table.setColumnWidth (5, Table::minimum);
table.setColumnWidth ((showAge ? 6 : 5), Table::flexible);
table.setColumnJustification (0, Table::right);
table.setColumnJustification (3, Table::right);
table.setColumnJustification (5, Table::right);
if (showAge) table.setColumnJustification (5, Table::right);
table.sortOn (3, Table::ascendingDate);
table.sortOn (2, Table::descendingPriority);
@@ -1424,7 +1472,7 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
split (descWords, lowerCase (task.getDescription ()), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
@@ -1440,9 +1488,10 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
T refTask (pending[*i]);
// Apply description filter.
std::string desc = lowerCase (refTask.getDescription ());
unsigned int matches = 0;
for (unsigned int w = 0; w < descWords.size (); ++w)
if (refTask.getDescription ().find (descWords[w]) != std::string::npos)
if (desc.find (descWords[w]) != std::string::npos)
++matches;
if (matches == descWords.size ())
@@ -1450,8 +1499,16 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
// Apply attribute filter.
matches = 0;
foreach (a, attrList)
if (a->second == refTask.getAttribute (a->first))
{
if (a->first == "project")
{
if (a->second.length () <= refTask.getAttribute (a->first).length ())
if (a->second == refTask.getAttribute (a->first).substr (0, a->second.length ()))
++matches;
}
else if (a->second == refTask.getAttribute (a->first))
++matches;
}
if (matches == attrList.size ())
{
@@ -1468,7 +1525,7 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
bool overdue = false;
Date now;
std::string due = refTask.getAttribute ("due");
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -1484,7 +1541,7 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
std::string age;
std::string created = refTask.getAttribute ("entry");
if (created.length () && created.find ("/") == std::string::npos)
if (created.length ())
{
Date dt (::atoi (created.c_str ()));
formatTimeDeltaDays (age, (time_t) (now - dt));
@@ -1497,8 +1554,8 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
table.addCell (row, 2, refTask.getAttribute ("priority"));
table.addCell (row, 3, due);
table.addCell (row, 4, active);
table.addCell (row, 5, age);
table.addCell (row, 6, refTask.getDescription ());
if (showAge) table.addCell (row, 5, age);
table.addCell (row, (showAge ? 6 : 5), refTask.getDescription ());
if (conf.get ("color", true))
{
@@ -1638,6 +1695,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 +1798,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 +1817,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 +1847,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 +1994,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");
@@ -1959,10 +2020,6 @@ void handleReportActive (const TDB& tdb, T& task, Config& conf)
table.sortOn (2, Table::descendingPriority);
table.sortOn (1, Table::ascendingCharacter);
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
task.getTags (tagList);
@@ -1980,7 +2037,7 @@ void handleReportActive (const TDB& tdb, T& task, Config& conf)
bool imminent = false;
bool overdue = false;
std::string due = refTask.getAttribute ("due");
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -2052,6 +2109,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");
@@ -2077,10 +2135,6 @@ void handleReportOverdue (const TDB& tdb, T& task, Config& conf)
table.sortOn (2, Table::descendingPriority);
table.sortOn (1, Table::ascendingCharacter);
// Split any description specified into words.
std::vector <std::string> descWords;
split (descWords, task.getDescription (), ' ');
// Get all the tags to match against.
std::vector <std::string> tagList;
task.getTags (tagList);
@@ -2098,7 +2152,7 @@ void handleReportOverdue (const TDB& tdb, T& task, Config& conf)
std::string due;
if ((due = refTask.getAttribute ("due")) != "")
{
if (due.length () && due.find ("/") == std::string::npos)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
@@ -2241,6 +2295,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);
@@ -2264,7 +2319,7 @@ void handleVersion (Config& conf)
}
}
std::cout << "Copyright (C) 2006 - 2008 P. Beckingham."
std::cout << "Copyright (C) 2006 - 2008, P. Beckingham."
<< std::endl
<< PACKAGE
<< " "

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

28
src/tests/Makefile Normal file
View File

@@ -0,0 +1,28 @@
PROJECT = t.t tdb.t date.t
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
LFLAGS = -L/usr/local/lib
OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../util.o ../Config.o
all: $(PROJECT)
install: $(PROJECT)
@echo unimplemented
test: $(PROJECT)
@echo unimplemented
clean:
-rm *.o $(PROJECT)
.cpp.o:
g++ -c $(CFLAGS) $<
t.t: t.t.o $(OBJECTS) test.o
g++ t.t.o $(OBJECTS) test.o $(LFLAGS) -o t.t
tdb.t: tdb.t.o $(OBJECTS) test.o
g++ tdb.t.o $(OBJECTS) test.o $(LFLAGS) -o tdb.t
date.t: date.t.o $(OBJECTS) test.o
g++ date.t.o $(OBJECTS) test.o $(LFLAGS) -o date.t

View File

@@ -9,78 +9,104 @@
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (46);
plan (63);
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");
ok (Date::leapYear (2000), "2000 is a leap year");
ok (Date::leapYear (1900), "1900 is 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");
Date fromString4 ("12/31/2007");
is (fromString4.month (), 12, "ctor (std::string) -> m");
is (fromString4.day (), 31, "ctor (std::string) -> d");
is (fromString4.year (), 2007, "ctor (std::string) -> y");
Date fromString5 ("12/31/2007", "m/d/Y");
is (fromString5.month (), 12, "ctor (std::string) -> m");
is (fromString5.day (), 31, "ctor (std::string) -> d");
is (fromString5.year (), 2007, "ctor (std::string) -> y");
Date fromString6 ("20071231", "YMD");
is (fromString6.month (), 12, "ctor (std::string) -> m");
is (fromString6.day (), 31, "ctor (std::string) -> d");
is (fromString6.year (), 2007, "ctor (std::string) -> y");
return 0;
}

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Task 1.1.0</title>
<title>Task 1.2.0</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
@@ -167,10 +167,33 @@ a img { border: none; padding: 0; margin: 0; }
<div class="content">
<p>
Download the latest task source code
<a href="http://www.beckingham.net/task-1.1.0.tar.gz">task-1.1.0.tar.gz</a>
(6/7/2008).
<a href="http://www.beckingham.net/task-1.2.0.tar.gz">task-1.2.0.tar.gz</a>
(6/13/2008).
</p>
New in version 1.1.0:
<p>
<h4>New in version 1.2.0</h4>
source: <a href="http://www.beckingham.net/task-1.2.0.tar.gz">task-1.2.0.tar.gz</a>
</p>
<ul>
<li>Subprojects supported - please see documentation below, or TUTORIAL
file
<li>"dateformat" configuration variable now properly used to parse as
well as render dates
<li>"task list x" now performs a caseless comparison between "x" and
the task description
<li>"showage" configuration variable determines whether the "Age" column
should appear on the "task list" and "task next" reports
<li>Improvements to the TUTORIAL file and this page
</ul>
<p>
<h4>New in version 1.1.0</h4>
source: <a href="http://www.beckingham.net/task-1.1.0.tar.gz">task-1.1.0.tar.gz</a>
<br />
Debian package: <a href="http://www.beckingham.net/task_1.1.0-1_i386.deb">task_1.1.0-1_i386.deb</a>
(Thanks to Richard Querin)
</p>
<ul>
@@ -223,10 +246,10 @@ a img { border: none; padding: 0; margin: 0; }
</p>
<pre><code>% ls
task-1.1.0.tar.gz
% gunzip task-1.1.0.tar.gz
% tar xf task-1.1.0.tar
% cd task-1.1.0
task-1.2.0.tar.gz
% gunzip task-1.2.0.tar.gz
% tar xf task-1.2.0.tar
% cd task-1.2.0
% ./configure
...
% make
@@ -340,6 +363,52 @@ Permanently delete task? (y/n) y</code></pre>
Wedding project tasks:
</p>
<p>
Subprojects are supported. If you have a project "Wedding", you can
specify that a task is a subproject "Transport" of "Wedding" by
assigning the project "Wedding.Transport". Let's do this:
</p>
<pre><code>% task 2 project:Wedding.Transport
% task ls
ID Project Pri Description
3 Family Send John a birthday card
2 Wedding.Transport Reserve a rental car
1 Wedding Book plane ticket</code></pre>
<p>
Task matches the leftmost part of the project when searching, so
projects may be abbreviated:
</p>
<pre><code>% task ls project:Wedding.Tra
ID Project Pri Description
2 Wedding.Transport Reserve a rental car</code></pre>
<p>
This way of matching projects can be used to see all tasks under
the "Wedding" project and all subprojects:
</p>
<pre><code>% task ls project:Wedding
ID Project Pri Description
2 Wedding.Transport Reserve a rental car
1 Wedding Book plane ticket</code></pre>
<p>
Let's reassign 2 back to the "Wedding" project:
</p>
<pre><code>% task 2 project:Wedding</code></pre>
<p>
Now that projects are assigned, we can look at just the
Wedding project tasks:
</p>
<pre><code>% task ls project:Wedding
ID Project Pri Description
@@ -978,6 +1047,12 @@ on_white on_bright_white</code></pre>
</tr>
</table>
</p>
</dd>
<dt>showage<dt>
<dd>
May be "yes" or "no". Determines whether the "Age"
column appears on the "list" and "next" reports.
<dd>
<dt>color</dt>