Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Side by Side Diff: lib/src/intl/date_format_field.dart

Issue 932093004: Add loose parsing option for dates, accepting mixed case and missing delimiters (Closed) Base URL: https://github.com/dart-lang/intl.git@master
Patch Set: Fix test that wasn't checking for exception Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/intl/date_format.dart ('k') | pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of intl; 5 part of intl;
6 6
7 /** 7 /**
8 * This is a private class internal to DateFormat which is used for formatting 8 * This is a private class internal to DateFormat which is used for formatting
9 * particular fields in a template. e.g. if the format is hh:mm:ss then the 9 * particular fields in a template. e.g. if the format is hh:mm:ss then the
10 * fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows 10 * fields would be "hh", ":", "mm", ":", and "ss". Each type of field knows
(...skipping 21 matching lines...) Expand all
32 /** Format date according to our specification and return the result. */ 32 /** Format date according to our specification and return the result. */
33 String format(DateTime date) { 33 String format(DateTime date) {
34 // Default implementation in the superclass, works for both types of 34 // Default implementation in the superclass, works for both types of
35 // literal patterns, and is overridden by _DateFormatPatternField. 35 // literal patterns, and is overridden by _DateFormatPatternField.
36 return pattern; 36 return pattern;
37 } 37 }
38 38
39 /** Abstract method for subclasses to implementing parsing for their format.*/ 39 /** Abstract method for subclasses to implementing parsing for their format.*/
40 void parse(_Stream input, _DateBuilder dateFields); 40 void parse(_Stream input, _DateBuilder dateFields);
41 41
42 /**
43 * Abstract method for subclasses to implementing 'loose' parsing for
44 * their format, accepting input case-insensitively, and allowing some
45 * delimiters to be skipped.
46 */
47 void parseLoose(_Stream input, _DateBuilder dateFields);
48
42 /** Parse a literal field. We just look for the exact input. */ 49 /** Parse a literal field. We just look for the exact input. */
43 void parseLiteral(_Stream input) { 50 void parseLiteral(_Stream input) {
44 var found = input.read(width); 51 var found = input.read(width);
45 if (found != pattern) { 52 if (found != pattern) {
46 throwFormatException(input); 53 throwFormatException(input);
47 } 54 }
48 } 55 }
49 56
57 /**
58 * Parse a literal field. We accept either an exact match, or an arbitrary
59 * amount of whitespace.
60 */
61 void parseLiteralLoose(_Stream input) {
62 var found = input.peek(width);
63 if (found == pattern) {
64 input.read(width);
65 }
66 while (!input.atEnd() && input.peek().trim().isEmpty) {
67 input.read();
68 }
69 }
70
50 /** Throw a format exception with an error message indicating the position.*/ 71 /** Throw a format exception with an error message indicating the position.*/
51 void throwFormatException(_Stream stream) { 72 void throwFormatException(_Stream stream) {
52 throw new FormatException("Trying to read $this from ${stream.contents} " 73 throw new FormatException("Trying to read $this from ${stream.contents} "
53 "at position ${stream.index}"); 74 "at position ${stream.index}");
54 } 75 }
55 } 76 }
56 77
57 /** 78 /**
58 * Represents a literal field - a sequence of characters that doesn't 79 * Represents a literal field - a sequence of characters that doesn't
59 * change according to the date's data. As such, the implementation 80 * change according to the date's data. As such, the implementation
60 * is extremely simple. 81 * is extremely simple.
61 */ 82 */
62 class _DateFormatLiteralField extends _DateFormatField { 83 class _DateFormatLiteralField extends _DateFormatField {
63 84
64 _DateFormatLiteralField(pattern, parent): super(pattern, parent); 85 _DateFormatLiteralField(pattern, parent): super(pattern, parent);
65 86
66 parse(_Stream input, _DateBuilder dateFields) { 87 parse(_Stream input, _DateBuilder dateFields) {
67 return parseLiteral(input); 88 parseLiteral(input);
68 } 89 }
90
91 parseLoose(_Stream input, _DateBuilder dateFields) =>
92 parseLiteralLoose(input);
69 } 93 }
70 94
71 /** 95 /**
72 * Represents a literal field with quoted characters in it. This is 96 * Represents a literal field with quoted characters in it. This is
73 * only slightly more complex than a _DateFormatLiteralField. 97 * only slightly more complex than a _DateFormatLiteralField.
74 */ 98 */
75 class _DateFormatQuotedField extends _DateFormatField { 99 class _DateFormatQuotedField extends _DateFormatField {
76 100
77 String _fullPattern; 101 String _fullPattern;
78 102
79 String fullPattern() => _fullPattern; 103 String fullPattern() => _fullPattern;
80 104
81 _DateFormatQuotedField(pattern, parent): super(pattern, parent) { 105 _DateFormatQuotedField(pattern, parent): super(pattern, parent) {
82 _fullPattern = pattern; 106 _fullPattern = pattern;
83 patchQuotes(); 107 patchQuotes();
84 } 108 }
85 109
86 parse(_Stream input, _DateBuilder dateFields) { 110 parse(_Stream input, _DateBuilder dateFields) {
87 return parseLiteral(input); 111 parseLiteral(input);
88 } 112 }
89 113
114 parseLoose(_Stream input, _DateBuilder dateFields) =>
115 parseLiteralLoose(input);
116
90 void patchQuotes() { 117 void patchQuotes() {
91 if (pattern == "''") { 118 if (pattern == "''") {
92 pattern = "'"; 119 pattern = "'";
93 } else { 120 } else {
94 pattern = pattern.substring(1, pattern.length - 1); 121 pattern = pattern.substring(1, pattern.length - 1);
95 var twoEscapedQuotes = new RegExp(r"''"); 122 var twoEscapedQuotes = new RegExp(r"''");
96 pattern = pattern.replaceAll(twoEscapedQuotes, "'"); 123 pattern = pattern.replaceAll(twoEscapedQuotes, "'");
97 } 124 }
98 } 125 }
99 } 126 }
100 127
128 /**
129 * A field that parses "loosely", meaning that we'll accept input that is
130 * missing delimiters, has upper/lower case mixed up, and might not strictly
131 * conform to the pattern, e.g. the pattern calls for Sep we might accept
132 * sep, september, sEPTember. Doesn't affect numeric fields.
133 */
134 class _LoosePatternField extends _DateFormatPatternField {
135 _LoosePatternField(String pattern, parent) : super(pattern, parent);
136
137 /**
138 * Parse from a list of possibilities, but case-insensitively.
139 * Assumes that input is lower case.
140 */
141 int parseEnumeratedString(_Stream input, List possibilities) {
142 var lowercasePossibilities = possibilities
143 .map((x) => x.toLowerCase())
144 .toList();
145 try {
146 return super.parseEnumeratedString(input, lowercasePossibilities);
147 } on FormatException {
148 return -1;
149 }
150 }
151
152 /**
153 * Parse a month name, case-insensitively, and set it in [dateFields].
154 * Assumes that [input] is lower case.
155 */
156 void parseMonth(input, dateFields) {
157 if (width <= 2) {
158 handleNumericField(input, dateFields.setMonth);
159 return;
160 }
161 var possibilities =
162 [symbols.MONTHS, symbols.SHORTMONTHS];
163 for (var monthNames in possibilities) {
164 var month = parseEnumeratedString(input, monthNames);
165 if (month != -1) {
166 dateFields.month = month + 1;
167 return;
168 }
169 }
170 }
171
172 /**
173 * Parse a standalone day name, case-insensitively.
174 * Assumes that input is lower case. Doesn't do anything
175 */
176 void parseStandaloneDay(input) {
177 // This is ignored, but we still have to skip over it the correct amount.
178 if (width <= 2) {
179 handleNumericField(input, (x) => x);
180 return;
181 }
182 var possibilities =
183 [symbols.STANDALONEWEEKDAYS, symbols.STANDALONESHORTWEEKDAYS];
184 for (var dayNames in possibilities) {
185 var day = parseEnumeratedString(input, dayNames);
186 if (day != -1) {
187 return;
188 }
189 }
190 }
191
192 /**
193 * Parse a standalone month name, case-insensitively.
194 * Assumes that input is lower case. Doesn't do anything
195 */
196 void parseStandaloneMonth(input, dateFields) {
197 if (width <= 2) {
198 handleNumericField(input, (x) => x);
199 return;
200 }
201 var possibilities =
202 [symbols.STANDALONEMONTHS, symbols.STANDALONESHORTMONTHS];
203 for (var monthNames in possibilities) {
204 var month = parseEnumeratedString(input, monthNames);
205 if (month != -1) {
206 dateFields.month = month + 1;
207 return;
208 }
209 }
210 }
211
212 /**
213 * Parse a day of the week name, case-insensitively.
214 * Assumes that input is lower case. Doesn't do anything
215 */
216 void parseDayOfWeek(_Stream input) {
217 // This is IGNORED, but we still have to skip over it the correct amount.
218 if (width <= 2) {
219 handleNumericField(input, (x) => x);
220 return;
221 }
222 var possibilities = [symbols.WEEKDAYS, symbols.SHORTWEEKDAYS];
223 for (var dayNames in possibilities) {
224 var day = parseEnumeratedString(input, dayNames);
225 if (day != -1) {
226 return;
227 }
228 }
229 }
230 }
231
101 /* 232 /*
102 * Represents a field in the pattern that formats some aspect of the 233 * Represents a field in the pattern that formats some aspect of the
103 * date. Consists primarily of a switch on the particular pattern characters 234 * date. Consists primarily of a switch on the particular pattern characters
104 * to determine what to do. 235 * to determine what to do.
105 */ 236 */
106 class _DateFormatPatternField extends _DateFormatField { 237 class _DateFormatPatternField extends _DateFormatField {
107 238
108 _DateFormatPatternField(pattern, parent): super(pattern, parent); 239 _DateFormatPatternField(pattern, parent): super(pattern, parent);
109 240
110 /** Format date according to our specification and return the result. */ 241 /** Format date according to our specification and return the result. */
111 String format(DateTime date) { 242 String format(DateTime date) {
112 return formatField(date); 243 return formatField(date);
113 } 244 }
114 245
115 /** 246 /**
116 * Parse the date according to our specification and put the result 247 * Parse the date according to our specification and put the result
117 * into the correct place in dateFields. 248 * into the correct place in dateFields.
118 */ 249 */
119 void parse(_Stream input, _DateBuilder dateFields) { 250 void parse(_Stream input, _DateBuilder dateFields) {
120 parseField(input, dateFields); 251 parseField(input, dateFields);
121 } 252 }
122 253
254
255 /**
256 * Parse the date according to our specification and put the result
257 * into the correct place in dateFields. Allow looser parsing, accepting
258 * case-insensitive input and skipped delimiters.
259 */
260 void parseLoose(_Stream input, _DateBuilder dateFields) {
261 new _LoosePatternField(pattern, parent).parse(input, dateFields);
262 }
263
123 /** 264 /**
124 * Parse a field representing part of a date pattern. Note that we do not 265 * Parse a field representing part of a date pattern. Note that we do not
125 * return a value, but rather build up the result in [builder]. 266 * return a value, but rather build up the result in [builder].
126 */ 267 */
127 void parseField(_Stream input, _DateBuilder builder) { 268 void parseField(_Stream input, _DateBuilder builder) {
128 try { 269 try {
129 switch(pattern[0]) { 270 switch(pattern[0]) {
130 case 'a': parseAmPm(input, builder); break; 271 case 'a': parseAmPm(input, builder); break;
131 case 'c': parseStandaloneDay(input); break; 272 case 'c': parseStandaloneDay(input); break;
132 case 'd': handleNumericField(input, builder.setDay); break; // day 273 case 'd': handleNumericField(input, builder.setDay); break; // day
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after
426 var basicString = toBePrinted.toString(); 567 var basicString = toBePrinted.toString();
427 if (basicString.length >= width) return basicString; 568 if (basicString.length >= width) return basicString;
428 var buffer = new StringBuffer(); 569 var buffer = new StringBuffer();
429 for (var i = 0; i < width - basicString.length; i++) { 570 for (var i = 0; i < width - basicString.length; i++) {
430 buffer.write('0'); 571 buffer.write('0');
431 } 572 }
432 buffer.write(basicString); 573 buffer.write(basicString);
433 return buffer.toString(); 574 return buffer.toString();
434 } 575 }
435 } 576 }
OLDNEW
« no previous file with comments | « lib/src/intl/date_format.dart ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698