Code Cleanup

- Factorize code for parsing date elements.
- Better order of blocks for parsing date elements.
- Add corresponding minimal-digit date parsing method for reading seconds,
  minutes and week.
- Update documentation and test.
This commit is contained in:
Louis-Claude Canon 2012-07-30 16:35:58 +02:00 committed by Paul Beckingham
parent 384be4b249
commit ec330921de
8 changed files with 116 additions and 218 deletions

View file

@ -7,6 +7,7 @@ Features
+ Stop consider new tasks after quitting a bulk change.
+ Removed deprecated 'fg:' and 'bg:' attributes.
+ The 'diagnostics' command now reports libuuid details.
+ New characters for parsing and formating dates ('n', 's' and 'v').
Bugs
+ Fixed bug #1043, where aliases were not recognized by bash autocompletion.

View file

@ -493,7 +493,7 @@ will be applied
to the date. Entered dates as well as all other displayed dates in reports
are formatted according to dateformat.
The default value is: m/d/Y. The string should contain the characters:
The default value is: m/d/Y. The string can contain the characters:
.RS
.RS
@ -501,13 +501,13 @@ m minimal-digit month, for example 1 or 12
.br
d minimal-digit day, for example 1 or 30
.br
y two-digit year, for example 09
y two-digit year, for example 09 or 12
.br
D two-digit day, for example 01 or 30
.br
M two-digit month, for example 01 or 12
.br
Y four-digit year, for example 2009
Y four-digit year, for example 2009 or 2012
.br
a short name of weekday, for example Mon or Wed
.br
@ -517,9 +517,17 @@ b short name of month, for example Jan or Aug
.br
B long name of month, for example January or August
.br
V weeknumber, for example 03 or 37
v minimal-digit week, for example 3 or 37
.br
H two-digit hour, for example 03 or 11
V two-digit week, for example 03 or 37
.br
h minimal-digit hour, for example 3 or 21
.br
n minimal-digit minutes, for example 5 or 42
.br
s minimal-digit seconds, for example 7 or 47
.br
H two-digit hour, for example 03 or 21
.br
N two-digit minutes, for example 05 or 42
.br
@ -527,6 +535,11 @@ S two-digit seconds, for example 07 or 47
.RE
.RE
.RS
The characters 'v', 'V', 'a' and 'A' can only be used for formatting printed
dates (not to parse them).
.RE
.RS
The string may also contain other characters to act as spacers, or formatting.
Examples for other values of dateformat:
@ -550,15 +563,15 @@ Examples for other values of dateformat.report:
.RS
.RS
.br
a D b Y (V) would do an output as "Fri 24 Jul 2009 (30)"
a D b Y (V) would do an output as "Fri 24 Jul 2009 (30)"
.br
A, B D, Y would do an output as "Friday, July 24, 2009"
A, B D, Y would do an output as "Friday, July 24, 2009"
.br
vV a Y-M-D would do an output as "v30 Fri 2009-07-24"
wV a Y-M-D would do an output as "w30 Fri 2009-07-24"
.br
yMD.HN would do an output as "110124.2342"
yMD.HN would do an output as "110124.2342"
.br
m/d/Y H:N would do an output as "1/24/2011 10:42"
m/d/Y H:N would do an output as "1/24/2011 10:42"
.br
a D b Y H:N:S would do an output as "Mon 24 Jan 2011 11:19:42"
.RE

View file

@ -238,10 +238,13 @@ const std::string Date::toString (
case 'A': sprintf (buffer, "%s", Date::dayName (dayOfWeek ()).c_str ()); break;
case 'b': sprintf (buffer, "%.3s", Date::monthName (month ()).c_str ()); break;
case 'B': sprintf (buffer, "%.9s", Date::monthName (month ()).c_str ()); break;
case 'v': sprintf (buffer, "%d", Date::weekOfYear (Date::dayOfWeek (context.config.get ("weekstart")))); break;
case 'V': sprintf (buffer, "%02d", Date::weekOfYear (Date::dayOfWeek (context.config.get ("weekstart")))); break;
case 'h': sprintf (buffer, "%d", this->hour ()); break;
case 'H': sprintf (buffer, "%02d", this->hour ()); break;
case 'n': sprintf (buffer, "%d", this->minute ()); break;
case 'N': sprintf (buffer, "%02d", this->minute ()); break;
case 's': sprintf (buffer, "%d", this->second ()); break;
case 'S': sprintf (buffer, "%02d", this->second ()); break;
case 'j': sprintf (buffer, "%d", this->dayOfYear ()); break;
case 'J': sprintf (buffer, "%03d", this->dayOfYear ()); break;

View file

@ -717,6 +717,47 @@ bool Nibbler::getDateISO (time_t& t)
////////////////////////////////////////////////////////////////////////////////
#ifdef NIBBLER_FEATURE_DATE
// Parse the longest integer using the next 'limit' characters of 'result'
// following position 'i' (when strict is true, the number of digits must be
// equal to limit).
bool Nibbler::parseDigits(std::string::size_type& i,
int& result,
unsigned int limit,
bool strict /* = true */)
{
// If the result has already been set
if (result != -1)
return false;
for (unsigned int f = limit; f > 0; --f)
{
// Check that the nibbler has enough unparsed characters
if (i + f <= _length)
{
// Check that 'f' of them are digits
unsigned int g;
for (g = 0; g < f; g++)
if (! isdigit (_input[i + g]))
break;
// Parse the integer when it is the case
if (g == f)
{
if (f == 1)
result = _input[i] - '0';
else
result = atoi (_input.substr (i, f).c_str ());
// Update the global cursor before returning
i += f;
return true;
}
}
// Do not try smaller limits if the option is strict on the size
if (strict)
break;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getDate (const std::string& format, time_t& t)
{
std::string::size_type i = _cursor;
@ -728,246 +769,85 @@ bool Nibbler::getDate (const std::string& format, time_t& t)
int minute = -1;
int second = -1;
// For parsing, unused.
int wday = -1;
int week = -1;
for (unsigned int f = 0; f < format.length (); ++f)
{
switch (format[f])
{
case 'm':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
month = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else if (i + 1 <= _length &&
isdigit (_input[i + 0]))
{
month = _input[i] - '0';
i += 1;
}
else
case 'M':
if (! parseDigits(i, month, 2, format[f] == 'M'))
return false;
break;
case 'd':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
day = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else if (i + 1 <= _length &&
isdigit (_input[i + 0]))
{
day = _input[i] - '0';
i += 1;
}
else
case 'D':
if (! parseDigits(i, day, 2, format[f] == 'D'))
return false;
break;
case 'y':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
year = 2000 + atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
case 'Y':
if (! parseDigits(i, year, format[f] == 'y' ? 2 : 4))
return false;
if (format[f] == 'y')
year += 2000;
break;
case 'h':
case 'H':
if (! parseDigits(i, hour, 2, format[f] == 'H'))
return false;
break;
case 'M':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
month = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
case 'n':
case 'N':
if (! parseDigits(i, minute, 2, format[f] == 'N'))
return false;
break;
case 'D':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
day = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
case 's':
case 'S':
if (! parseDigits(i, second, 2, format[f] == 'S'))
return false;
break;
// Merely parse, not extract.
case 'v':
case 'V':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
day = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
return false;
break;
case 'Y':
if (i + 4 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]) &&
isdigit (_input[i + 3]))
{
year = atoi (_input.substr (i, 4).c_str ());
i += 4;
}
else
if (! parseDigits(i, week, 2, format[f] == 'V'))
return false;
break;
// Merely parse, not extract.
case 'a':
if (i + 3 <= _length &&
! isdigit (_input[i + 0]) &&
! isdigit (_input[i + 1]) &&
! isdigit (_input[i + 2]))
i += 3;
else
return false;
break;
// Merely parse, not extract.
case 'b':
if (i + 3 <= _length &&
! isdigit (_input[i + 0]) &&
! isdigit (_input[i + 1]) &&
! isdigit (_input[i + 2]))
{
month = Date::monthOfYear (_input.substr (i, 3).c_str());
i += 3;
}
else
return false;
break;
// Merely parse, not extract.
case 'A':
if (i + 3 <= _length &&
! isdigit (_input[i + 0]) &&
! isdigit (_input[i + 1]) &&
! isdigit (_input[i + 2]))
i += Date::dayName (Date::dayOfWeek (_input.substr (i, 3).c_str ())).size ();
{
wday = Date::dayOfWeek (_input.substr (i, 3).c_str ());
i += (format[f] == 'a') ? 3 : Date::dayName (wday).size ();
}
else
return false;
break;
case 'b':
case 'B':
if (i + 3 <= _length &&
! isdigit (_input[i + 0]) &&
! isdigit (_input[i + 1]) &&
! isdigit (_input[i + 2]))
{
month = Date::monthOfYear (_input.substr (i, 3).c_str ());
i += Date::monthName (month).size ();
}
else
return false;
break;
case 'h':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
hour = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else if (i + 1 <= _length &&
isdigit (_input[i + 0]))
{
hour = atoi (_input.substr (i, 1).c_str ());
i += 1;
}
else
return false;
break;
case 'H':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
hour = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
return false;
break;
case 'N':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
minute = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
return false;
break;
case 'S':
if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
second = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else
return false;
break;
case 'j':
if (i + 3 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]))
{
day = atoi (_input.substr (i, 3).c_str ());
i += 3;
}
else if (i + 2 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]))
{
day = atoi (_input.substr (i, 2).c_str ());
i += 2;
}
else if (i + 1 <= _length &&
isdigit (_input[i + 0]))
{
day = atoi (_input.substr (i, 1).c_str ());
i += 1;
}
else
return false;
break;
case 'J':
if (i + 3 <= _length &&
isdigit (_input[i + 0]) &&
isdigit (_input[i + 1]) &&
isdigit (_input[i + 2]))
{
day = atoi (_input.substr (i, 3).c_str ());
i += 3;
if (month != -1)
return false;
month = Date::monthOfYear (_input.substr (i, 3).c_str());
i += (format[f] == 'b') ? 3 : Date::monthName (month).size ();
}
else
return false;

View file

@ -78,6 +78,7 @@ public:
bool getPartialUUID (std::string&);
bool getDateISO (time_t&);
#ifdef NIBBLER_FEATURE_DATE
bool parseDigits(std::string::size_type&, int&, unsigned int, bool strict = true);
bool getDate (const std::string&, time_t&);
#endif
bool getOneOf (const std::vector <std::string>&, std::string&);

View file

@ -207,17 +207,17 @@ int main (int argc, char** argv)
t.is (fromString7.day (), 1, "ctor (std::string) -> d");
t.is (fromString7.year (), 2008, "ctor (std::string) -> y");
Date fromString8 ("Tue 01 Jan 2008 (01)", "a D b Y (V)");
t.is (fromString8.month (), 1, "ctor (std::string) -> m");
t.is (fromString8.day (), 1, "ctor (std::string) -> d");
Date fromString8 ("Tue 05 Feb 2008 (06)", "a D b Y (V)");
t.is (fromString8.month (), 2, "ctor (std::string) -> m");
t.is (fromString8.day (), 5, "ctor (std::string) -> d");
t.is (fromString8.year (), 2008, "ctor (std::string) -> y");
Date fromString9 ("Tuesday, January 1, 2008", "A, B d, Y");
t.is (fromString9.month (), 1, "ctor (std::string) -> m");
t.is (fromString9.day (), 1, "ctor (std::string) -> d");
Date fromString9 ("Tuesday, February 5, 2008", "A, B d, Y");
t.is (fromString9.month (), 2, "ctor (std::string) -> m");
t.is (fromString9.day (), 5, "ctor (std::string) -> d");
t.is (fromString9.year (), 2008, "ctor (std::string) -> y");
Date fromString10 ("v01 Tue 2008-01-01", "vV a Y-M-D");
Date fromString10 ("w01 Tue 2008-01-01", "wV a Y-M-D");
t.is (fromString10.month (), 1, "ctor (std::string) -> m");
t.is (fromString10.day (), 1, "ctor (std::string) -> d");
t.is (fromString10.year (), 2008, "ctor (std::string) -> y");

View file

@ -57,8 +57,8 @@ if (open my $fh, '>', 'date3.rc')
"dateformat=m/d/y\n",
"dateformat=m/d/y\n",
"weekstart=Monday\n",
"dateformat.info=A D B Y (vV)\n",
"dateformat.report=A D B Y (vV)\n";
"dateformat.info=A D B Y (wV)\n",
"dateformat.report=A D B Y (wV)\n";
close $fh;
ok (-r 'date3.rc', 'Created date3.rc');
}
@ -79,7 +79,7 @@ ok (!-r 'pending.data', 'Removed pending.data');
qx{../src/task rc:date3.rc add foo due:4/8/10 2>&1};
$output = qx{../src/task rc:date3.rc list 2>&1};
like ($output, qr/Thursday 08 April 2010 \(v14\)/, 'date format A D B Y (vV) parsed');
like ($output, qr/Thursday 08 April 2010 \(w14\)/, 'date format A D B Y (wV) parsed');
$output = qx{../src/task rc:date3.rc rc.dateformat.report:"D b Y - a" list 2>&1};
like ($output, qr/08 Apr 2010 - Thu/, 'date format D b Y - a parsed');

View file

@ -545,8 +545,8 @@ int main (int argc, char** argv)
t.is (dt.day (), 1, "ctor (std::string) -> d");
t.is (dt.year (), 2008, "ctor (std::string) -> y");
n = Nibbler ("v01 Tue 2008-01-01");
t.ok (n.getDate ("vV a Y-M-D", ti), "vV a Y-M-D ok");
n = Nibbler ("w01 Tue 2008-01-01");
t.ok (n.getDate ("wV a Y-M-D", ti), "wV a Y-M-D ok");
dt = Date (ti);
t.is (dt.month (), 1, "ctor (std::string) -> m");
t.is (dt.day (), 1, "ctor (std::string) -> d");