mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
ISO8601: Disentangled ISO8601d and ISO8601p
This commit is contained in:
parent
abbebf69ad
commit
9adfddfe65
1 changed files with 213 additions and 210 deletions
423
src/ISO8601.cpp
423
src/ISO8601.cpp
|
@ -968,216 +968,6 @@ void ISO8601d::resolve ()
|
||||||
_date = utc ? timegm (&t) : mktime (&t);
|
_date = utc ? timegm (&t) : mktime (&t);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p::ISO8601p ()
|
|
||||||
{
|
|
||||||
clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p::ISO8601p (time_t input)
|
|
||||||
{
|
|
||||||
clear ();
|
|
||||||
_period = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p::ISO8601p (const std::string& input)
|
|
||||||
{
|
|
||||||
clear ();
|
|
||||||
|
|
||||||
if (Lexer::isAllDigits (input))
|
|
||||||
{
|
|
||||||
time_t value = (time_t) strtol (input.c_str (), NULL, 10);
|
|
||||||
if (value == 0 || value > 60)
|
|
||||||
{
|
|
||||||
_period = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::size_type idx = 0;
|
|
||||||
parse (input, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p::~ISO8601p ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p& ISO8601p::operator= (const ISO8601p& other)
|
|
||||||
{
|
|
||||||
if (this != &other)
|
|
||||||
{
|
|
||||||
_year = other._year;
|
|
||||||
_month = other._month;
|
|
||||||
_day = other._day;
|
|
||||||
_hours = other._hours;
|
|
||||||
_minutes = other._minutes;
|
|
||||||
_seconds = other._seconds;
|
|
||||||
_period = other._period;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool ISO8601p::operator< (const ISO8601p& other)
|
|
||||||
{
|
|
||||||
return _period < other._period;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool ISO8601p::operator> (const ISO8601p& other)
|
|
||||||
{
|
|
||||||
return _period > other._period;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p::operator std::string () const
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
s << _period;
|
|
||||||
return s.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
ISO8601p::operator time_t () const
|
|
||||||
{
|
|
||||||
return _period;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Supported:
|
|
||||||
//
|
|
||||||
// duration ::= designated # duration
|
|
||||||
//
|
|
||||||
// designated ::= 'P' [nn 'Y'] [nn 'M'] [nn 'D'] ['T' [nn 'H'] [nn 'M'] [nn 'S']]
|
|
||||||
//
|
|
||||||
// Not supported:
|
|
||||||
//
|
|
||||||
// duration ::= designated '/' datetime-ext # duration end
|
|
||||||
// | degignated '/' datetime # duration end
|
|
||||||
// | designated # duration
|
|
||||||
// | 'P' datetime-ext '/' datetime-ext # start end
|
|
||||||
// | 'P' datetime '/' datetime # start end
|
|
||||||
// | 'P' datetime-ext # start
|
|
||||||
// | 'P' datetime # start
|
|
||||||
// | datetime-ext '/' designated # start duration
|
|
||||||
// | datetime-ext '/' 'P' datetime-ext # start end
|
|
||||||
// | datetime-ext '/' datetime-ext # start end
|
|
||||||
// | datetime '/' designated # start duration
|
|
||||||
// | datetime '/' 'P' datetime # start end
|
|
||||||
// | datetime '/' datetime # start end
|
|
||||||
// ;
|
|
||||||
//
|
|
||||||
bool ISO8601p::parse (const std::string& input, std::string::size_type& start)
|
|
||||||
{
|
|
||||||
// Attempt and ISO parse first.
|
|
||||||
auto original_start = start;
|
|
||||||
Nibbler n (input.substr (original_start));
|
|
||||||
n.save ();
|
|
||||||
|
|
||||||
if (parse_designated (n))
|
|
||||||
{
|
|
||||||
// Check the values and determine time_t.
|
|
||||||
if (validate ())
|
|
||||||
{
|
|
||||||
// Record cursor position.
|
|
||||||
start = n.cursor ();
|
|
||||||
|
|
||||||
resolve ();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt a legacy format parse next.
|
|
||||||
n.restore ();
|
|
||||||
|
|
||||||
// Static and so preserved between calls.
|
|
||||||
static std::vector <std::string> units;
|
|
||||||
if (units.size () == 0)
|
|
||||||
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
|
|
||||||
units.push_back (durations[i].unit);
|
|
||||||
|
|
||||||
std::string number;
|
|
||||||
std::string unit;
|
|
||||||
|
|
||||||
if (n.getOneOf (units, unit))
|
|
||||||
{
|
|
||||||
if (n.depleted () ||
|
|
||||||
Lexer::isWhitespace (n.next ()) ||
|
|
||||||
Lexer::isSingleCharOperator (n.next ()))
|
|
||||||
{
|
|
||||||
start = original_start + n.cursor ();
|
|
||||||
|
|
||||||
// Linear lookup - should instead be logarithmic.
|
|
||||||
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
|
|
||||||
{
|
|
||||||
if (durations[i].unit == unit &&
|
|
||||||
durations[i].standalone == true)
|
|
||||||
{
|
|
||||||
_period = static_cast <int> (durations[i].seconds);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (n.getNumber (number) &&
|
|
||||||
number.find ('e') == std::string::npos &&
|
|
||||||
number.find ('E') == std::string::npos &&
|
|
||||||
(number.find ('+') == std::string::npos || number.find ('+') == 0) &&
|
|
||||||
(number.find ('-') == std::string::npos || number.find ('-') == 0))
|
|
||||||
{
|
|
||||||
n.skipWS ();
|
|
||||||
if (n.getOneOf (units, unit))
|
|
||||||
{
|
|
||||||
// The "d" unit is a special case, because it is the only one that can
|
|
||||||
// legitimately occur at the beginning of a UUID, and be followed by an
|
|
||||||
// operator:
|
|
||||||
//
|
|
||||||
// 1111111d-0000-0000-0000-000000000000
|
|
||||||
//
|
|
||||||
// Because Lexer::isDuration is higher precedence than Lexer::isUUID,
|
|
||||||
// the above UUID looks like:
|
|
||||||
//
|
|
||||||
// <1111111d> <-> ...
|
|
||||||
// duration op ...
|
|
||||||
//
|
|
||||||
// So as a special case, durations, with units of "d" are rejected if the
|
|
||||||
// quantity exceeds 10000.
|
|
||||||
//
|
|
||||||
if (unit == "d" &&
|
|
||||||
strtol (number.c_str (), NULL, 10) > 10000)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (n.depleted () ||
|
|
||||||
Lexer::isWhitespace (n.next ()) ||
|
|
||||||
Lexer::isSingleCharOperator (n.next ()))
|
|
||||||
{
|
|
||||||
start = original_start + n.cursor ();
|
|
||||||
double quantity = strtod (number.c_str (), NULL);
|
|
||||||
|
|
||||||
// Linear lookup - should instead be logarithmic.
|
|
||||||
double seconds = 1;
|
|
||||||
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
|
|
||||||
{
|
|
||||||
if (durations[i].unit == unit)
|
|
||||||
{
|
|
||||||
seconds = durations[i].seconds;
|
|
||||||
_period = static_cast <int> (quantity * static_cast <double> (seconds));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
time_t ISO8601d::toEpoch ()
|
time_t ISO8601d::toEpoch ()
|
||||||
{
|
{
|
||||||
|
@ -1790,6 +1580,219 @@ void ISO8601d::operator++ (int)
|
||||||
_date = tomorrow._date;
|
_date = tomorrow._date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p::ISO8601p ()
|
||||||
|
{
|
||||||
|
clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p::ISO8601p (time_t input)
|
||||||
|
{
|
||||||
|
clear ();
|
||||||
|
_period = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p::ISO8601p (const std::string& input)
|
||||||
|
{
|
||||||
|
clear ();
|
||||||
|
|
||||||
|
if (Lexer::isAllDigits (input))
|
||||||
|
{
|
||||||
|
time_t value = (time_t) strtol (input.c_str (), NULL, 10);
|
||||||
|
if (value == 0 || value > 60)
|
||||||
|
{
|
||||||
|
_period = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::size_type idx = 0;
|
||||||
|
parse (input, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p::~ISO8601p ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p& ISO8601p::operator= (const ISO8601p& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
_year = other._year;
|
||||||
|
_month = other._month;
|
||||||
|
_day = other._day;
|
||||||
|
_hours = other._hours;
|
||||||
|
_minutes = other._minutes;
|
||||||
|
_seconds = other._seconds;
|
||||||
|
_period = other._period;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool ISO8601p::operator< (const ISO8601p& other)
|
||||||
|
{
|
||||||
|
return _period < other._period;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool ISO8601p::operator> (const ISO8601p& other)
|
||||||
|
{
|
||||||
|
return _period > other._period;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p::operator std::string () const
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
s << _period;
|
||||||
|
return s.str ();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
ISO8601p::operator time_t () const
|
||||||
|
{
|
||||||
|
return _period;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Supported:
|
||||||
|
//
|
||||||
|
// duration ::= designated # duration
|
||||||
|
//
|
||||||
|
// designated ::= 'P' [nn 'Y'] [nn 'M'] [nn 'D'] ['T' [nn 'H'] [nn 'M'] [nn 'S']]
|
||||||
|
//
|
||||||
|
// Not supported:
|
||||||
|
//
|
||||||
|
// duration ::= designated '/' datetime-ext # duration end
|
||||||
|
// | degignated '/' datetime # duration end
|
||||||
|
// | designated # duration
|
||||||
|
// | 'P' datetime-ext '/' datetime-ext # start end
|
||||||
|
// | 'P' datetime '/' datetime # start end
|
||||||
|
// | 'P' datetime-ext # start
|
||||||
|
// | 'P' datetime # start
|
||||||
|
// | datetime-ext '/' designated # start duration
|
||||||
|
// | datetime-ext '/' 'P' datetime-ext # start end
|
||||||
|
// | datetime-ext '/' datetime-ext # start end
|
||||||
|
// | datetime '/' designated # start duration
|
||||||
|
// | datetime '/' 'P' datetime # start end
|
||||||
|
// | datetime '/' datetime # start end
|
||||||
|
// ;
|
||||||
|
//
|
||||||
|
bool ISO8601p::parse (const std::string& input, std::string::size_type& start)
|
||||||
|
{
|
||||||
|
// Attempt and ISO parse first.
|
||||||
|
auto original_start = start;
|
||||||
|
Nibbler n (input.substr (original_start));
|
||||||
|
n.save ();
|
||||||
|
|
||||||
|
if (parse_designated (n))
|
||||||
|
{
|
||||||
|
// Check the values and determine time_t.
|
||||||
|
if (validate ())
|
||||||
|
{
|
||||||
|
// Record cursor position.
|
||||||
|
start = n.cursor ();
|
||||||
|
|
||||||
|
resolve ();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt a legacy format parse next.
|
||||||
|
n.restore ();
|
||||||
|
|
||||||
|
// Static and so preserved between calls.
|
||||||
|
static std::vector <std::string> units;
|
||||||
|
if (units.size () == 0)
|
||||||
|
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
|
||||||
|
units.push_back (durations[i].unit);
|
||||||
|
|
||||||
|
std::string number;
|
||||||
|
std::string unit;
|
||||||
|
|
||||||
|
if (n.getOneOf (units, unit))
|
||||||
|
{
|
||||||
|
if (n.depleted () ||
|
||||||
|
Lexer::isWhitespace (n.next ()) ||
|
||||||
|
Lexer::isSingleCharOperator (n.next ()))
|
||||||
|
{
|
||||||
|
start = original_start + n.cursor ();
|
||||||
|
|
||||||
|
// Linear lookup - should instead be logarithmic.
|
||||||
|
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
|
||||||
|
{
|
||||||
|
if (durations[i].unit == unit &&
|
||||||
|
durations[i].standalone == true)
|
||||||
|
{
|
||||||
|
_period = static_cast <int> (durations[i].seconds);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (n.getNumber (number) &&
|
||||||
|
number.find ('e') == std::string::npos &&
|
||||||
|
number.find ('E') == std::string::npos &&
|
||||||
|
(number.find ('+') == std::string::npos || number.find ('+') == 0) &&
|
||||||
|
(number.find ('-') == std::string::npos || number.find ('-') == 0))
|
||||||
|
{
|
||||||
|
n.skipWS ();
|
||||||
|
if (n.getOneOf (units, unit))
|
||||||
|
{
|
||||||
|
// The "d" unit is a special case, because it is the only one that can
|
||||||
|
// legitimately occur at the beginning of a UUID, and be followed by an
|
||||||
|
// operator:
|
||||||
|
//
|
||||||
|
// 1111111d-0000-0000-0000-000000000000
|
||||||
|
//
|
||||||
|
// Because Lexer::isDuration is higher precedence than Lexer::isUUID,
|
||||||
|
// the above UUID looks like:
|
||||||
|
//
|
||||||
|
// <1111111d> <-> ...
|
||||||
|
// duration op ...
|
||||||
|
//
|
||||||
|
// So as a special case, durations, with units of "d" are rejected if the
|
||||||
|
// quantity exceeds 10000.
|
||||||
|
//
|
||||||
|
if (unit == "d" &&
|
||||||
|
strtol (number.c_str (), NULL, 10) > 10000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (n.depleted () ||
|
||||||
|
Lexer::isWhitespace (n.next ()) ||
|
||||||
|
Lexer::isSingleCharOperator (n.next ()))
|
||||||
|
{
|
||||||
|
start = original_start + n.cursor ();
|
||||||
|
double quantity = strtod (number.c_str (), NULL);
|
||||||
|
|
||||||
|
// Linear lookup - should instead be logarithmic.
|
||||||
|
double seconds = 1;
|
||||||
|
for (unsigned int i = 0; i < NUM_DURATIONS; i++)
|
||||||
|
{
|
||||||
|
if (durations[i].unit == unit)
|
||||||
|
{
|
||||||
|
seconds = durations[i].seconds;
|
||||||
|
_period = static_cast <int> (quantity * static_cast <double> (seconds));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void ISO8601p::clear ()
|
void ISO8601p::clear ()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue