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:
+327
-178
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user