123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
|
/*******************************************************************************
copyright: Copyright (c) 2005 John Chapman. All rights reserved
license: BSD style: $(LICENSE)
version: Initial release: 2005
author: John Chapman
******************************************************************************/
module tango.text.locale.Parse;
private import tango.time.WallClock;
private import tango.core.Exception;
private import tango.text.locale.Core;
private import tango.time.chrono.Calendar;
private struct DateTimeParseResult {
int year = -1;
int month = -1;
int day = -1;
int hour;
int minute;
int second;
double fraction;
int timeMark;
Calendar calendar;
TimeSpan timeZoneOffset;
Time parsedDate;
}
package Time parseTime(const(char)[] s, DateTimeFormat dtf) {
DateTimeParseResult result;
if (!tryParseExactMultiple(s, dtf.getAllDateTimePatterns(), dtf, result))
throw new IllegalArgumentException("String was not a valid Time.");
return result.parsedDate;
}
package Time parseTimeExact(const(char)[] s, const(char)[] format, DateTimeFormat dtf) {
DateTimeParseResult result;
if (!tryParseExact(s, format, dtf, result))
throw new IllegalArgumentException("String was not a valid Time.");
return result.parsedDate;
}
package bool tryParseTime(const(char)[] s, DateTimeFormat dtf, out Time result) {
result = Time.min;
DateTimeParseResult resultRecord;
if (!tryParseExactMultiple(s, dtf.getAllDateTimePatterns(), dtf, resultRecord))
return false;
result = resultRecord.parsedDate;
return true;
}
package bool tryParseTimeExact(const(char)[] s, const(char)[] format, DateTimeFormat dtf, out Time result) {
result = Time.min;
DateTimeParseResult resultRecord;
if (!tryParseExact(s, format, dtf, resultRecord))
return false;
result = resultRecord.parsedDate;
return true;
}
private bool tryParseExactMultiple(const(char)[] s, const(char[])[] formats, DateTimeFormat dtf, ref DateTimeParseResult result) {
foreach (const(char)[] format; formats) {
if (tryParseExact(s, format, dtf, result))
return true;
}
return false;
}
private bool tryParseExact(const(char)[] s, const(char)[] pattern, DateTimeFormat dtf, ref DateTimeParseResult result) {
bool doParse() {
int parseDigits(const(char)[] s, ref int pos, int max) {
int result = s[pos++] - '0';
while (max > 1 && pos < s.length && s[pos] >= '0' && s[pos] <= '9') {
result = result * 10 + s[pos++] - '0';
--max;
}
return result;
}
bool parseOne(const(char)[] s, ref int pos, const(char)[] value) {
if (s[pos .. pos + value.length] != value)
return false;
pos += value.length;
return true;
}
int parseMultiple(const(char)[] s, ref int pos, const(char[])[] values ...) {
int result = -1;
size_t max;
foreach (int i, const(char)[] value; values) {
if (value.length == 0 || s.length - pos < value.length)
continue;
if (s[pos .. pos + value.length] == value) {
if (result == 0 || value.length > max) {
result = i + 1;
max = value.length;
}
}
}
pos += max;
return result;
}
TimeSpan parseTimeZoneOffset(const(char)[] s, ref int pos) {
bool sign;
if (pos < s.length) {
if (s[pos] == '-') {
sign = true;
pos++;
}
else if (s[pos] == '+')
pos++;
}
int hour = parseDigits(s, pos, 2);
int minute;
if (pos < s.length && s[pos] == ':') {
pos++;
minute = parseDigits(s, pos, 2);
}
//Due to dmd bug, this doesn't compile
//TimeSpan result = TimeSpan.hours(hour) + TimeSpan.minutes(minute);
TimeSpan result = TimeSpan(TimeSpan.TicksPerHour * hour + TimeSpan.TicksPerMinute * minute);
if (sign)
result = -result;
return result;
}
char[] stringOf(char c, int count = 1) {
char[] s = new char[count];
s[0 .. count] = c;
return s;
}
result.calendar = dtf.calendar;
result.year = result.month = result.day = -1;
result.hour = result.minute = result.second = 0;
result.fraction = 0.0;
int pos, i, count;
char c;
while (pos < pattern.length && i < s.length) {
c = pattern[pos++];
if (c == ' ') {
i++;
while (i < s.length && s[i] == ' ')
i++;
if (i >= s.length)
break;
continue;
}
count = 1;
switch (c) {
case 'd': case 'm': case 'M': case 'y':
case 'h': case 'H': case 's':
case 't': case 'z':
while (pos < pattern.length && pattern[pos] == c) {
pos++;
count++;
}
break;
case ':':
if (!parseOne(s, i, dtf.timeSeparator))
return false;
continue;
case '/':
if (!parseOne(s, i, dtf.dateSeparator))
return false;
continue;
case '\\':
if (pos < pattern.length) {
c = pattern[pos++];
if (s[i++] != c)
return false;
}
else
return false;
continue;
case '\'':
while (pos < pattern.length) {
c = pattern[pos++];
if (c == '\'')
break;
if (s[i++] != c)
return false;
}
continue;
default:
if (s[i++] != c)
return false;
continue;
}
switch (c) {
case 'd': // day
if (count == 1 || count == 2)
result.day = parseDigits(s, i, 2);
else if (count == 3)
result.day = parseMultiple(s, i, dtf.abbreviatedDayNames);
else
result.day = parseMultiple(s, i, dtf.dayNames);
if (result.day == -1)
return false;
break;
case 'M': // month
if (count == 1 || count == 2)
result.month = parseDigits(s, i, 2);
else if (count == 3)
result.month = parseMultiple(s, i, dtf.abbreviatedMonthNames);
else
result.month = parseMultiple(s, i, dtf.monthNames);
if (result.month == -1)
return false;
break;
case 'y': // year
if (count == 1 || count == 2)
result.year = parseDigits(s, i, 2);
else
result.year = parseDigits(s, i, 4);
if (result.year == -1)
return false;
break;
case 'h': // 12-hour clock
case 'H': // 24-hour clock
result.hour = parseDigits(s, i, 2);
break;
case 'm': // minute
result.minute = parseDigits(s, i, 2);
break;
case 's': // second
result.second = parseDigits(s, i, 2);
break;
case 't': // time mark
if (count == 1)
result.timeMark = parseMultiple(s, i, stringOf(dtf.amDesignator[0]), stringOf(dtf.pmDesignator[0]));
else
result.timeMark = parseMultiple(s, i, dtf.amDesignator, dtf.pmDesignator);
break;
case 'z':
result.timeZoneOffset = parseTimeZoneOffset(s, i);
break;
default:
break;
}
}
if (pos < pattern.length || i < s.length)
return false;
if (result.timeMark == 1) { // am
if (result.hour == 12)
result.hour = 0;
}
else if (result.timeMark == 2) { // pm
if (result.hour < 12)
result.hour += 12;
}
// If the input string didn't specify a date part, try to return something meaningful.
if (result.year == -1 || result.month == -1 || result.day == -1) {
Time now = WallClock.now;
if (result.month == -1 && result.day == -1) {
if (result.year == -1) {
result.year = result.calendar.getYear(now);
result.month = result.calendar.getMonth(now);
result.day = result.calendar.getDayOfMonth(now);
}
else
result.month = result.day = 1;
}
else {
if (result.year == -1)
result.year = result.calendar.getYear(now);
if (result.month == -1)
result.month = 1;
if (result.day == -1)
result.day = 1;
}
}
return true;
}
if (doParse()) {
result.parsedDate = result.calendar.toTime(result.year, result.month, result.day, result.hour, result.minute, result.second, 0);
return true;
}
return false;
}
|