132
|
1 /*******************************************************************************
|
|
2
|
|
3 copyright: Copyright (c) 2005 John Chapman. All rights reserved
|
|
4
|
|
5 license: BSD style: $(LICENSE)
|
|
6
|
|
7 version: Initial release: 2005
|
|
8
|
|
9 author: John Chapman
|
|
10
|
|
11 ******************************************************************************/
|
|
12
|
|
13 module tango.text.locale.Parse;
|
|
14
|
|
15 private import tango.time.WallClock;
|
|
16
|
|
17 private import tango.core.Exception;
|
|
18
|
|
19 private import tango.text.locale.Core;
|
|
20
|
|
21 private import tango.time.chrono.Calendar;
|
|
22
|
|
23 private struct DateTimeParseResult {
|
|
24
|
|
25 int year = -1;
|
|
26 int month = -1;
|
|
27 int day = -1;
|
|
28 int hour;
|
|
29 int minute;
|
|
30 int second;
|
|
31 double fraction;
|
|
32 int timeMark;
|
|
33 Calendar calendar;
|
|
34 TimeSpan timeZoneOffset;
|
|
35 Time parsedDate;
|
|
36
|
|
37 }
|
|
38
|
|
39 package Time parseTime(char[] s, DateTimeFormat dtf) {
|
|
40 DateTimeParseResult result;
|
|
41 if (!tryParseExactMultiple(s, dtf.getAllDateTimePatterns(), dtf, result))
|
|
42 throw new IllegalArgumentException("String was not a valid Time.");
|
|
43 return result.parsedDate;
|
|
44 }
|
|
45
|
|
46 package Time parseTimeExact(char[] s, char[] format, DateTimeFormat dtf) {
|
|
47 DateTimeParseResult result;
|
|
48 if (!tryParseExact(s, format, dtf, result))
|
|
49 throw new IllegalArgumentException("String was not a valid Time.");
|
|
50 return result.parsedDate;
|
|
51 }
|
|
52
|
|
53 package bool tryParseTime(char[] s, DateTimeFormat dtf, out Time result) {
|
|
54 result = Time.min;
|
|
55 DateTimeParseResult resultRecord;
|
|
56 if (!tryParseExactMultiple(s, dtf.getAllDateTimePatterns(), dtf, resultRecord))
|
|
57 return false;
|
|
58 result = resultRecord.parsedDate;
|
|
59 return true;
|
|
60 }
|
|
61
|
|
62 package bool tryParseTimeExact(char[] s, char[] format, DateTimeFormat dtf, out Time result) {
|
|
63 result = Time.min;
|
|
64 DateTimeParseResult resultRecord;
|
|
65 if (!tryParseExact(s, format, dtf, resultRecord))
|
|
66 return false;
|
|
67 result = resultRecord.parsedDate;
|
|
68 return true;
|
|
69 }
|
|
70
|
|
71 private bool tryParseExactMultiple(char[] s, char[][] formats, DateTimeFormat dtf, inout DateTimeParseResult result) {
|
|
72 foreach (char[] format; formats) {
|
|
73 if (tryParseExact(s, format, dtf, result))
|
|
74 return true;
|
|
75 }
|
|
76 return false;
|
|
77 }
|
|
78
|
|
79 private bool tryParseExact(char[] s, char[] pattern, DateTimeFormat dtf, inout DateTimeParseResult result) {
|
|
80
|
|
81 bool doParse() {
|
|
82
|
|
83 int parseDigits(char[] s, inout int pos, int max) {
|
|
84 int result = s[pos++] - '0';
|
|
85 while (max > 1 && pos < s.length && s[pos] >= '0' && s[pos] <= '9') {
|
|
86 result = result * 10 + s[pos++] - '0';
|
|
87 --max;
|
|
88 }
|
|
89 return result;
|
|
90 }
|
|
91
|
|
92 bool parseOne(char[] s, inout int pos, char[] value) {
|
|
93 if (s[pos .. pos + value.length] != value)
|
|
94 return false;
|
|
95 pos += value.length;
|
|
96 return true;
|
|
97 }
|
|
98
|
|
99 int parseMultiple(char[] s, inout int pos, char[][] values ...) {
|
|
100 int result = -1, max;
|
|
101 foreach (int i, char[] value; values) {
|
|
102 if (value.length == 0 || s.length - pos < value.length)
|
|
103 continue;
|
|
104
|
|
105 if (s[pos .. pos + value.length] == value) {
|
|
106 if (result == 0 || value.length > max) {
|
|
107 result = i + 1;
|
|
108 max = value.length;
|
|
109 }
|
|
110 }
|
|
111 }
|
|
112 pos += max;
|
|
113 return result;
|
|
114 }
|
|
115
|
|
116 TimeSpan parseTimeZoneOffset(char[] s, inout int pos) {
|
|
117 bool sign;
|
|
118 if (pos < s.length) {
|
|
119 if (s[pos] == '-') {
|
|
120 sign = true;
|
|
121 pos++;
|
|
122 }
|
|
123 else if (s[pos] == '+')
|
|
124 pos++;
|
|
125 }
|
|
126 int hour = parseDigits(s, pos, 2);
|
|
127 int minute;
|
|
128 if (pos < s.length && s[pos] == ':') {
|
|
129 pos++;
|
|
130 minute = parseDigits(s, pos, 2);
|
|
131 }
|
|
132 //Due to dmd bug, this doesn't compile
|
|
133 //TimeSpan result = TimeSpan.hours(hour) + TimeSpan.minutes(minute);
|
|
134 TimeSpan result = TimeSpan(TimeSpan.TicksPerHour * hour + TimeSpan.TicksPerMinute * minute);
|
|
135 if (sign)
|
|
136 result = -result;
|
|
137 return result;
|
|
138 }
|
|
139
|
|
140 char[] stringOf(char c, int count = 1) {
|
|
141 char[] s = new char[count];
|
|
142 s[0 .. count] = c;
|
|
143 return s;
|
|
144 }
|
|
145
|
|
146 result.calendar = dtf.calendar;
|
|
147 result.year = result.month = result.day = -1;
|
|
148 result.hour = result.minute = result.second = 0;
|
|
149 result.fraction = 0.0;
|
|
150
|
|
151 int pos, i, count;
|
|
152 char c;
|
|
153
|
|
154 while (pos < pattern.length && i < s.length) {
|
|
155 c = pattern[pos++];
|
|
156
|
|
157 if (c == ' ') {
|
|
158 i++;
|
|
159 while (i < s.length && s[i] == ' ')
|
|
160 i++;
|
|
161 if (i >= s.length)
|
|
162 break;
|
|
163 continue;
|
|
164 }
|
|
165
|
|
166 count = 1;
|
|
167
|
|
168 switch (c) {
|
|
169 case 'd': case 'm': case 'M': case 'y':
|
|
170 case 'h': case 'H': case 's':
|
|
171 case 't': case 'z':
|
|
172 while (pos < pattern.length && pattern[pos] == c) {
|
|
173 pos++;
|
|
174 count++;
|
|
175 }
|
|
176 break;
|
|
177 case ':':
|
|
178 if (!parseOne(s, i, dtf.timeSeparator))
|
|
179 return false;
|
|
180 continue;
|
|
181 case '/':
|
|
182 if (!parseOne(s, i, dtf.dateSeparator))
|
|
183 return false;
|
|
184 continue;
|
|
185 case '\\':
|
|
186 if (pos < pattern.length) {
|
|
187 c = pattern[pos++];
|
|
188 if (s[i++] != c)
|
|
189 return false;
|
|
190 }
|
|
191 else
|
|
192 return false;
|
|
193 continue;
|
|
194 case '\'':
|
|
195 while (pos < pattern.length) {
|
|
196 c = pattern[pos++];
|
|
197 if (c == '\'')
|
|
198 break;
|
|
199 if (s[i++] != c)
|
|
200 return false;
|
|
201 }
|
|
202 continue;
|
|
203 default:
|
|
204 if (s[i++] != c)
|
|
205 return false;
|
|
206 continue;
|
|
207 }
|
|
208
|
|
209 switch (c) {
|
|
210 case 'd': // day
|
|
211 if (count == 1 || count == 2)
|
|
212 result.day = parseDigits(s, i, 2);
|
|
213 else if (count == 3)
|
|
214 result.day = parseMultiple(s, i, dtf.abbreviatedDayNames);
|
|
215 else
|
|
216 result.day = parseMultiple(s, i, dtf.dayNames);
|
|
217 if (result.day == -1)
|
|
218 return false;
|
|
219 break;
|
|
220 case 'M': // month
|
|
221 if (count == 1 || count == 2)
|
|
222 result.month = parseDigits(s, i, 2);
|
|
223 else if (count == 3)
|
|
224 result.month = parseMultiple(s, i, dtf.abbreviatedMonthNames);
|
|
225 else
|
|
226 result.month = parseMultiple(s, i, dtf.monthNames);
|
|
227 if (result.month == -1)
|
|
228 return false;
|
|
229 break;
|
|
230 case 'y': // year
|
|
231 if (count == 1 || count == 2)
|
|
232 result.year = parseDigits(s, i, 2);
|
|
233 else
|
|
234 result.year = parseDigits(s, i, 4);
|
|
235 if (result.year == -1)
|
|
236 return false;
|
|
237 break;
|
|
238 case 'h': // 12-hour clock
|
|
239 case 'H': // 24-hour clock
|
|
240 result.hour = parseDigits(s, i, 2);
|
|
241 break;
|
|
242 case 'm': // minute
|
|
243 result.minute = parseDigits(s, i, 2);
|
|
244 break;
|
|
245 case 's': // second
|
|
246 result.second = parseDigits(s, i, 2);
|
|
247 break;
|
|
248 case 't': // time mark
|
|
249 if (count == 1)
|
|
250 result.timeMark = parseMultiple(s, i, stringOf(dtf.amDesignator[0]), stringOf(dtf.pmDesignator[0]));
|
|
251 else
|
|
252 result.timeMark = parseMultiple(s, i, dtf.amDesignator, dtf.pmDesignator);
|
|
253 break;
|
|
254 case 'z':
|
|
255 result.timeZoneOffset = parseTimeZoneOffset(s, i);
|
|
256 break;
|
|
257 default:
|
|
258 break;
|
|
259 }
|
|
260 }
|
|
261
|
|
262 if (pos < pattern.length || i < s.length)
|
|
263 return false;
|
|
264
|
|
265 if (result.timeMark == 1) { // am
|
|
266 if (result.hour == 12)
|
|
267 result.hour = 0;
|
|
268 }
|
|
269 else if (result.timeMark == 2) { // pm
|
|
270 if (result.hour < 12)
|
|
271 result.hour += 12;
|
|
272 }
|
|
273
|
|
274 // If the input string didn't specify a date part, try to return something meaningful.
|
|
275 if (result.year == -1 || result.month == -1 || result.day == -1) {
|
|
276 Time now = WallClock.now;
|
|
277 if (result.month == -1 && result.day == -1) {
|
|
278 if (result.year == -1) {
|
|
279 result.year = result.calendar.getYear(now);
|
|
280 result.month = result.calendar.getMonth(now);
|
|
281 result.day = result.calendar.getDayOfMonth(now);
|
|
282 }
|
|
283 else
|
|
284 result.month = result.day = 1;
|
|
285 }
|
|
286 else {
|
|
287 if (result.year == -1)
|
|
288 result.year = result.calendar.getYear(now);
|
|
289 if (result.month == -1)
|
|
290 result.month = 1;
|
|
291 if (result.day == -1)
|
|
292 result.day = 1;
|
|
293 }
|
|
294 }
|
|
295 return true;
|
|
296 }
|
|
297
|
|
298 if (doParse()) {
|
|
299 result.parsedDate = result.calendar.toTime(result.year, result.month, result.day, result.hour, result.minute, result.second, 0);
|
|
300 return true;
|
|
301 }
|
|
302 return false;
|
|
303 }
|