Added an initial implementation of parsedate(), parsedate_etc(), get_dateformats(),
and set_dateformats(). It's not complete yet, but already quite useful. Added a "d.m.y" to the R5 set of formats. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4367 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,733 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2003, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
#include <parsedate.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* The date format is as follows:
|
||||||
|
*
|
||||||
|
* a/A weekday
|
||||||
|
* d day of month
|
||||||
|
* b/B month name
|
||||||
|
* m month
|
||||||
|
* y/Y year
|
||||||
|
* H/I hours
|
||||||
|
* M minute
|
||||||
|
* S seconds
|
||||||
|
* p meridian (i.e. am/pm)
|
||||||
|
* T time unit: last hour, next tuesday, today, ...
|
||||||
|
* z/Z time zone
|
||||||
|
* - dash or slash
|
||||||
|
*
|
||||||
|
* Any of ",.:" is allowed and will be expected in the input string as is.
|
||||||
|
* You can enclose a single field with "[]" to mark it as being optional.
|
||||||
|
* A space stands for white space.
|
||||||
|
* No other character is allowed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char * const kFormatsTable[] = {
|
||||||
|
"[A][,] B d[,] H:M:S [p] Y[,] [Z]",
|
||||||
|
"[A][,] B d[,] [Y][,] H:M:S [p] [Z]",
|
||||||
|
"[A][,] B d[,] [Y][,] H:M [p][,] [Z]",
|
||||||
|
"[A][,] B d[,] [Y][,] H [p][,] [Z]",
|
||||||
|
"[A][,] B d[,] H:M [p][,] [Y] [Z]",
|
||||||
|
"[A][,] d B[,] [Y][,] H:M [p][,] [Z]",
|
||||||
|
"[A][,] d B[,] [Y][,] H:M:S [p][,] [Z]",
|
||||||
|
"[A][,] d B[,] H:M:S [Y][,] [p][,] [Z]",
|
||||||
|
"[A][,] d B[,] H:M [Y][,] [p][,] [Z]",
|
||||||
|
"d.m.y H:M:S [p] [Z]",
|
||||||
|
"d.m.y H:M [p] [Z]",
|
||||||
|
"d.m.y",
|
||||||
|
"[A][,] m-d-y[,] [H][:][M] [p]",
|
||||||
|
"[A][,] m-d[,] H:M [p]",
|
||||||
|
"[A][,] m-d[,] H[p]",
|
||||||
|
"[A][,] m-d",
|
||||||
|
"[A][,] B d[,] Y",
|
||||||
|
"[A][,] H:M [p]",
|
||||||
|
"[A][,] H [p]",
|
||||||
|
"H:M [p][,] [A]",
|
||||||
|
"H [p][,] [A]",
|
||||||
|
"[A][,] B d[,] H:M:S [p] [Z] [Y]",
|
||||||
|
"[A][,] B d[,] H:M [p] [Z] [Y]",
|
||||||
|
"[A][,] d B [,] H:M:S [p] [Z] [Y]",
|
||||||
|
"[A][,] d B [,] H:M [p] [Z] [Y]",
|
||||||
|
"[A][,] d-B-Y H:M:S [p] [Z]",
|
||||||
|
"[A][,] d-B-Y H:M [p] [Z]",
|
||||||
|
"d B Y H:M:S [Z]",
|
||||||
|
"d B Y H:M [Z]",
|
||||||
|
"y-m-d",
|
||||||
|
"y-m-d H:M:S [p] [Z]",
|
||||||
|
"m-d-y H[p]",
|
||||||
|
"m-d-y H:M[p]",
|
||||||
|
"m-d-y H:M:S[p]",
|
||||||
|
"H[p] m-d-y",
|
||||||
|
"H:M[p] m-d-y",
|
||||||
|
"H:M:S[p] m-d-y",
|
||||||
|
"A[,] H:M:S [p] [Z]",
|
||||||
|
"A[,] H:M [p] [Z]",
|
||||||
|
"H:M:S [p] [Z]",
|
||||||
|
"H:M [p] [Z]",
|
||||||
|
"A[,] [B] [d] [Y]",
|
||||||
|
"A[,] [d] [B] [Y]",
|
||||||
|
"B d[,][Y] H[p][,] [Z]",
|
||||||
|
"B d[,] H[p]",
|
||||||
|
"B d [,] H:M [p]",
|
||||||
|
"d B [,][Y] H [p] [Z]",
|
||||||
|
"d B [,] H:M [p]",
|
||||||
|
"B d [,][Y]",
|
||||||
|
"B d [,] H:M [p][,] [Y]",
|
||||||
|
"B d [,] H [p][,] [Y]",
|
||||||
|
"d B [,][Y]",
|
||||||
|
"H[p] [,] B d",
|
||||||
|
"H:M[p] [,] B d",
|
||||||
|
"T [T][T][T][T][T]",
|
||||||
|
"T H:M:S [p]",
|
||||||
|
"T H:M [p]",
|
||||||
|
"T H [p]",
|
||||||
|
"H:M [p] T",
|
||||||
|
"H [p] T",
|
||||||
|
"H [p]",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
static const char * const *sFormatsTable = kFormatsTable;
|
||||||
|
|
||||||
|
|
||||||
|
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_DAY = 1 | TYPE_DAY_UNIT,
|
||||||
|
TYPE_MONTH = 2 | TYPE_MONTH_UNIT,
|
||||||
|
TYPE_YEAR = 3 | TYPE_MONTH_UNIT,
|
||||||
|
TYPE_WEEKDAY = 4 | TYPE_DAY_UNIT,
|
||||||
|
TYPE_NOW = 5,
|
||||||
|
TYPE_HOUR = 6 | TYPE_SECOND_UNIT,
|
||||||
|
TYPE_MINUTE = 7 | TYPE_SECOND_UNIT,
|
||||||
|
TYPE_SECOND = 8 | TYPE_SECOND_UNIT,
|
||||||
|
TYPE_TIME_ZONE = 9 | TYPE_SECOND_UNIT,
|
||||||
|
TYPE_MERIDIAN = 10 | TYPE_SECOND_UNIT,
|
||||||
|
|
||||||
|
TYPE_DASH = 12,
|
||||||
|
TYPE_DOT = 13,
|
||||||
|
TYPE_COLON = 14,
|
||||||
|
TYPE_SEMICOLON = 15,
|
||||||
|
|
||||||
|
TYPE_PLUS_MINUS = 16 | TYPE_MODIFIER | TYPE_RELATIVE,
|
||||||
|
TYPE_NEXT_LAST_THIS = 17 | TYPE_MODIFIER | TYPE_RELATIVE,
|
||||||
|
|
||||||
|
TYPE_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum value_type {
|
||||||
|
VALUE_NUMERICAL,
|
||||||
|
VALUE_STRING,
|
||||||
|
VALUE_CHAR,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum value_modifier {
|
||||||
|
MODIFY_PLUS,
|
||||||
|
MODIFY_MINUS,
|
||||||
|
MODIFY_NEXT,
|
||||||
|
MODIFY_THIS,
|
||||||
|
MODIFY_LAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct known_identifier {
|
||||||
|
const char *string;
|
||||||
|
int32 type;
|
||||||
|
int32 value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const known_identifier kIdentifiers[] = {
|
||||||
|
{"today", TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_DAY_UNIT, 0},
|
||||||
|
{"tomorrow", TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_DAY_UNIT, 1},
|
||||||
|
{"yesterday", TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_DAY_UNIT, -1},
|
||||||
|
{"now", TYPE_RELATIVE | TYPE_NOT_MODIFIABLE | TYPE_NOW, 0},
|
||||||
|
|
||||||
|
{"this", TYPE_NEXT_LAST_THIS, 0},
|
||||||
|
{"next", TYPE_NEXT_LAST_THIS, 1},
|
||||||
|
{"last", TYPE_NEXT_LAST_THIS, -1},
|
||||||
|
|
||||||
|
{"year", TYPE_UNIT | TYPE_RELATIVE | TYPE_YEAR_UNIT, 1},
|
||||||
|
{"month", TYPE_UNIT | TYPE_RELATIVE | TYPE_MONTH_UNIT, 1},
|
||||||
|
{"day", TYPE_UNIT | TYPE_RELATIVE | TYPE_DAY_UNIT, 1},
|
||||||
|
{"days", TYPE_UNIT | TYPE_RELATIVE | TYPE_DAY_UNIT, 1},
|
||||||
|
{"hour", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1 * 60 * 60},
|
||||||
|
{"hrs", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1 * 60 * 60},
|
||||||
|
{"hours", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1 * 60 * 60},
|
||||||
|
{"sec", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1},
|
||||||
|
{"second", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1},
|
||||||
|
{"seconds", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 1},
|
||||||
|
{"min", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 60},
|
||||||
|
{"minute", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 60},
|
||||||
|
{"minutes", TYPE_UNIT | TYPE_RELATIVE | TYPE_SECOND_UNIT, 60},
|
||||||
|
|
||||||
|
{"am", TYPE_MERIDIAN | TYPE_NOT_MODIFIABLE, 0},
|
||||||
|
{"pm", TYPE_MERIDIAN | TYPE_NOT_MODIFIABLE, 12 * 60 * 60}, // 12 hours
|
||||||
|
|
||||||
|
{"sunday", TYPE_WEEKDAY, 0},
|
||||||
|
{"monday", TYPE_WEEKDAY, 1},
|
||||||
|
{"tuesday", TYPE_WEEKDAY, 2},
|
||||||
|
{"wednesday", TYPE_WEEKDAY, 3},
|
||||||
|
{"thursday", TYPE_WEEKDAY, 4},
|
||||||
|
{"friday", TYPE_WEEKDAY, 5},
|
||||||
|
{"saturday", TYPE_WEEKDAY, 6},
|
||||||
|
|
||||||
|
{"january", TYPE_MONTH, 1},
|
||||||
|
{"february", TYPE_MONTH, 2},
|
||||||
|
{"march", TYPE_MONTH, 3},
|
||||||
|
{"april", TYPE_MONTH, 4},
|
||||||
|
{"may", TYPE_MONTH, 5},
|
||||||
|
{"june", TYPE_MONTH, 6},
|
||||||
|
{"july", TYPE_MONTH, 7},
|
||||||
|
{"august", TYPE_MONTH, 8},
|
||||||
|
{"september", TYPE_MONTH, 9},
|
||||||
|
{"october", TYPE_MONTH, 10},
|
||||||
|
{"november", TYPE_MONTH, 11},
|
||||||
|
{"december", TYPE_MONTH, 12},
|
||||||
|
|
||||||
|
{"GMT", TYPE_TIME_ZONE, 0},
|
||||||
|
// ToDo: add more time zones
|
||||||
|
|
||||||
|
{NULL, TYPE_UNKNOWN, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_ELEMENTS 32
|
||||||
|
|
||||||
|
struct parsed_element {
|
||||||
|
int32 type;
|
||||||
|
int32 value_type;
|
||||||
|
int32 modifier;
|
||||||
|
bigtime_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
isType(int32 typeA, int32 typeB)
|
||||||
|
{
|
||||||
|
return (typeA & TYPE_MASK) == (typeB & TYPE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
preparseDate(const char *dateString, parsed_element *elements)
|
||||||
|
{
|
||||||
|
int32 index = 0;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (dateString == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
for (; (c = dateString[0]) != '\0'; dateString++) {
|
||||||
|
// we don't care about spaces
|
||||||
|
if (isspace(c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if we're reached our maximum number of elements, bail out
|
||||||
|
if (index >= MAX_ELEMENTS)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
memset(&elements[index], 0, sizeof(parsed_element));
|
||||||
|
|
||||||
|
if (c == ',') {
|
||||||
|
elements[index].type = TYPE_COLON;
|
||||||
|
elements[index].value_type = VALUE_CHAR;
|
||||||
|
} else if (c == '.') {
|
||||||
|
elements[index].type = TYPE_DOT;
|
||||||
|
elements[index].value_type = VALUE_CHAR;
|
||||||
|
} else if (c == '/' || c == '-') {
|
||||||
|
// ToDo: "-" can also be the minus modifier... - how to distinguish?
|
||||||
|
elements[index].type = TYPE_DASH;
|
||||||
|
elements[index].value_type = VALUE_CHAR;
|
||||||
|
} else if (c == ':') {
|
||||||
|
elements[index].type = TYPE_SEMICOLON;
|
||||||
|
elements[index].value_type = VALUE_CHAR;
|
||||||
|
} else if (c == '+') {
|
||||||
|
elements[index].type = TYPE_RELATIVE;
|
||||||
|
elements[index].value_type = VALUE_CHAR;
|
||||||
|
elements[index].modifier = MODIFY_PLUS;
|
||||||
|
|
||||||
|
// this counts for the next element
|
||||||
|
continue;
|
||||||
|
} else if (c == '-') {
|
||||||
|
// ToDo: this never happens, due to TYPE_DASH
|
||||||
|
elements[index].type = TYPE_MODIFIER;
|
||||||
|
elements[index].value_type = VALUE_CHAR;
|
||||||
|
elements[index].modifier = MODIFY_MINUS;
|
||||||
|
|
||||||
|
// this counts for the next element
|
||||||
|
continue;
|
||||||
|
} else if (isdigit(c)) {
|
||||||
|
// fetch whole number
|
||||||
|
|
||||||
|
elements[index].type = TYPE_UNKNOWN;
|
||||||
|
elements[index].value_type = VALUE_NUMERICAL;
|
||||||
|
elements[index].value = atoll(dateString);
|
||||||
|
|
||||||
|
// skip number
|
||||||
|
while (isdigit(dateString[1]))
|
||||||
|
dateString++;
|
||||||
|
} else if (isalpha(c)) {
|
||||||
|
// fetch whole string
|
||||||
|
|
||||||
|
const char *string = dateString;
|
||||||
|
while (isalpha(dateString[1]))
|
||||||
|
dateString++;
|
||||||
|
int32 length = dateString + 1 - string;
|
||||||
|
|
||||||
|
// compare with known strings
|
||||||
|
// ToDo: should understand other languages as well...
|
||||||
|
|
||||||
|
const known_identifier *identifier = kIdentifiers;
|
||||||
|
for (; identifier->string; identifier++) {
|
||||||
|
if (!strncasecmp(identifier->string, string, length)
|
||||||
|
&& !identifier->string[length])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (identifier->string == NULL) {
|
||||||
|
// unknown string, we don't have to parse any further
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 0 && (identifier->type & TYPE_UNIT) != 0) {
|
||||||
|
// this is just a unit, so it will give the last value a meaning
|
||||||
|
|
||||||
|
if (elements[--index].value_type != VALUE_NUMERICAL
|
||||||
|
&& elements[index].type != TYPE_NEXT_LAST_THIS)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
elements[index].value *= identifier->value;
|
||||||
|
} else if (index > 0 && elements[index - 1].type == TYPE_NEXT_LAST_THIS) {
|
||||||
|
if (isType(identifier->type, TYPE_MONTH)
|
||||||
|
|| isType(identifier->type, TYPE_WEEKDAY)) {
|
||||||
|
index--;
|
||||||
|
|
||||||
|
switch (elements[index].value) {
|
||||||
|
case -1:
|
||||||
|
elements[index].modifier = MODIFY_LAST;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
elements[index].modifier = MODIFY_THIS;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
elements[index].modifier = MODIFY_NEXT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elements[index].value = identifier->value;
|
||||||
|
} else
|
||||||
|
return B_ERROR;
|
||||||
|
} else {
|
||||||
|
elements[index].value_type = VALUE_STRING;
|
||||||
|
elements[index].value = identifier->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
elements[index].type = identifier->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if we can join any preceding modifiers
|
||||||
|
|
||||||
|
if (index > 0
|
||||||
|
&& (elements[index - 1].type & TYPE_MODIFIER) != 0
|
||||||
|
&& (elements[index].type & TYPE_NOT_MODIFIABLE) == 0) {
|
||||||
|
// copy the current one to the last and go on
|
||||||
|
elements[index].modifier = elements[index - 1].modifier;
|
||||||
|
elements[index].value *= elements[index - 1].value;
|
||||||
|
elements[index - 1] = elements[index];
|
||||||
|
} else {
|
||||||
|
// we filled out one parsed_element
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// were there any elements?
|
||||||
|
if (index == 0)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
elements[index].type = TYPE_END;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
computeRelativeUnit(parsed_element &element, struct tm &tm, time_t &now, bool &makeTime, bool isString)
|
||||||
|
{
|
||||||
|
uint32 type = element.type;
|
||||||
|
|
||||||
|
// ToDo: should change flags as well!
|
||||||
|
|
||||||
|
makeTime = true;
|
||||||
|
localtime_r(&now, &tm);
|
||||||
|
|
||||||
|
// set the relative start depending on unit
|
||||||
|
|
||||||
|
if (type & (TYPE_DAY_UNIT | TYPE_MONTH_UNIT | TYPE_YEAR_UNIT)) {
|
||||||
|
tm.tm_hour = 0;
|
||||||
|
tm.tm_min = 0;
|
||||||
|
tm.tm_sec = 0;
|
||||||
|
}
|
||||||
|
if (type & (TYPE_MONTH_UNIT | TYPE_YEAR_UNIT))
|
||||||
|
tm.tm_mday = 1;
|
||||||
|
if (type & TYPE_YEAR_UNIT)
|
||||||
|
tm.tm_mon = 0;
|
||||||
|
|
||||||
|
// adjust value
|
||||||
|
|
||||||
|
if (type & TYPE_RELATIVE) {
|
||||||
|
if (type & TYPE_MONTH_UNIT)
|
||||||
|
tm.tm_mon += element.value;
|
||||||
|
else if (type & TYPE_DAY_UNIT)
|
||||||
|
tm.tm_mday += element.value;
|
||||||
|
else if (type & TYPE_SECOND_UNIT)
|
||||||
|
tm.tm_sec += element.value;
|
||||||
|
else if (!isType(type, TYPE_NOW))
|
||||||
|
;
|
||||||
|
//puts("what's going on here?");
|
||||||
|
} else if (isType(type, TYPE_WEEKDAY)) {
|
||||||
|
tm.tm_mday += abs(element.value) - tm.tm_wday;
|
||||||
|
|
||||||
|
if (element.modifier == MODIFY_NEXT)
|
||||||
|
tm.tm_mday += 7;
|
||||||
|
else if (element.modifier == MODIFY_LAST)
|
||||||
|
tm.tm_mday -= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
computeDate(const char *format, bool *optional, parsed_element *elements, time_t now, int *_flags)
|
||||||
|
{
|
||||||
|
//printf("matches: %s\n", format);
|
||||||
|
|
||||||
|
parsed_element *element = elements;
|
||||||
|
uint32 position = 0;
|
||||||
|
struct tm tm;
|
||||||
|
time_t date;
|
||||||
|
bool relative = false, makeTime = false;
|
||||||
|
|
||||||
|
// ToDo: only call time() if necessary
|
||||||
|
if (now == -1)
|
||||||
|
now = time(NULL);
|
||||||
|
|
||||||
|
memset(&tm, 0, sizeof(tm));
|
||||||
|
// localtime_r(&now, &tm);
|
||||||
|
// printf("now = %ld, -> %ld\n", now, mktime(&tm));
|
||||||
|
|
||||||
|
while (element->type != TYPE_END) {
|
||||||
|
// skip whitespace
|
||||||
|
while (isspace(format[0]))
|
||||||
|
format++;
|
||||||
|
|
||||||
|
if (format[0] == '[' && format[2] == ']') {
|
||||||
|
// does this optional parameter not match our date string?
|
||||||
|
if (!optional[position]) {
|
||||||
|
format += 3;
|
||||||
|
position++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (element->value_type) {
|
||||||
|
case VALUE_CHAR:
|
||||||
|
// skip the single character
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VALUE_NUMERICAL:
|
||||||
|
switch (format[0]) {
|
||||||
|
case 'd':
|
||||||
|
tm.tm_mday = element->value;
|
||||||
|
makeTime = true;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
tm.tm_mon = element->value - 1;
|
||||||
|
makeTime = true;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
case 'I':
|
||||||
|
tm.tm_hour = element->value;
|
||||||
|
makeTime = true;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
tm.tm_min = element->value;
|
||||||
|
makeTime = true;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
tm.tm_sec = element->value;
|
||||||
|
makeTime = true;
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
case 'Y':
|
||||||
|
tm.tm_year = element->value;
|
||||||
|
if (tm.tm_year > 1900)
|
||||||
|
tm.tm_year -= 1900;
|
||||||
|
makeTime = true;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
// ToDo:!
|
||||||
|
computeRelativeUnit(*element, tm, now, makeTime, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VALUE_STRING:
|
||||||
|
switch (format[0]) {
|
||||||
|
case 'a': // weekday
|
||||||
|
case 'A':
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
break;
|
||||||
|
case 'z': // time zone
|
||||||
|
case 'Z':
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
computeRelativeUnit(*element, tm, now, makeTime, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// format matched at this point, check next element
|
||||||
|
format++;
|
||||||
|
position++;
|
||||||
|
element++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (makeTime)
|
||||||
|
return mktime(&tm);
|
||||||
|
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
time_t
|
||||||
|
parsedate_etc(const char *dateString, time_t now, int *_flags)
|
||||||
|
{
|
||||||
|
// preparse date string so that it can be easily compared to our formats
|
||||||
|
|
||||||
|
parsed_element elements[MAX_ELEMENTS];
|
||||||
|
|
||||||
|
if (preparseDate(dateString, elements) < B_OK) {
|
||||||
|
*_flags = PARSEDATE_INVALID_DATE;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*for (int32 index = 0; elements[index].type != TYPE_END; 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",
|
||||||
|
index, e.type & TYPE_MASK, type, e.value,
|
||||||
|
e.value_type == VALUE_NUMERICAL ? "numerical" :
|
||||||
|
(e.value_type == VALUE_STRING ? "string" : "char"));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
bool optional[MAX_ELEMENTS];
|
||||||
|
|
||||||
|
for (int32 index = 0; sFormatsTable[index]; index++) {
|
||||||
|
// test if this format matches our date string
|
||||||
|
|
||||||
|
const char *format = sFormatsTable[index];
|
||||||
|
uint32 position = 0;
|
||||||
|
|
||||||
|
parsed_element *element = elements;
|
||||||
|
while (element->type != TYPE_END) {
|
||||||
|
// skip whitespace
|
||||||
|
while (isspace(format[0]))
|
||||||
|
format++;
|
||||||
|
|
||||||
|
if (format[0] == '[' && format[2] == ']') {
|
||||||
|
optional[position] = true;
|
||||||
|
format++;
|
||||||
|
} else
|
||||||
|
optional[position] = false;
|
||||||
|
|
||||||
|
switch (element->value_type) {
|
||||||
|
case VALUE_CHAR:
|
||||||
|
// check the allowed single characters
|
||||||
|
|
||||||
|
switch (element->type & TYPE_MASK) {
|
||||||
|
case TYPE_DOT:
|
||||||
|
if (format[0] != '.')
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case TYPE_DASH:
|
||||||
|
if (format[0] != '/' && format[0] != '-')
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case TYPE_COLON:
|
||||||
|
if (format[0] != ',')
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case TYPE_SEMICOLON:
|
||||||
|
if (format[0] != ';')
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto next_format;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VALUE_NUMERICAL:
|
||||||
|
switch (format[0]) {
|
||||||
|
case 'd':
|
||||||
|
if (element->value > 31)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
if (element->value > 12)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
case 'I':
|
||||||
|
if (element->value > 24)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
case 'S':
|
||||||
|
if (element->value > 59)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
case 'Y':
|
||||||
|
case 'T':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto next_format;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VALUE_STRING:
|
||||||
|
switch (format[0]) {
|
||||||
|
case 'a': // weekday
|
||||||
|
case 'A':
|
||||||
|
if ((element->type & TYPE_MASK) != TYPE_WEEKDAY)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'b': // month
|
||||||
|
case 'B':
|
||||||
|
if ((element->type & TYPE_MASK) != TYPE_MONTH)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'p': // meridian
|
||||||
|
if ((element->type & TYPE_MASK) != TYPE_MERIDIAN)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'z': // time zone
|
||||||
|
case 'Z':
|
||||||
|
if ((element->type & TYPE_MASK) != TYPE_TIME_ZONE)
|
||||||
|
goto next_format;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
// last/next/this
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto next_format;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// format matched at this point, check next element
|
||||||
|
if (optional[position])
|
||||||
|
format++;
|
||||||
|
format++;
|
||||||
|
position++;
|
||||||
|
element++;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
next_format:
|
||||||
|
// format didn't match element - let's see if the current
|
||||||
|
// one is only optional (in which case we can continue)
|
||||||
|
if (!optional[position])
|
||||||
|
goto skip_format;
|
||||||
|
|
||||||
|
optional[position] = false;
|
||||||
|
format += 2;
|
||||||
|
position++;
|
||||||
|
// skip the closing ']'
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the format is already empty (since we reached our last element)
|
||||||
|
while (format[0]) {
|
||||||
|
if (format[0] == '[')
|
||||||
|
format += 3;
|
||||||
|
else if (isspace(format[0]))
|
||||||
|
format++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (format[0])
|
||||||
|
goto skip_format;
|
||||||
|
|
||||||
|
// made it here? then we seem to have found our guy
|
||||||
|
|
||||||
|
return computeDate(sFormatsTable[index], optional, elements, now, _flags);
|
||||||
|
|
||||||
|
skip_format:
|
||||||
|
// check if the next format has the same beginning as the skipped one,
|
||||||
|
// and if so, skip that one, too.
|
||||||
|
|
||||||
|
int32 length = format + 1 - sFormatsTable[index];
|
||||||
|
|
||||||
|
while (sFormatsTable[index + 1]
|
||||||
|
&& !strncmp(sFormatsTable[index], sFormatsTable[index + 1], length))
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't find any matching formats
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
time_t
|
||||||
|
parsedate(const char *dateString, time_t now)
|
||||||
|
{
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
return parsedate_etc(dateString, now, &flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
set_dateformats(const char **table)
|
||||||
|
{
|
||||||
|
sFormatsTable = table ? table : kFormatsTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char **
|
||||||
|
get_dateformats(void)
|
||||||
|
{
|
||||||
|
return const_cast<const char **>(sFormatsTable);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user