- Added the ability to control date formats via the 'dateformat' configuration variable.

This commit is contained in:
Paul Beckingham 2008-06-07 17:09:09 -04:00
parent 714d9c5544
commit e8b7114ce8
6 changed files with 361 additions and 105 deletions

View file

@ -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.0.1.tar.gz
task-1.1.0.tar.gz
% gunzip task-1.1.0.tar.gz
% tar xf task-1.1.0.tar
% cd task-1.0.1
% cd task-1.1.0
% ./configure
...
% make
@ -625,6 +625,31 @@ Configuring Task
unnecessary blank lines so that task makes better use
screen real estate on small-screened devices.
dateformat This is a string of characters that define how task
formats dates. The default value is:
m/d/Y
which means dates look like:
6/7/2008
The string should contain the characters:
m minimal-digit month 1, 12
d minimal-digit day 1, 30
y two-digit year 08
M two-digit month 01, 12
D two-digit day 01, 30
Y four-digit year 2008
The string may also contain other characters to act as
spacers, or formatting. Other values could include:
d/m/Y 7/6/2008
YMD 20080607
m-d-y 6-7-08
color May be "on" or "off". Determines whether task uses
color.

View file

@ -116,20 +116,77 @@ void Date::toMDY (int& m, int& d, int& y)
}
////////////////////////////////////////////////////////////////////////////////
void Date::toString (std::string& output)
{
output = toString ();
}
////////////////////////////////////////////////////////////////////////////////
std::string Date::toString (void)
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)
{
switch (format[i])
{
case 'm':
{
char m[3];
sprintf (m, "%d", this->month ());
formatted += m;
}
break;
case 'M':
{
char m[3];
sprintf (m, "%02d", this->month ());
formatted += m;
}
break;
case 'd':
{
char d[3];
sprintf (d, "%d", this->day ());
formatted += d;
}
break;
case 'D':
{
char d[3];
sprintf (d, "%02d", this->day ());
formatted += d;
}
break;
case 'y':
{
char y[3];
sprintf (y, "%02d", this->year () % 100);
formatted += y;
}
break;
case 'Y':
{
char y[5];
sprintf (y, "%d", this->year ());
formatted += y;
}
break;
default:
formatted += format[i];
break;
}
}
return formatted;
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -44,8 +44,7 @@ public:
void toEpoch (time_t&);
time_t toEpoch ();
void toMDY (int&, int&, int&);
void toString (std::string&);
std::string toString (void);
std::string toString (const std::string& format = "m/d/Y");
static bool valid (const int, const int, const int);
static bool leapYear (int);

View file

@ -285,22 +285,6 @@ int main (int argc, char** argv)
return 0;
}
////////////////////////////////////////////////////////////////////////////////
std::string epochToString (const std::string& epoch)
{
char formatted[12] = {0};
if (epoch.length () && epoch.find ("/") == std::string::npos)
{
Date dt (::atoi (epoch.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
sprintf (formatted, "%d/%d/%04d", m, d, y);
}
return formatted;
}
////////////////////////////////////////////////////////////////////////////////
void handleAdd (const TDB& tdb, T& task, Config& conf)
{
@ -507,11 +491,7 @@ void handleList (const TDB& tdb, T& task, Config& conf)
if (due.length () && due.find ("/") == std::string::npos)
{
Date dt (::atoi (due.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
due = formatted;
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
overdue = (dt < now) ? true : false;
now += 7 * 86400;
@ -671,11 +651,7 @@ void handleSmallList (const TDB& tdb, T& task, Config& conf)
if (due.length () && due.find ("/") == std::string::npos)
{
Date dt (::atoi (due.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
due = formatted;
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
overdue = (dt < now) ? true : false;
now += 7 * 86400;
@ -826,7 +802,7 @@ void handleCompleted (const TDB& tdb, T& task, Config& conf)
// All criteria match, so add refTask to the output table.
int row = table.addRow ();
table.addCell (row, 0, end.toString ());
table.addCell (row, 0, end.toString (conf.get ("dateformat", "m/d/Y")));
table.addCell (row, 1, refTask.getAttribute ("project"));
table.addCell (row, 2, refTask.getDescription ());
@ -935,7 +911,10 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
{
row = table.addRow ();
table.addCell (row, 0, "Due");
table.addCell (row, 1, epochToString (due));
Date dt (::atoi (due.c_str ()));
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
table.addCell (row, 1, due);
if (due.length () && due.find ("/") == std::string::npos)
{
@ -960,7 +939,8 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
{
row = table.addRow ();
table.addCell (row, 0, "Start");
table.addCell (row, 1, epochToString (refTask.getAttribute ("start")));
Date dt (::atoi (refTask.getAttribute ("start").c_str ()));
table.addCell (row, 1, dt.toString (conf.get ("dateformat", "m/d/Y")));
}
// end
@ -968,7 +948,8 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
{
row = table.addRow ();
table.addCell (row, 0, "End");
table.addCell (row, 1, epochToString (refTask.getAttribute ("end")));
Date dt (::atoi (refTask.getAttribute ("end").c_str ()));
table.addCell (row, 1, dt.toString (conf.get ("dateformat", "m/d/Y")));
}
// tags ...
@ -990,7 +971,8 @@ void handleInfo (const TDB& tdb, T& task, Config& conf)
row = table.addRow ();
table.addCell (row, 0, "Entered");
std::string entry = epochToString (refTask.getAttribute ("entry"));
Date dt (::atoi (refTask.getAttribute ("entry").c_str ()));
std::string entry = dt.toString (conf.get ("dateformat", "m/d/Y"));
std::string age;
std::string created = refTask.getAttribute ("entry");
@ -1126,22 +1108,14 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
if (started.length () && started.find ("/") == std::string::npos)
{
Date dt (::atoi (started.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
started = formatted;
started = dt.toString (conf.get ("dateformat", "m/d/Y"));
}
std::string entered = refTask.getAttribute ("entry");
if (entered.length () && entered.find ("/") == std::string::npos)
{
Date dt (::atoi (entered.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
entered = formatted;
entered = dt.toString (conf.get ("dateformat", "m/d/Y"));
}
// Now format the matching task.
@ -1151,11 +1125,7 @@ void handleLongList (const TDB& tdb, T& task, Config& conf)
if (due.length () && due.find ("/") == std::string::npos)
{
Date dt (::atoi (due.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
due = formatted;
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
overdue = (dt < now) ? true : false;
now += 7 * 86400;
@ -1491,11 +1461,7 @@ void handleReportNext (const TDB& tdb, T& task, Config& conf)
if (due.length () && due.find ("/") == std::string::npos)
{
Date dt (::atoi (due.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
due = formatted;
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
overdue = (dt < now) ? true : false;
now += 7 * 86400;
@ -2007,11 +1973,7 @@ void handleReportActive (const TDB& tdb, T& task, Config& conf)
if (due.length () && due.find ("/") == std::string::npos)
{
Date dt (::atoi (due.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
due = formatted;
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
Date now;
overdue = dt < now ? true : false;
@ -2129,11 +2091,7 @@ void handleReportOverdue (const TDB& tdb, T& task, Config& conf)
if (due.length () && due.find ("/") == std::string::npos)
{
Date dt (::atoi (due.c_str ()));
int m, d, y;
dt.toMDY (m, d, y);
char formatted[12];
sprintf (formatted, "%d/%d/%04d", m, d, y);
due = formatted;
due = dt.toString (conf.get ("dateformat", "m/d/Y"));
// If overdue.
if (dt < now)
@ -2227,9 +2185,9 @@ void handleReportStats (const TDB& tdb, T& task, Config& conf)
if (tasks.size ())
{
Date e (earliest);
std::cout << "Oldest task " << e.toString () << std::endl;
std::cout << "Oldest task " << e.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
Date l (latest);
std::cout << "Newest task " << l.toString () << std::endl;
std::cout << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
std::cout << "Task used for " << formatSeconds (latest - earliest) << std::endl;
}

87
src/tests/date.t.cpp Normal file
View file

@ -0,0 +1,87 @@
////////////////////////////////////////////////////////////////////////////////
// Copyright 2005 - 2008, Paul Beckingham. All rights reserved.
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <Date.h>
#include <test.h>
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (46);
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");
t.ok (Date::valid (2, 29, 2008), "valid: 2/29/2008");
t.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");
t.is (Date::daysInMonth (2, 2008), 29, "29 days in February 2008");
t.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");
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");
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");
t.is (now - yesterday, 1, "today - yesterday == 1");
t.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");
Date epoch (9, 8, 2001);
t.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");
Date fromEpoch (epoch.toEpoch ());
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");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

192
task.html
View file

@ -1,22 +1,22 @@
<!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</title>
<title>Task 1.1.0</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
text-align: center;
margin: 0; padding: 1em;
margin: 0; padding: 1em;
}
#container {
width: 740px;
text-align: left;
text-align: left;
margin: 0 auto; padding: 0;
}
#header {
height: 60px;
height: 60px;
margin: 0 0 15px; padding: 0;
}
@ -27,22 +27,22 @@ body {
}
#header a:link,
#header a:visited {
#header a:visited {
color:#000;
text-decoration: none;
}
#header h1 {
font: bold 400% georgia, serif;
letter-spacing: -1px;
font: bold 400% georgia, serif;
letter-spacing: -1px;
margin: 0;
float: left;
float: left;
}
#header h2 {
font: normal 12px verdana, arial, sans-serif;
font: normal 12px verdana, arial, sans-serif;
margin: 2.5em 0 0 0.8em;
float: left;
float: left;
}
#content {}
@ -51,41 +51,41 @@ body {
#content h2,
#content h3,
#content h4,
#content h5 {
font-family: "lucidamac bold", "lucida grande", arial, sans-serif;
#content h5 {
font-family: "lucidamac bold", "lucida grande", arial, sans-serif;
letter-spacing: -1px;
}
#content h1 {
#content h1 {
font-size: 24px;
margin: 0 0 0.3em;
margin: 0 0 0.3em;
}
#content h2 {
#content h2 {
font-size: 22px;
margin: 0 0 0.3em;
margin: 0 0 0.3em;
}
#content h3 {
#content h3 {
font-size: 20px;
margin: 1.2em 0 0.3em;
margin: 1.2em 0 0.3em;
}
#content h4 {
#content h4 {
font-size: 18px;
margin: 1.2em 0 0.3em;
margin: 1.2em 0 0.3em;
border-bottom: 1px dotted #bbb;
}
#content h5 {
font-size: 18px;
background: #ffd;
margin: 1.2em 0 0.3em;
margin: 1.2em 0 0.3em;
border-bottom: 1px dotted #aaa;
}
#content p {
line-height: 15px;
line-height: 15px;
margin: 0 0 1.2em;
}
@ -95,7 +95,7 @@ body {
padding:0;
}
#content code {
#content code {
font: normal 12px "bitstream vera sans mono", monaco "lucida console", "courier new", courier, serif;
}
@ -118,6 +118,13 @@ body {
font: normal 12px "lucida grande", verdana, arial, helvetica, sans-serif;
}
th {
background-color: #e0e0e0;
}
td {
background-color: #f7f7f7;
}
input,
textarea { font: normal 12px "bitstream vera sans", verdana, sans-serif; }
@ -130,12 +137,13 @@ a img { border: none; padding: 0; margin: 0; }
<body>
<div id="toolbar">
<a href="#setup">Quick Setup</a>
<a href="#setup">Setup</a>
<a href="#simple">Simple</a>
<a href="#advanced">Advanced</a>
<a href="#shell">Shell</a>
<a href="#config">Configuration</a>
<a href="#color">Colors</a>
<a href="#usage">Usage</a>
</div>
<div id="container">
@ -149,6 +157,7 @@ a img { border: none; padding: 0; margin: 0; }
and use the task program.
</p>
<br />
<h2 class="title">
Get the Source Code
</h2>
@ -156,8 +165,8 @@ 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.0.1.tar.gz">task-1.0.1.tar.gz</a>
(6/4/2008).
<a href="http://www.beckingham.net/task-1.1.0.tar.gz">task-1.1.0.tar.gz</a>
(6/7/2008).
</p>
<p>
@ -175,6 +184,7 @@ a img { border: none; padding: 0; margin: 0; }
</p>
</div>
<br />
<h2 class="title">
Task Program Tutorial
</h2>
@ -186,6 +196,7 @@ a img { border: none; padding: 0; margin: 0; }
</p>
</div>
<br />
<h2 class="title">
<a name="setup">Quick Setup<a>
</h2>
@ -197,16 +208,22 @@ a img { border: none; padding: 0; margin: 0; }
<p>
<pre><code>% ls
task-1.0.1.tar.gz
% gunzip task-1.0.1.tar.gz
% tar xf task-1.0.1.tar
% cd task-1.0.1
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
% ./configure
...
% make
...
% make install # (may require sudo, depending on --prefix)</code></pre>
<p>
(For those of you using <a href="http://www.cygwin.com">Cygwin</a>,
you need to make sure you have the "g++" and "make" packages
available, which are found in the "devel" category.)
</p>
<p>
You need to make sure that the installed task program is in your
PATH environment variable.
@ -235,6 +252,7 @@ Done.
[then task will show version information]</code></pre>
</div>
<br />
<h2 class="title">
<a name="simple">Simple Usage</a>
</h2>
@ -461,6 +479,7 @@ ID Project Pri Due Active Age Description
<pre><code>% task 3 -john</code></pre>
</div>
<br />
<h2 class="title">
<a name="advanced">Advanced Usage</a>
</h2>
@ -724,6 +743,7 @@ ID Project Pri Description
</p>
</div>
<br />
<h2 class="title">
<a name="shell">Interacting with the Shell</a>
</h2>
@ -807,6 +827,7 @@ on_white on_bright_white</code></pre>
</p>
</div>
<br />
<h2 class="title">
<a name="config">Configuring Task</a>
</h2>
@ -865,6 +886,57 @@ on_white on_bright_white</code></pre>
the window you are using, for text wrapping.
</dd>
<dt>blanklines</dt>
<dd>
May be "on" or "off". Prevents the display of unnecessary blank
lines so that task makes better use screen real estate on small-
screened devices.
</dd>
<dt>dateformat</dt>
<dd>
<p>
This is a string of characters that define how task formats dates.
The default value is:
</p>
<pre><code>m/d/Y</code></pre>
<p>
which means dates look like:
</p>
<pre><code>6/7/2008</code></pre>
<p>
The string should contain the characters:
<table>
<tr> <th>Character</th> <th>Meaning</th> <th>Example</th> </tr>
<tr> <td>m</td> <td>minimal-digit month</td> <td>1, 12</td> </tr>
<tr> <td>d</td> <td>minimal-digit day</td> <td>1, 30</td> </tr>
<tr> <td>y</td> <td>two-digit year</td> <td>08</td> </tr>
<tr> <td>M</td> <td>two-digit month</td> <td>01, 12</td> </tr>
<tr> <td>D</td> <td>two-digit day</td> <td>01, 30</td> </tr>
<tr> <td>Y</td> <td>four-digit year</td> <td>2008</td> </tr>
</table>
</p>
<p>
The string may also contain other characters to act as spacers,
or formatting. Other values could include:
</p>
<p>
<table>
<tr> <th>dateformat</td> <th>How it looks</th> </tr>
<tr> <td>d/m/Y</td> <td>7/6/2008</td> </tr>
<tr> <td>YMD</td> <td>20080607</td> </tr>
<tr> <td>m-d-y</td> <td>6-7-08</td> </tr>
</table>
</p>
<dd>
<dt>color</dt>
<dd>
May be "on" or "off". Determines whether task uses color.
@ -907,6 +979,7 @@ on_white on_bright_white</code></pre>
</div>
<br />
<h2 class="title">
<a name="color">Colors</a>
</h2>
@ -943,13 +1016,70 @@ on_cyan on_bright_cyan
on_white on_bright_white</code></pre>
</div>
<br />
<h2 class="title">
<a name="usage">Command Usage<a>
</h2>
<div class="content">
<pre><code>task add [tags] [attrs] desc...
task list [tags] [attrs] desc...
task long [tags] [attrs] desc...
task ls [tags] [attrs] desc...
task completed [tags] [attrs] desc...
task ID [tags] [attrs] [desc...]
task ID /from/to/
task delete ID
task info ID
task start ID
task done ID
task projects
task tags
task summary
task history
task next
task calendar
task active
task overdue
task stats
task usage
task export
task color
task version
ID is the numeric identifier displayed by the 'task list' command
Tags are arbitrary words, any quantity:
+tag The + means add the tag
-tag The - means remove the tag
Attributes are:
project: Project name
priority: Priority
due: Due date
fg: Foreground color
bg: Background color
Any command or attribute name may be abbreviated if still unique:
task list project:Home
task li pro:Home
Some task descriptions need to be escaped because of the shell:
task add "quoted ' quote"
task add escaped \' quote
Many characters have special meaning to the shell, including:
$ ! ' " ( ) ; \ ` * ? { } [ ] < > | &amp; % # ~</code></pre>
<div>
<br />
<br />
<div class="content">
<p>
Copyright 2006-2008, Paul Beckingham. All rights reserved.
Copyright 2006-2008, P. Beckingham. All rights reserved.
</p>
</div>
</div>
</div>
</body>