| 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; } |