Big (and final for now) update to the code, it's now complete and working better

and faster than the R5 code as far as I have checked it.
Refactored the source a bit, it's now much clearer: divided the heavily overloaded
type field into type/unit/flags.
Introduced some new methods to track everything we have to to provide a
useful date in hopefully all cases.
Now supports using a dash to mark negative values as well.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4376 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2003-08-26 02:21:19 +00:00
parent 6fb692ef97
commit 13e966c78e
+327 -178
View File
@@ -13,6 +13,14 @@
#include <ctype.h> #include <ctype.h>
#define TRACE_PARSEDATE 0
#if TRACE_PARSEDATE
# define TRACE(x) printf x ;
#else
# define TRACE(x) ;
#endif
/* The date format is as follows: /* The date format is as follows:
* *
* a/A weekday * a/A weekday
@@ -102,43 +110,44 @@ static const char * const *sFormatsTable = kFormatsTable;
enum field_type { enum field_type {
TYPE_DAY_UNIT = 0x0100,
TYPE_SECOND_UNIT = 0x0200,
TYPE_MONTH_UNIT = 0x0400,
TYPE_YEAR_UNIT = 0x0800,
TYPE_RELATIVE = 0x1000,
TYPE_NOT_MODIFIABLE = 0x2000,
TYPE_MODIFIER = 0x4000,
TYPE_UNIT = 0x8000,
TYPE_UNIT_MASK = 0x0f00,
TYPE_MASK = 0x00ff,
TYPE_UNKNOWN = 0, TYPE_UNKNOWN = 0,
TYPE_DAY = 1 | TYPE_DAY_UNIT, TYPE_DAY,
TYPE_MONTH = 2 | TYPE_MONTH_UNIT, TYPE_MONTH,
TYPE_YEAR = 3 | TYPE_MONTH_UNIT, TYPE_YEAR,
TYPE_WEEKDAY = 4 | TYPE_DAY_UNIT, TYPE_WEEKDAY,
TYPE_NOW = 5, TYPE_HOUR,
TYPE_HOUR = 6 | TYPE_SECOND_UNIT, TYPE_MINUTE,
TYPE_MINUTE = 7 | TYPE_SECOND_UNIT, TYPE_SECOND,
TYPE_SECOND = 8 | TYPE_SECOND_UNIT, TYPE_TIME_ZONE,
TYPE_TIME_ZONE = 9 | TYPE_SECOND_UNIT, TYPE_MERIDIAN,
TYPE_MERIDIAN = 10 | TYPE_SECOND_UNIT,
TYPE_DASH = 12, TYPE_DASH,
TYPE_DOT = 13, TYPE_DOT,
TYPE_COMMA = 14, TYPE_COMMA,
TYPE_COLON = 15, TYPE_COLON,
TYPE_PLUS_MINUS = 16 | TYPE_MODIFIER | TYPE_RELATIVE,
TYPE_NEXT_LAST_THIS = 17 | TYPE_MODIFIER | TYPE_RELATIVE,
TYPE_UNIT,
TYPE_MODIFIER,
TYPE_END, TYPE_END,
}; };
#define FLAG_NONE 0
#define FLAG_RELATIVE 1
#define FLAG_NOT_MODIFIABLE 2
#define FLAG_NOW 4
#define FLAG_NEXT_LAST_THIS 8
#define FLAG_PLUS_MINUS 16
#define FLAG_HAS_DASH 32
enum units {
UNIT_NONE,
UNIT_YEAR,
UNIT_MONTH,
UNIT_DAY,
UNIT_SECOND,
};
enum value_type { enum value_type {
VALUE_NUMERICAL, VALUE_NUMERICAL,
VALUE_STRING, VALUE_STRING,
@@ -146,65 +155,68 @@ enum value_type {
}; };
enum value_modifier { enum value_modifier {
MODIFY_PLUS, MODIFY_MINUS = -2,
MODIFY_MINUS, MODIFY_LAST = -1,
MODIFY_NEXT, MODIFY_NONE = 0,
MODIFY_THIS, MODIFY_THIS = MODIFY_NONE,
MODIFY_LAST, MODIFY_NEXT = 1,
MODIFY_PLUS = 2,
}; };
struct known_identifier { struct known_identifier {
const char *string; const char *string;
const char *alternate_string; const char *alternate_string;
int32 type; uint8 type;
uint8 flags;
uint8 unit;
int32 value; int32 value;
}; };
static const known_identifier kIdentifiers[] = { static const known_identifier kIdentifiers[] = {
{"today", NULL, TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_DAY_UNIT, 0}, {"today", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE, UNIT_DAY, 0},
{"tomorrow", NULL, TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_DAY_UNIT, 1}, {"tomorrow", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE, UNIT_DAY, 1},
{"yesterday", NULL, TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_DAY_UNIT, -1}, {"yesterday", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE, UNIT_DAY, -1},
{"now", NULL, TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_NOW, 0}, {"now", NULL, TYPE_UNIT, FLAG_RELATIVE | FLAG_NOT_MODIFIABLE | FLAG_NOW, 0},
{"this", NULL, TYPE_NEXT_LAST_THIS, 0}, {"this", NULL, TYPE_MODIFIER, FLAG_NEXT_LAST_THIS, UNIT_NONE, MODIFY_THIS},
{"next", NULL, TYPE_NEXT_LAST_THIS, 1}, {"next", NULL, TYPE_MODIFIER, FLAG_NEXT_LAST_THIS, UNIT_NONE, MODIFY_NEXT},
{"last", NULL, TYPE_NEXT_LAST_THIS, -1}, {"last", NULL, TYPE_MODIFIER, FLAG_NEXT_LAST_THIS, UNIT_NONE, MODIFY_LAST},
{"years", "year", TYPE_UNIT | TYPE_RELATIVE | TYPE_YEAR_UNIT, 1}, {"years", "year", TYPE_UNIT, FLAG_RELATIVE, UNIT_YEAR, 1},
{"months", "month",TYPE_UNIT | TYPE_RELATIVE | TYPE_MONTH_UNIT, 1}, {"months", "month",TYPE_UNIT, FLAG_RELATIVE, UNIT_MONTH, 1},
{"days", "day", TYPE_UNIT | TYPE_RELATIVE | TYPE_DAY_UNIT, 1}, {"days", "day", TYPE_UNIT, FLAG_RELATIVE, UNIT_DAY, 1},
{"hour", NULL, TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1 * 60 * 60}, {"hour", NULL, TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1 * 60 * 60},
{"hours", "hrs", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1 * 60 * 60}, {"hours", "hrs", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1 * 60 * 60},
{"second", "sec", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1}, {"second", "sec", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1},
{"seconds", "secs", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1}, {"seconds", "secs", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 1},
{"minute", "min", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 60}, {"minute", "min", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 60},
{"minutes", "mins", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 60}, {"minutes", "mins", TYPE_UNIT, FLAG_RELATIVE, UNIT_SECOND, 60},
{"am", NULL, TYPE_MERIDIAN | TYPE_NOT_MODIFIABLE, 0}, {"am", NULL, TYPE_MERIDIAN, FLAG_NOT_MODIFIABLE, UNIT_SECOND, 0},
{"pm", NULL, TYPE_MERIDIAN | TYPE_NOT_MODIFIABLE, 12 * 60 * 60}, // 12 hours {"pm", NULL, TYPE_MERIDIAN, FLAG_NOT_MODIFIABLE, UNIT_SECOND, 12 * 60 * 60},
{"sunday", "sun", TYPE_WEEKDAY, 0}, {"sunday", "sun", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 0},
{"monday", "mon", TYPE_WEEKDAY, 1}, {"monday", "mon", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 1},
{"tuesday", "tue", TYPE_WEEKDAY, 2}, {"tuesday", "tue", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 2},
{"wednesday", "wed", TYPE_WEEKDAY, 3}, {"wednesday", "wed", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 3},
{"thursday", "thu", TYPE_WEEKDAY, 4}, {"thursday", "thu", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 4},
{"friday", "fri", TYPE_WEEKDAY, 5}, {"friday", "fri", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 5},
{"saturday", "sat", TYPE_WEEKDAY, 6}, {"saturday", "sat", TYPE_WEEKDAY, FLAG_NONE, UNIT_DAY, 6},
{"january", "jan", TYPE_MONTH, 1}, {"january", "jan", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 1},
{"february", "feb", TYPE_MONTH, 2}, {"february", "feb", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 2},
{"march", "mar", TYPE_MONTH, 3}, {"march", "mar", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 3},
{"april", "apr", TYPE_MONTH, 4}, {"april", "apr", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 4},
{"may", "may", TYPE_MONTH, 5}, {"may", "may", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 5},
{"june", "jun", TYPE_MONTH, 6}, {"june", "jun", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 6},
{"july", "jul", TYPE_MONTH, 7}, {"july", "jul", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 7},
{"august", "aug", TYPE_MONTH, 8}, {"august", "aug", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 8},
{"september", "sep", TYPE_MONTH, 9}, {"september", "sep", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 9},
{"october", "oct", TYPE_MONTH, 10}, {"october", "oct", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 10},
{"november", "nov", TYPE_MONTH, 11}, {"november", "nov", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 11},
{"december", "dec", TYPE_MONTH, 12}, {"december", "dec", TYPE_MONTH, FLAG_NONE, UNIT_MONTH, 12},
{"GMT", NULL, TYPE_TIME_ZONE, 0}, {"GMT", NULL, TYPE_TIME_ZONE, FLAG_NONE, UNIT_SECOND, 0},
// ToDo: add more time zones // ToDo: add more time zones
{NULL} {NULL}
@@ -212,66 +224,152 @@ static const known_identifier kIdentifiers[] = {
#define MAX_ELEMENTS 32 #define MAX_ELEMENTS 32
struct parsed_element { class DateMask {
int32 type; public:
int32 value_type; DateMask() : fMask(0UL) {}
int32 modifier;
bigtime_t value; void Set(uint8 type) { fMask |= Flag(type); }
bool IsSet(uint8 type) { return fMask & Flag(type); }
bool HasTime();
bool IsComplete();
private:
inline uint32 Flag(uint8 type) { return 1UL << type; }
uint32 fMask;
}; };
bool struct parsed_element {
isType(int32 typeA, int32 typeB) uint8 base_type;
uint8 type;
uint8 flags;
uint8 unit;
uint8 value_type;
int8 modifier;
bigtime_t value;
void SetCharType(uint8 fieldType, int8 modify = MODIFY_NONE);
void Adopt(const known_identifier &identifier);
void AdoptUnit(const known_identifier &identifier);
bool IsNextLastThis();
};
void
parsed_element::SetCharType(uint8 fieldType, int8 modify)
{ {
return (typeA & TYPE_MASK) == (typeB & TYPE_MASK); base_type = type = fieldType;
value_type = VALUE_CHAR;
modifier = modify;
} }
void
parsed_element::Adopt(const known_identifier &identifier)
{
base_type = type = identifier.type;
flags = identifier.flags;
unit = identifier.unit;
if (identifier.type == TYPE_MODIFIER)
modifier = identifier.value;
value_type = VALUE_STRING;
value = identifier.value;
}
void
parsed_element::AdoptUnit(const known_identifier &identifier)
{
base_type = type = TYPE_UNIT;
flags = identifier.flags;
unit = identifier.unit;
value *= identifier.value;
}
inline bool
parsed_element::IsNextLastThis()
{
return base_type == TYPE_MODIFIER
&& (modifier == MODIFY_NEXT || modifier == MODIFY_LAST || modifier == MODIFY_THIS);
}
// #pragma mark -
bool
DateMask::HasTime()
{
// this will cause
return IsSet(TYPE_HOUR);
}
/** This method checks if the date mask is complete in the
* sense that it doesn't need to have a prefilled "struct tm"
* when its time value is computed.
*/
bool
DateMask::IsComplete()
{
// mask must be absolute, at last
if (fMask & Flag(TYPE_UNIT))
return false;
// minimal set of flags to have a complete set
return !(~fMask & (Flag(TYPE_DAY) | Flag(TYPE_MONTH)));
}
// #pragma mark -
status_t status_t
preparseDate(const char *dateString, parsed_element *elements) preparseDate(const char *dateString, parsed_element *elements)
{ {
int32 index = 0; int32 index = 0, modify = MODIFY_NONE;
char c; char c;
if (dateString == NULL) if (dateString == NULL)
return B_ERROR; return B_ERROR;
memset(&elements[0], 0, sizeof(parsed_element));
for (; (c = dateString[0]) != '\0'; dateString++) { for (; (c = dateString[0]) != '\0'; dateString++) {
// we don't care about spaces // we don't care about spaces
if (isspace(c)) if (isspace(c)) {
modify = MODIFY_NONE;
continue; continue;
}
// if we're reached our maximum number of elements, bail out // if we're reached our maximum number of elements, bail out
if (index >= MAX_ELEMENTS) if (index >= MAX_ELEMENTS)
return B_ERROR; return B_ERROR;
memset(&elements[index], 0, sizeof(parsed_element));
if (c == ',') { if (c == ',') {
elements[index].type = TYPE_COMMA; elements[index].SetCharType(TYPE_COMMA);
elements[index].value_type = VALUE_CHAR;
} else if (c == '.') { } else if (c == '.') {
elements[index].type = TYPE_DOT; elements[index].SetCharType(TYPE_DOT);
elements[index].value_type = VALUE_CHAR; } else if (c == '/') {
} else if (c == '/' || c == '-') { // "-" is handled differently (as a modifier)
// ToDo: "-" can also be the minus modifier... - how to distinguish? elements[index].SetCharType(TYPE_DASH);
elements[index].type = TYPE_DASH;
elements[index].value_type = VALUE_CHAR;
} else if (c == ':') { } else if (c == ':') {
elements[index].type = TYPE_COLON; elements[index].SetCharType(TYPE_COLON);
elements[index].value_type = VALUE_CHAR;
} else if (c == '+') { } else if (c == '+') {
elements[index].type = TYPE_RELATIVE; modify = MODIFY_PLUS;
elements[index].value_type = VALUE_CHAR;
elements[index].modifier = MODIFY_PLUS;
// this counts for the next element // this counts for the next element
continue; continue;
} else if (c == '-') { } else if (c == '-') {
// ToDo: this never happens, due to TYPE_DASH modify = MODIFY_MINUS;
elements[index].type = TYPE_MODIFIER; elements[index].flags = FLAG_HAS_DASH;
elements[index].value_type = VALUE_CHAR;
elements[index].modifier = MODIFY_MINUS;
// this counts for the next element // this counts for the next element
continue; continue;
@@ -281,6 +379,7 @@ preparseDate(const char *dateString, parsed_element *elements)
elements[index].type = TYPE_UNKNOWN; elements[index].type = TYPE_UNKNOWN;
elements[index].value_type = VALUE_NUMERICAL; elements[index].value_type = VALUE_NUMERICAL;
elements[index].value = atoll(dateString); elements[index].value = atoll(dateString);
elements[index].modifier = modify;
// skip number // skip number
while (isdigit(dateString[1])) while (isdigit(dateString[1]))
@@ -312,17 +411,17 @@ preparseDate(const char *dateString, parsed_element *elements)
return B_ERROR; return B_ERROR;
} }
if (index > 0 && (identifier->type & TYPE_UNIT) != 0) { if (index > 0 && identifier->type == TYPE_UNIT) {
// this is just a unit, so it will give the last value a meaning // this is just a unit, so it will give the last value a meaning
if (elements[--index].value_type != VALUE_NUMERICAL if (elements[--index].value_type != VALUE_NUMERICAL
&& elements[index].type != TYPE_NEXT_LAST_THIS) && !elements[index].IsNextLastThis())
return B_ERROR; return B_ERROR;
elements[index].value *= identifier->value; elements[index].AdoptUnit(*identifier);
} else if (index > 0 && elements[index - 1].type == TYPE_NEXT_LAST_THIS) { } else if (index > 0 && elements[index - 1].IsNextLastThis()) {
if (isType(identifier->type, TYPE_MONTH) if (identifier->type == TYPE_MONTH
|| isType(identifier->type, TYPE_WEEKDAY)) { || identifier->type == TYPE_WEEKDAY) {
index--; index--;
switch (elements[index].value) { switch (elements[index].value) {
@@ -336,30 +435,31 @@ preparseDate(const char *dateString, parsed_element *elements)
elements[index].modifier = MODIFY_NEXT; elements[index].modifier = MODIFY_NEXT;
break; break;
} }
elements[index].value = identifier->value; elements[index].Adopt(*identifier);
elements[index].type = TYPE_UNIT;
} else } else
return B_ERROR; return B_ERROR;
} else { } else {
elements[index].value_type = VALUE_STRING; elements[index].Adopt(*identifier);
elements[index].value = identifier->value;
} }
elements[index].type = identifier->type;
} }
// see if we can join any preceding modifiers // see if we can join any preceding modifiers
if (index > 0 if (index > 0
&& (elements[index - 1].type & TYPE_MODIFIER) != 0 && elements[index - 1].type == TYPE_MODIFIER
&& (elements[index].type & TYPE_NOT_MODIFIABLE) == 0) { && (elements[index].flags & FLAG_NOT_MODIFIABLE) == 0) {
// copy the current one to the last and go on // copy the current one to the last and go on
elements[index].modifier = elements[index - 1].modifier; elements[index].modifier = elements[index - 1].modifier;
elements[index].value *= elements[index - 1].value; elements[index].value *= elements[index - 1].value;
elements[index].flags |= elements[index - 1].flags;
elements[index - 1] = elements[index]; elements[index - 1] = elements[index];
} else { } else {
// we filled out one parsed_element // we filled out one parsed_element
index++; index++;
} }
memset(&elements[index], 0, sizeof(parsed_element));
} }
// were there any elements? // were there any elements?
@@ -373,68 +473,86 @@ preparseDate(const char *dateString, parsed_element *elements)
static void static void
computeRelativeUnit(parsed_element &element, struct tm &tm, time_t &now, bool &makeTime, bool isString) computeRelativeUnit(parsed_element &element, struct tm &tm, int *_flags)
{ {
uint32 type = element.type;
// ToDo: should change flags as well!
makeTime = true;
localtime_r(&now, &tm);
// set the relative start depending on unit // set the relative start depending on unit
if (type & (TYPE_DAY_UNIT | TYPE_MONTH_UNIT | TYPE_YEAR_UNIT)) { switch (element.unit) {
case UNIT_YEAR:
tm.tm_mon = 0; // supposed to fall through
case UNIT_MONTH:
tm.tm_mday = 1; // supposed to fall through
case UNIT_DAY:
tm.tm_hour = 0; tm.tm_hour = 0;
tm.tm_min = 0; tm.tm_min = 0;
tm.tm_sec = 0; tm.tm_sec = 0;
break;
} }
if (type & (TYPE_MONTH_UNIT | TYPE_YEAR_UNIT))
tm.tm_mday = 1;
if (type & TYPE_YEAR_UNIT)
tm.tm_mon = 0;
// adjust value // adjust value
if (type & TYPE_RELATIVE) { if (element.flags & FLAG_RELATIVE) {
if (type & TYPE_MONTH_UNIT) if (element.unit == UNIT_MONTH)
tm.tm_mon += element.value; tm.tm_mon += element.value;
else if (type & TYPE_DAY_UNIT) else if (element.unit == UNIT_DAY)
tm.tm_mday += element.value; tm.tm_mday += element.value;
else if (type & TYPE_SECOND_UNIT) else if (element.unit == UNIT_SECOND) {
if (element.modifier == MODIFY_MINUS)
tm.tm_sec -= element.value;
else
tm.tm_sec += element.value; tm.tm_sec += element.value;
else if (!isType(type, TYPE_NOW))
; *_flags |= PARSEDATE_MINUTE_RELATIVE_TIME;
//puts("what's going on here?"); } else if (element.unit == UNIT_YEAR)
} else if (isType(type, TYPE_WEEKDAY)) { tm.tm_year += element.value;
tm.tm_mday += abs(element.value) - tm.tm_wday; } else if (element.base_type == TYPE_WEEKDAY) {
tm.tm_mday += element.value - tm.tm_wday;
if (element.modifier == MODIFY_NEXT) if (element.modifier == MODIFY_NEXT)
tm.tm_mday += 7; tm.tm_mday += 7;
else if (element.modifier == MODIFY_LAST) else if (element.modifier == MODIFY_LAST)
tm.tm_mday -= 7; tm.tm_mday -= 7;
} else if (element.base_type == TYPE_MONTH) {
tm.tm_mon = element.value - 1;
if (element.modifier == MODIFY_NEXT)
tm.tm_year++;
else if (element.modifier == MODIFY_LAST)
tm.tm_year--;
} }
} }
/** Uses the format assignment (through "format", and "optional") for the parsed elements
* and calculates the time value with respect to "now".
* Will also set the day/minute relative flags in "_flags".
*/
static time_t static time_t
computeDate(const char *format, bool *optional, parsed_element *elements, time_t now, int *_flags) computeDate(const char *format, bool *optional, parsed_element *elements, time_t now, DateMask dateMask, int *_flags)
{ {
//printf("matches: %s\n", format); TRACE(("matches: %s\n", format));
parsed_element *element = elements; parsed_element *element = elements;
uint32 position = 0; uint32 position = 0;
struct tm tm; struct tm tm;
time_t date; bool relative = false;
bool relative = false, makeTime = false;
// ToDo: only call time() if necessary
if (now == -1) if (now == -1)
now = time(NULL); now = time(NULL);
if (dateMask.IsComplete())
memset(&tm, 0, sizeof(tm)); memset(&tm, 0, sizeof(tm));
// localtime_r(&now, &tm); else {
// printf("now = %ld, -> %ld\n", now, mktime(&tm)); localtime_r(&now, &tm);
if (dateMask.HasTime()) {
tm.tm_min = 0;
tm.tm_sec = 0;
}
*_flags = PARSEDATE_RELATIVE_TIME;
}
while (element->type != TYPE_END) { while (element->type != TYPE_END) {
// skip whitespace // skip whitespace
@@ -461,36 +579,33 @@ computeDate(const char *format, bool *optional, parsed_element *elements, time_t
switch (format[0]) { switch (format[0]) {
case 'd': case 'd':
tm.tm_mday = element->value; tm.tm_mday = element->value;
makeTime = true;
break; break;
case 'm': case 'm':
tm.tm_mon = element->value - 1; tm.tm_mon = element->value - 1;
makeTime = true;
break; break;
case 'H': case 'H':
case 'I': case 'I':
tm.tm_hour = element->value; tm.tm_hour = element->value;
makeTime = true;
break; break;
case 'M': case 'M':
tm.tm_min = element->value; tm.tm_min = element->value;
makeTime = true;
break; break;
case 'S': case 'S':
tm.tm_sec = element->value; tm.tm_sec = element->value;
makeTime = true;
break; break;
case 'y': case 'y':
case 'Y': case 'Y':
tm.tm_year = element->value; tm.tm_year = element->value;
if (tm.tm_year > 1900) if (tm.tm_year > 1900)
tm.tm_year -= 1900; tm.tm_year -= 1900;
makeTime = true;
break; break;
case 'T': case 'T':
// ToDo:! computeRelativeUnit(*element, tm, _flags);
computeRelativeUnit(*element, tm, now, makeTime, false);
break; break;
case '-':
// there is no TYPE_DASH element for this (just a flag)
format++;
continue;
} }
break; break;
@@ -498,17 +613,28 @@ computeDate(const char *format, bool *optional, parsed_element *elements, time_t
switch (format[0]) { switch (format[0]) {
case 'a': // weekday case 'a': // weekday
case 'A': case 'A':
// we'll apply this element later, if still necessary
if (!dateMask.IsComplete())
computeRelativeUnit(*element, tm, _flags);
break; break;
case 'b': case 'b': // month
case 'B': case 'B':
tm.tm_mon = element->value - 1;
break; break;
case 'p': case 'p': // meridian
tm.tm_sec += element->value;
break; break;
case 'z': // time zone case 'z': // time zone
case 'Z': case 'Z':
tm.tm_sec += element->value - timezone;
break; break;
case 'T': case 'T': // time unit
computeRelativeUnit(*element, tm, now, makeTime, true); if (element->flags & FLAG_NOW) {
*_flags = PARSEDATE_MINUTE_RELATIVE_TIME | PARSEDATE_RELATIVE_TIME;
break;
}
computeRelativeUnit(*element, tm, _flags);
break; break;
} }
break; break;
@@ -516,14 +642,14 @@ computeDate(const char *format, bool *optional, parsed_element *elements, time_t
// format matched at this point, check next element // format matched at this point, check next element
format++; format++;
if (format[0] == ']')
format++;
position++; position++;
element++; element++;
} }
if (makeTime)
return mktime(&tm); return mktime(&tm);
return now;
} }
@@ -539,20 +665,16 @@ parsedate_etc(const char *dateString, time_t now, int *_flags)
return B_ERROR; return B_ERROR;
} }
#if TRACE_PARSEDATE
for (int32 index = 0; elements[index].type != TYPE_END; index++) { for (int32 index = 0; elements[index].type != TYPE_END; index++) {
parsed_element e = elements[index]; parsed_element e = elements[index];
char type[32];
sprintf(type, "0x%lx", e.type);
if (e.type & TYPE_UNIT)
strcat(type, " unit");
if (e.type & TYPE_RELATIVE)
strcat(type, " relative");
printf(" %ld: type = %ld (%s), value = %Ld (%s)\n", printf(" %ld: type = %ld, base_type = %ld, unit = %ld, flags = %ld, value = %Ld (%s)\n",
index, e.type & TYPE_MASK, type, e.value, index, e.type, e.base_type, e.unit, e.flags, e.value,
e.value_type == VALUE_NUMERICAL ? "numerical" : e.value_type == VALUE_NUMERICAL ? "numerical" :
(e.value_type == VALUE_STRING ? "string" : "char")); (e.value_type == VALUE_STRING ? "string" : "char"));
} }
#endif
bool optional[MAX_ELEMENTS]; bool optional[MAX_ELEMENTS];
@@ -561,6 +683,7 @@ parsedate_etc(const char *dateString, time_t now, int *_flags)
const char *format = sFormatsTable[index]; const char *format = sFormatsTable[index];
uint32 position = 0; uint32 position = 0;
DateMask dateMask;
parsed_element *element = elements; parsed_element *element = elements;
while (element->type != TYPE_END) { while (element->type != TYPE_END) {
@@ -578,13 +701,13 @@ parsedate_etc(const char *dateString, time_t now, int *_flags)
case VALUE_CHAR: case VALUE_CHAR:
// check the allowed single characters // check the allowed single characters
switch (element->type & TYPE_MASK) { switch (element->type) {
case TYPE_DOT: case TYPE_DOT:
if (format[0] != '.') if (format[0] != '.')
goto next_format; goto next_format;
break; break;
case TYPE_DASH: case TYPE_DASH:
if (format[0] != '/' && format[0] != '-') if (format[0] != '-')
goto next_format; goto next_format;
break; break;
case TYPE_COMMA: case TYPE_COMMA:
@@ -601,29 +724,50 @@ parsedate_etc(const char *dateString, time_t now, int *_flags)
break; break;
case VALUE_NUMERICAL: case VALUE_NUMERICAL:
// make sure that unit types are respected
if (element->type == TYPE_UNIT && format[0] != 'T')
goto next_format;
switch (format[0]) { switch (format[0]) {
case 'd': case 'd':
if (element->value > 31) if (element->value > 31)
goto next_format; goto next_format;
dateMask.Set(TYPE_DAY);
break; break;
case 'm': case 'm':
if (element->value > 12) if (element->value > 12)
goto next_format; goto next_format;
dateMask.Set(TYPE_MONTH);
break; break;
case 'H': case 'H':
case 'I': case 'I':
if (element->value > 24) if (element->value > 24)
goto next_format; goto next_format;
dateMask.Set(TYPE_HOUR);
break; break;
case 'M': case 'M':
dateMask.Set(TYPE_MINUTE);
case 'S': case 'S':
if (element->value > 59) if (element->value > 59)
goto next_format; goto next_format;
break; break;
case 'y': case 'y':
case 'Y': case 'Y':
case 'T': // accept all values
break; break;
case 'T':
dateMask.Set(TYPE_UNIT);
break;
case '-':
if (element->flags & FLAG_HAS_DASH) {
element--; // consider this element again
break;
}
// supposed to fall through
default: default:
goto next_format; goto next_format;
} }
@@ -633,25 +777,30 @@ parsedate_etc(const char *dateString, time_t now, int *_flags)
switch (format[0]) { switch (format[0]) {
case 'a': // weekday case 'a': // weekday
case 'A': case 'A':
if (!isType(element->type, TYPE_WEEKDAY)) if (element->type != TYPE_WEEKDAY)
goto next_format; goto next_format;
break; break;
case 'b': // month case 'b': // month
case 'B': case 'B':
if (!isType(element->type, TYPE_MONTH)) if (element->type != TYPE_MONTH)
goto next_format; goto next_format;
dateMask.Set(TYPE_MONTH);
break; break;
case 'p': // meridian case 'p': // meridian
if (!isType(element->type, TYPE_MERIDIAN)) if (element->type != TYPE_MERIDIAN)
goto next_format; goto next_format;
break; break;
case 'z': // time zone case 'z': // time zone
case 'Z': case 'Z':
if (!isType(element->type, TYPE_TIME_ZONE)) if (element->type != TYPE_TIME_ZONE)
goto next_format; goto next_format;
break; break;
case 'T': case 'T': // time unit
// last/next/this if (element->type != TYPE_UNIT)
goto next_format;
dateMask.Set(TYPE_UNIT);
break; break;
default: default:
goto next_format; goto next_format;
@@ -693,7 +842,7 @@ parsedate_etc(const char *dateString, time_t now, int *_flags)
// made it here? then we seem to have found our guy // made it here? then we seem to have found our guy
return computeDate(sFormatsTable[index], optional, elements, now, _flags); return computeDate(sFormatsTable[index], optional, elements, now, dateMask, _flags);
skip_format: skip_format:
// check if the next format has the same beginning as the skipped one, // check if the next format has the same beginning as the skipped one,