1 | /* |
2 | * Copyright 2001-2006,2008 Stephen Colebourne |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | package org.joda.time.format; |
17 | |
18 | import java.util.Collection; |
19 | import java.util.HashSet; |
20 | import java.util.Set; |
21 | |
22 | import org.joda.time.DateTimeFieldType; |
23 | import org.joda.time.DateTimeZone; |
24 | |
25 | /** |
26 | * Factory that creates instances of DateTimeFormatter for the ISO8601 standard. |
27 | * <p> |
28 | * Datetime formatting is performed by the {@link DateTimeFormatter} class. |
29 | * Three classes provide factory methods to create formatters, and this is one. |
30 | * The others are {@link DateTimeFormat} and {@link DateTimeFormatterBuilder}. |
31 | * <p> |
32 | * ISO8601 is the international standard for data interchange. It defines a |
33 | * framework, rather than an absolute standard. As a result this provider has a |
34 | * number of methods that represent common uses of the framework. The most common |
35 | * formats are {@link #date() date}, {@link #time() time}, and {@link #dateTime() dateTime}. |
36 | * <p> |
37 | * For example, to format a date time in ISO format: |
38 | * <pre> |
39 | * DateTime dt = new DateTime(); |
40 | * DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); |
41 | * String str = fmt.print(dt); |
42 | * </pre> |
43 | * <p> |
44 | * It is important to understand that these formatters are not linked to |
45 | * the <code>ISOChronology</code>. These formatters may be used with any |
46 | * chronology, however there may be certain side effects with more unusual |
47 | * chronologies. For example, the ISO formatters rely on dayOfWeek being |
48 | * single digit, dayOfMonth being two digit and dayOfYear being three digit. |
49 | * A chronology with a ten day week would thus cause issues. However, in |
50 | * general, it is safe to use these formatters with other chronologies. |
51 | * <p> |
52 | * ISODateTimeFormat is thread-safe and immutable, and the formatters it |
53 | * returns are as well. |
54 | * |
55 | * @author Brian S O'Neill |
56 | * @since 1.0 |
57 | * @see DateTimeFormat |
58 | * @see DateTimeFormatterBuilder |
59 | */ |
60 | public class ISODateTimeFormat { |
61 | |
62 | //----------------------------------------------------------------------- |
63 | private static DateTimeFormatter |
64 | ye, // year element (yyyy) |
65 | mye, // monthOfYear element (-MM) |
66 | dme, // dayOfMonth element (-dd) |
67 | we, // weekyear element (xxxx) |
68 | wwe, // weekOfWeekyear element (-ww) |
69 | dwe, // dayOfWeek element (-ee) |
70 | dye, // dayOfYear element (-DDD) |
71 | hde, // hourOfDay element (HH) |
72 | mhe, // minuteOfHour element (:mm) |
73 | sme, // secondOfMinute element (:ss) |
74 | fse, // fractionOfSecond element (.SSSSSSSSS) |
75 | ze, // zone offset element |
76 | lte, // literal 'T' element |
77 | |
78 | //y, // year (same as year element) |
79 | ym, // year month |
80 | ymd, // year month day |
81 | |
82 | //w, // weekyear (same as weekyear element) |
83 | ww, // weekyear week |
84 | wwd, // weekyear week day |
85 | |
86 | //h, // hour (same as hour element) |
87 | hm, // hour minute |
88 | hms, // hour minute second |
89 | hmsl, // hour minute second millis |
90 | hmsf, // hour minute second fraction |
91 | |
92 | dh, // date hour |
93 | dhm, // date hour minute |
94 | dhms, // date hour minute second |
95 | dhmsl, // date hour minute second millis |
96 | dhmsf, // date hour minute second fraction |
97 | |
98 | //d, // date (same as ymd) |
99 | t, // time |
100 | tx, // time no millis |
101 | tt, // Ttime |
102 | ttx, // Ttime no millis |
103 | dt, // date time |
104 | dtx, // date time no millis |
105 | |
106 | //wd, // week date (same as wwd) |
107 | wdt, // week date time |
108 | wdtx, // week date time no millis |
109 | |
110 | od, // ordinal date (same as yd) |
111 | odt, // ordinal date time |
112 | odtx, // ordinal date time no millis |
113 | |
114 | bd, // basic date |
115 | bt, // basic time |
116 | btx, // basic time no millis |
117 | btt, // basic Ttime |
118 | bttx, // basic Ttime no millis |
119 | bdt, // basic date time |
120 | bdtx, // basic date time no millis |
121 | |
122 | bod, // basic ordinal date |
123 | bodt, // basic ordinal date time |
124 | bodtx, // basic ordinal date time no millis |
125 | |
126 | bwd, // basic week date |
127 | bwdt, // basic week date time |
128 | bwdtx, // basic week date time no millis |
129 | |
130 | dpe, // date parser element |
131 | tpe, // time parser element |
132 | dp, // date parser |
133 | ldp, // local date parser |
134 | tp, // time parser |
135 | ltp, // local time parser |
136 | dtp, // date time parser |
137 | dotp, // date optional time parser |
138 | ldotp; // local date optional time parser |
139 | |
140 | /** |
141 | * Constructor. |
142 | * |
143 | * @since 1.1 (previously private) |
144 | */ |
145 | protected ISODateTimeFormat() { |
146 | super(); |
147 | } |
148 | |
149 | //----------------------------------------------------------------------- |
150 | /** |
151 | * Returns a formatter that outputs only those fields specified. |
152 | * <p> |
153 | * This method examines the fields provided and returns an ISO-style |
154 | * formatter that best fits. This can be useful for outputting |
155 | * less-common ISO styles, such as YearMonth (YYYY-MM) or MonthDay (--MM-DD). |
156 | * <p> |
157 | * The list provided may have overlapping fields, such as dayOfWeek and |
158 | * dayOfMonth. In this case, the style is chosen based on the following |
159 | * list, thus in the example, the calendar style is chosen as dayOfMonth |
160 | * is higher in priority than dayOfWeek: |
161 | * <ul> |
162 | * <li>monthOfYear - calendar date style |
163 | * <li>dayOfYear - ordinal date style |
164 | * <li>weekOfWeekYear - week date style |
165 | * <li>dayOfMonth - calendar date style |
166 | * <li>dayOfWeek - week date style |
167 | * <li>year |
168 | * <li>weekyear |
169 | * </ul> |
170 | * The supported formats are: |
171 | * <pre> |
172 | * Extended Basic Fields |
173 | * 2005-03-25 20050325 year/monthOfYear/dayOfMonth |
174 | * 2005-03 2005-03 year/monthOfYear |
175 | * 2005--25 2005--25 year/dayOfMonth * |
176 | * 2005 2005 year |
177 | * --03-25 --0325 monthOfYear/dayOfMonth |
178 | * --03 --03 monthOfYear |
179 | * ---03 ---03 dayOfMonth |
180 | * 2005-084 2005084 year/dayOfYear |
181 | * -084 -084 dayOfYear |
182 | * 2005-W12-5 2005W125 weekyear/weekOfWeekyear/dayOfWeek |
183 | * 2005-W-5 2005W-5 weekyear/dayOfWeek * |
184 | * 2005-W12 2005W12 weekyear/weekOfWeekyear |
185 | * -W12-5 -W125 weekOfWeekyear/dayOfWeek |
186 | * -W12 -W12 weekOfWeekyear |
187 | * -W-5 -W-5 dayOfWeek |
188 | * 10:20:30.040 102030.040 hour/minute/second/milli |
189 | * 10:20:30 102030 hour/minute/second |
190 | * 10:20 1020 hour/minute |
191 | * 10 10 hour |
192 | * -20:30.040 -2030.040 minute/second/milli |
193 | * -20:30 -2030 minute/second |
194 | * -20 -20 minute |
195 | * --30.040 --30.040 second/milli |
196 | * --30 --30 second |
197 | * ---.040 ---.040 milli * |
198 | * 10-30.040 10-30.040 hour/second/milli * |
199 | * 10:20-.040 1020-.040 hour/minute/milli * |
200 | * 10-30 10-30 hour/second * |
201 | * 10--.040 10--.040 hour/milli * |
202 | * -20-.040 -20-.040 minute/milli * |
203 | * plus datetime formats like {date}T{time} |
204 | * </pre> |
205 | * * indiates that this is not an official ISO format and can be excluded |
206 | * by passing in <code>strictISO</code> as <code>true</code>. |
207 | * <p> |
208 | * This method can side effect the input collection of fields. |
209 | * If the input collection is modifiable, then each field that was added to |
210 | * the formatter will be removed from the collection, including any duplicates. |
211 | * If the input collection is unmodifiable then no side effect occurs. |
212 | * <p> |
213 | * This side effect processing is useful if you need to know whether all |
214 | * the fields were converted into the formatter or not. To achieve this, |
215 | * pass in a modifiable list, and check that it is empty on exit. |
216 | * |
217 | * @param fields the fields to get a formatter for, not null, |
218 | * updated by the method call unless unmodifiable, |
219 | * removing those fields built in the formatter |
220 | * @param extended true to use the extended format (with separators) |
221 | * @param strictISO true to stick exactly to ISO8601, false to include additional formats |
222 | * @return a suitable formatter |
223 | * @throws IllegalArgumentException if there is no format for the fields |
224 | * @since 1.1 |
225 | */ |
226 | public static DateTimeFormatter forFields( |
227 | Collection fields, |
228 | boolean extended, |
229 | boolean strictISO) { |
230 | |
231 | if (fields == null || fields.size() == 0) { |
232 | throw new IllegalArgumentException("The fields must not be null or empty"); |
233 | } |
234 | Set workingFields = new HashSet(fields); |
235 | int inputSize = workingFields.size(); |
236 | boolean reducedPrec = false; |
237 | DateTimeFormatterBuilder bld = new DateTimeFormatterBuilder(); |
238 | // date |
239 | if (workingFields.contains(DateTimeFieldType.monthOfYear())) { |
240 | reducedPrec = dateByMonth(bld, workingFields, extended, strictISO); |
241 | } else if (workingFields.contains(DateTimeFieldType.dayOfYear())) { |
242 | reducedPrec = dateByOrdinal(bld, workingFields, extended, strictISO); |
243 | } else if (workingFields.contains(DateTimeFieldType.weekOfWeekyear())) { |
244 | reducedPrec = dateByWeek(bld, workingFields, extended, strictISO); |
245 | } else if (workingFields.contains(DateTimeFieldType.dayOfMonth())) { |
246 | reducedPrec = dateByMonth(bld, workingFields, extended, strictISO); |
247 | } else if (workingFields.contains(DateTimeFieldType.dayOfWeek())) { |
248 | reducedPrec = dateByWeek(bld, workingFields, extended, strictISO); |
249 | } else if (workingFields.remove(DateTimeFieldType.year())) { |
250 | bld.append(yearElement()); |
251 | reducedPrec = true; |
252 | } else if (workingFields.remove(DateTimeFieldType.weekyear())) { |
253 | bld.append(weekyearElement()); |
254 | reducedPrec = true; |
255 | } |
256 | boolean datePresent = (workingFields.size() < inputSize); |
257 | |
258 | // time |
259 | time(bld, workingFields, extended, strictISO, reducedPrec, datePresent); |
260 | |
261 | // result |
262 | if (bld.canBuildFormatter() == false) { |
263 | throw new IllegalArgumentException("No valid format for fields: " + fields); |
264 | } |
265 | |
266 | // side effect the input collection to indicate the processed fields |
267 | // handling unmodifiable collections with no side effect |
268 | try { |
269 | fields.retainAll(workingFields); |
270 | } catch (UnsupportedOperationException ex) { |
271 | // ignore, so we can handle unmodifiable collections |
272 | } |
273 | return bld.toFormatter(); |
274 | } |
275 | |
276 | //----------------------------------------------------------------------- |
277 | /** |
278 | * Creates a date using the calendar date format. |
279 | * Specification reference: 5.2.1. |
280 | * |
281 | * @param bld the builder |
282 | * @param fields the fields |
283 | * @param extended true to use extended format |
284 | * @param strictISO true to only allow ISO formats |
285 | * @return true if reduced precision |
286 | * @since 1.1 |
287 | */ |
288 | private static boolean dateByMonth( |
289 | DateTimeFormatterBuilder bld, |
290 | Collection fields, |
291 | boolean extended, |
292 | boolean strictISO) { |
293 | |
294 | boolean reducedPrec = false; |
295 | if (fields.remove(DateTimeFieldType.year())) { |
296 | bld.append(yearElement()); |
297 | if (fields.remove(DateTimeFieldType.monthOfYear())) { |
298 | if (fields.remove(DateTimeFieldType.dayOfMonth())) { |
299 | // YYYY-MM-DD/YYYYMMDD |
300 | appendSeparator(bld, extended); |
301 | bld.appendMonthOfYear(2); |
302 | appendSeparator(bld, extended); |
303 | bld.appendDayOfMonth(2); |
304 | } else { |
305 | // YYYY-MM/YYYY-MM |
306 | bld.appendLiteral('-'); |
307 | bld.appendMonthOfYear(2); |
308 | reducedPrec = true; |
309 | } |
310 | } else { |
311 | if (fields.remove(DateTimeFieldType.dayOfMonth())) { |
312 | // YYYY--DD/YYYY--DD (non-iso) |
313 | checkNotStrictISO(fields, strictISO); |
314 | bld.appendLiteral('-'); |
315 | bld.appendLiteral('-'); |
316 | bld.appendDayOfMonth(2); |
317 | } else { |
318 | // YYYY/YYYY |
319 | reducedPrec = true; |
320 | } |
321 | } |
322 | |
323 | } else if (fields.remove(DateTimeFieldType.monthOfYear())) { |
324 | bld.appendLiteral('-'); |
325 | bld.appendLiteral('-'); |
326 | bld.appendMonthOfYear(2); |
327 | if (fields.remove(DateTimeFieldType.dayOfMonth())) { |
328 | // --MM-DD/--MMDD |
329 | appendSeparator(bld, extended); |
330 | bld.appendDayOfMonth(2); |
331 | } else { |
332 | // --MM/--MM |
333 | reducedPrec = true; |
334 | } |
335 | } else if (fields.remove(DateTimeFieldType.dayOfMonth())) { |
336 | // ---DD/---DD |
337 | bld.appendLiteral('-'); |
338 | bld.appendLiteral('-'); |
339 | bld.appendLiteral('-'); |
340 | bld.appendDayOfMonth(2); |
341 | } |
342 | return reducedPrec; |
343 | } |
344 | |
345 | //----------------------------------------------------------------------- |
346 | /** |
347 | * Creates a date using the ordinal date format. |
348 | * Specification reference: 5.2.2. |
349 | * |
350 | * @param bld the builder |
351 | * @param fields the fields |
352 | * @param extended true to use extended format |
353 | * @param strictISO true to only allow ISO formats |
354 | * @since 1.1 |
355 | */ |
356 | private static boolean dateByOrdinal( |
357 | DateTimeFormatterBuilder bld, |
358 | Collection fields, |
359 | boolean extended, |
360 | boolean strictISO) { |
361 | |
362 | boolean reducedPrec = false; |
363 | if (fields.remove(DateTimeFieldType.year())) { |
364 | bld.append(yearElement()); |
365 | if (fields.remove(DateTimeFieldType.dayOfYear())) { |
366 | // YYYY-DDD/YYYYDDD |
367 | appendSeparator(bld, extended); |
368 | bld.appendDayOfYear(3); |
369 | } else { |
370 | // YYYY/YYYY |
371 | reducedPrec = true; |
372 | } |
373 | |
374 | } else if (fields.remove(DateTimeFieldType.dayOfYear())) { |
375 | // -DDD/-DDD |
376 | bld.appendLiteral('-'); |
377 | bld.appendDayOfYear(3); |
378 | } |
379 | return reducedPrec; |
380 | } |
381 | |
382 | //----------------------------------------------------------------------- |
383 | /** |
384 | * Creates a date using the calendar date format. |
385 | * Specification reference: 5.2.3. |
386 | * |
387 | * @param bld the builder |
388 | * @param fields the fields |
389 | * @param extended true to use extended format |
390 | * @param strictISO true to only allow ISO formats |
391 | * @since 1.1 |
392 | */ |
393 | private static boolean dateByWeek( |
394 | DateTimeFormatterBuilder bld, |
395 | Collection fields, |
396 | boolean extended, |
397 | boolean strictISO) { |
398 | |
399 | boolean reducedPrec = false; |
400 | if (fields.remove(DateTimeFieldType.weekyear())) { |
401 | bld.append(weekyearElement()); |
402 | if (fields.remove(DateTimeFieldType.weekOfWeekyear())) { |
403 | appendSeparator(bld, extended); |
404 | bld.appendLiteral('W'); |
405 | bld.appendWeekOfWeekyear(2); |
406 | if (fields.remove(DateTimeFieldType.dayOfWeek())) { |
407 | // YYYY-WWW-D/YYYYWWWD |
408 | appendSeparator(bld, extended); |
409 | bld.appendDayOfWeek(1); |
410 | } else { |
411 | // YYYY-WWW/YYYY-WWW |
412 | reducedPrec = true; |
413 | } |
414 | } else { |
415 | if (fields.remove(DateTimeFieldType.dayOfWeek())) { |
416 | // YYYY-W-D/YYYYW-D (non-iso) |
417 | checkNotStrictISO(fields, strictISO); |
418 | appendSeparator(bld, extended); |
419 | bld.appendLiteral('W'); |
420 | bld.appendLiteral('-'); |
421 | bld.appendDayOfWeek(1); |
422 | } else { |
423 | // YYYY/YYYY |
424 | reducedPrec = true; |
425 | } |
426 | } |
427 | |
428 | } else if (fields.remove(DateTimeFieldType.weekOfWeekyear())) { |
429 | bld.appendLiteral('-'); |
430 | bld.appendLiteral('W'); |
431 | bld.appendWeekOfWeekyear(2); |
432 | if (fields.remove(DateTimeFieldType.dayOfWeek())) { |
433 | // -WWW-D/-WWWD |
434 | appendSeparator(bld, extended); |
435 | bld.appendDayOfWeek(1); |
436 | } else { |
437 | // -WWW/-WWW |
438 | reducedPrec = true; |
439 | } |
440 | } else if (fields.remove(DateTimeFieldType.dayOfWeek())) { |
441 | // -W-D/-W-D |
442 | bld.appendLiteral('-'); |
443 | bld.appendLiteral('W'); |
444 | bld.appendLiteral('-'); |
445 | bld.appendDayOfWeek(1); |
446 | } |
447 | return reducedPrec; |
448 | } |
449 | |
450 | //----------------------------------------------------------------------- |
451 | /** |
452 | * Adds the time fields to the builder. |
453 | * Specification reference: 5.3.1. |
454 | * |
455 | * @param bld the builder |
456 | * @param fields the fields |
457 | * @param extended whether to use the extended format |
458 | * @param strictISO whether to be strict |
459 | * @param reducedPrec whether the date was reduced precision |
460 | * @param datePresent whether there was a date |
461 | * @since 1.1 |
462 | */ |
463 | private static void time( |
464 | DateTimeFormatterBuilder bld, |
465 | Collection fields, |
466 | boolean extended, |
467 | boolean strictISO, |
468 | boolean reducedPrec, |
469 | boolean datePresent) { |
470 | |
471 | boolean hour = fields.remove(DateTimeFieldType.hourOfDay()); |
472 | boolean minute = fields.remove(DateTimeFieldType.minuteOfHour()); |
473 | boolean second = fields.remove(DateTimeFieldType.secondOfMinute()); |
474 | boolean milli = fields.remove(DateTimeFieldType.millisOfSecond()); |
475 | if (!hour && !minute && !second && !milli) { |
476 | return; |
477 | } |
478 | if (hour || minute || second || milli) { |
479 | if (strictISO && reducedPrec) { |
480 | throw new IllegalArgumentException("No valid ISO8601 format for fields because Date was reduced precision: " + fields); |
481 | } |
482 | if (datePresent) { |
483 | bld.appendLiteral('T'); |
484 | } |
485 | } |
486 | if (hour && minute && second || (hour && !second && !milli)) { |
487 | // OK - HMSm/HMS/HM/H - valid in combination with date |
488 | } else { |
489 | if (strictISO && datePresent) { |
490 | throw new IllegalArgumentException("No valid ISO8601 format for fields because Time was truncated: " + fields); |
491 | } |
492 | if (!hour && (minute && second || (minute && !milli) || second)) { |
493 | // OK - MSm/MS/M/Sm/S - valid ISO formats |
494 | } else { |
495 | if (strictISO) { |
496 | throw new IllegalArgumentException("No valid ISO8601 format for fields: " + fields); |
497 | } |
498 | } |
499 | } |
500 | if (hour) { |
501 | bld.appendHourOfDay(2); |
502 | } else if (minute || second || milli) { |
503 | bld.appendLiteral('-'); |
504 | } |
505 | if (extended && hour && minute) { |
506 | bld.appendLiteral(':'); |
507 | } |
508 | if (minute) { |
509 | bld.appendMinuteOfHour(2); |
510 | } else if (second || milli) { |
511 | bld.appendLiteral('-'); |
512 | } |
513 | if (extended && minute && second) { |
514 | bld.appendLiteral(':'); |
515 | } |
516 | if (second) { |
517 | bld.appendSecondOfMinute(2); |
518 | } else if (milli) { |
519 | bld.appendLiteral('-'); |
520 | } |
521 | if (milli) { |
522 | bld.appendLiteral('.'); |
523 | bld.appendMillisOfSecond(3); |
524 | } |
525 | } |
526 | |
527 | //----------------------------------------------------------------------- |
528 | /** |
529 | * Checks that the iso only flag is not set, throwing an exception if it is. |
530 | * |
531 | * @param fields the fields |
532 | * @param strictISO true if only ISO formats allowed |
533 | * @since 1.1 |
534 | */ |
535 | private static void checkNotStrictISO(Collection fields, boolean strictISO) { |
536 | if (strictISO) { |
537 | throw new IllegalArgumentException("No valid ISO8601 format for fields: " + fields); |
538 | } |
539 | } |
540 | |
541 | /** |
542 | * Appends the separator if necessary. |
543 | * |
544 | * @param bld the builder |
545 | * @param extended whether to append the separator |
546 | * @param sep the separator |
547 | * @since 1.1 |
548 | */ |
549 | private static void appendSeparator(DateTimeFormatterBuilder bld, boolean extended) { |
550 | if (extended) { |
551 | bld.appendLiteral('-'); |
552 | } |
553 | } |
554 | |
555 | //----------------------------------------------------------------------- |
556 | /** |
557 | * Returns a generic ISO date parser for parsing dates with a possible zone. |
558 | * It accepts formats described by the following syntax: |
559 | * <pre> |
560 | * date = date-element ['T' offset] |
561 | * date-element = std-date-element | ord-date-element | week-date-element |
562 | * std-date-element = yyyy ['-' MM ['-' dd]] |
563 | * ord-date-element = yyyy ['-' DDD] |
564 | * week-date-element = xxxx '-W' ww ['-' e] |
565 | * offset = 'Z' | (('+' | '-') HH [':' mm [':' ss [('.' | ',') SSS]]]) |
566 | * </pre> |
567 | */ |
568 | public static DateTimeFormatter dateParser() { |
569 | if (dp == null) { |
570 | DateTimeParser tOffset = new DateTimeFormatterBuilder() |
571 | .appendLiteral('T') |
572 | .append(offsetElement()).toParser(); |
573 | dp = new DateTimeFormatterBuilder() |
574 | .append(dateElementParser()) |
575 | .appendOptional(tOffset) |
576 | .toFormatter(); |
577 | } |
578 | return dp; |
579 | } |
580 | |
581 | /** |
582 | * Returns a generic ISO date parser for parsing local dates. |
583 | * This parser is initialised with the local (UTC) time zone. |
584 | * <p> |
585 | * It accepts formats described by the following syntax: |
586 | * <pre> |
587 | * date-element = std-date-element | ord-date-element | week-date-element |
588 | * std-date-element = yyyy ['-' MM ['-' dd]] |
589 | * ord-date-element = yyyy ['-' DDD] |
590 | * week-date-element = xxxx '-W' ww ['-' e] |
591 | * </pre> |
592 | * @since 1.3 |
593 | */ |
594 | public static DateTimeFormatter localDateParser() { |
595 | if (ldp == null) { |
596 | ldp = dateElementParser().withZone(DateTimeZone.UTC); |
597 | } |
598 | return ldp; |
599 | } |
600 | |
601 | /** |
602 | * Returns a generic ISO date parser for parsing dates. |
603 | * It accepts formats described by the following syntax: |
604 | * <pre> |
605 | * date-element = std-date-element | ord-date-element | week-date-element |
606 | * std-date-element = yyyy ['-' MM ['-' dd]] |
607 | * ord-date-element = yyyy ['-' DDD] |
608 | * week-date-element = xxxx '-W' ww ['-' e] |
609 | * </pre> |
610 | */ |
611 | public static DateTimeFormatter dateElementParser() { |
612 | if (dpe == null) { |
613 | dpe = new DateTimeFormatterBuilder() |
614 | .append(null, new DateTimeParser[] { |
615 | new DateTimeFormatterBuilder() |
616 | .append(yearElement()) |
617 | .appendOptional |
618 | (new DateTimeFormatterBuilder() |
619 | .append(monthElement()) |
620 | .appendOptional(dayOfMonthElement().getParser()) |
621 | .toParser()) |
622 | .toParser(), |
623 | new DateTimeFormatterBuilder() |
624 | .append(weekyearElement()) |
625 | .append(weekElement()) |
626 | .appendOptional(dayOfWeekElement().getParser()) |
627 | .toParser(), |
628 | new DateTimeFormatterBuilder() |
629 | .append(yearElement()) |
630 | .append(dayOfYearElement()) |
631 | .toParser() |
632 | }) |
633 | .toFormatter(); |
634 | } |
635 | return dpe; |
636 | } |
637 | |
638 | /** |
639 | * Returns a generic ISO time parser for parsing times with a possible zone. |
640 | * It accepts formats described by the following syntax: |
641 | * <pre> |
642 | * time = ['T'] time-element [offset] |
643 | * time-element = HH [minute-element] | [fraction] |
644 | * minute-element = ':' mm [second-element] | [fraction] |
645 | * second-element = ':' ss [fraction] |
646 | * fraction = ('.' | ',') digit+ |
647 | * offset = 'Z' | (('+' | '-') HH [':' mm [':' ss [('.' | ',') SSS]]]) |
648 | * </pre> |
649 | */ |
650 | public static DateTimeFormatter timeParser() { |
651 | if (tp == null) { |
652 | tp = new DateTimeFormatterBuilder() |
653 | .appendOptional(literalTElement().getParser()) |
654 | .append(timeElementParser()) |
655 | .appendOptional(offsetElement().getParser()) |
656 | .toFormatter(); |
657 | } |
658 | return tp; |
659 | } |
660 | |
661 | /** |
662 | * Returns a generic ISO time parser for parsing local times. |
663 | * This parser is initialised with the local (UTC) time zone. |
664 | * <p> |
665 | * It accepts formats described by the following syntax: |
666 | * <pre> |
667 | * time = ['T'] time-element |
668 | * time-element = HH [minute-element] | [fraction] |
669 | * minute-element = ':' mm [second-element] | [fraction] |
670 | * second-element = ':' ss [fraction] |
671 | * fraction = ('.' | ',') digit+ |
672 | * </pre> |
673 | * @since 1.3 |
674 | */ |
675 | public static DateTimeFormatter localTimeParser() { |
676 | if (ltp == null) { |
677 | ltp = new DateTimeFormatterBuilder() |
678 | .appendOptional(literalTElement().getParser()) |
679 | .append(timeElementParser()) |
680 | .toFormatter().withZone(DateTimeZone.UTC); |
681 | } |
682 | return ltp; |
683 | } |
684 | |
685 | /** |
686 | * Returns a generic ISO time parser. It accepts formats described by |
687 | * the following syntax: |
688 | * <pre> |
689 | * time-element = HH [minute-element] | [fraction] |
690 | * minute-element = ':' mm [second-element] | [fraction] |
691 | * second-element = ':' ss [fraction] |
692 | * fraction = ('.' | ',') digit+ |
693 | * </pre> |
694 | */ |
695 | public static DateTimeFormatter timeElementParser() { |
696 | if (tpe == null) { |
697 | // Decimal point can be either '.' or ',' |
698 | DateTimeParser decimalPoint = new DateTimeFormatterBuilder() |
699 | .append(null, new DateTimeParser[] { |
700 | new DateTimeFormatterBuilder() |
701 | .appendLiteral('.') |
702 | .toParser(), |
703 | new DateTimeFormatterBuilder() |
704 | .appendLiteral(',') |
705 | .toParser() |
706 | }) |
707 | .toParser(); |
708 | |
709 | tpe = new DateTimeFormatterBuilder() |
710 | // time-element |
711 | .append(hourElement()) |
712 | .append |
713 | (null, new DateTimeParser[] { |
714 | new DateTimeFormatterBuilder() |
715 | // minute-element |
716 | .append(minuteElement()) |
717 | .append |
718 | (null, new DateTimeParser[] { |
719 | new DateTimeFormatterBuilder() |
720 | // second-element |
721 | .append(secondElement()) |
722 | // second fraction |
723 | .appendOptional(new DateTimeFormatterBuilder() |
724 | .append(decimalPoint) |
725 | .appendFractionOfSecond(1, 9) |
726 | .toParser()) |
727 | .toParser(), |
728 | // minute fraction |
729 | new DateTimeFormatterBuilder() |
730 | .append(decimalPoint) |
731 | .appendFractionOfMinute(1, 9) |
732 | .toParser(), |
733 | null |
734 | }) |
735 | .toParser(), |
736 | // hour fraction |
737 | new DateTimeFormatterBuilder() |
738 | .append(decimalPoint) |
739 | .appendFractionOfHour(1, 9) |
740 | .toParser(), |
741 | null |
742 | }) |
743 | .toFormatter(); |
744 | } |
745 | return tpe; |
746 | } |
747 | |
748 | /** |
749 | * Returns a generic ISO datetime parser which parses either a date or |
750 | * a time or both. It accepts formats described by the following syntax: |
751 | * <pre> |
752 | * datetime = time | date-opt-time |
753 | * time = 'T' time-element [offset] |
754 | * date-opt-time = date-element ['T' [time-element] [offset]] |
755 | * date-element = std-date-element | ord-date-element | week-date-element |
756 | * std-date-element = yyyy ['-' MM ['-' dd]] |
757 | * ord-date-element = yyyy ['-' DDD] |
758 | * week-date-element = xxxx '-W' ww ['-' e] |
759 | * time-element = HH [minute-element] | [fraction] |
760 | * minute-element = ':' mm [second-element] | [fraction] |
761 | * second-element = ':' ss [fraction] |
762 | * fraction = ('.' | ',') digit+ |
763 | * offset = 'Z' | (('+' | '-') HH [':' mm [':' ss [('.' | ',') SSS]]]) |
764 | * </pre> |
765 | */ |
766 | public static DateTimeFormatter dateTimeParser() { |
767 | if (dtp == null) { |
768 | // This is different from the general time parser in that the 'T' |
769 | // is required. |
770 | DateTimeParser time = new DateTimeFormatterBuilder() |
771 | .appendLiteral('T') |
772 | .append(timeElementParser()) |
773 | .appendOptional(offsetElement().getParser()) |
774 | .toParser(); |
775 | dtp = new DateTimeFormatterBuilder() |
776 | .append(null, new DateTimeParser[] {time, dateOptionalTimeParser().getParser()}) |
777 | .toFormatter(); |
778 | } |
779 | return dtp; |
780 | } |
781 | |
782 | /** |
783 | * Returns a generic ISO datetime parser where the date is mandatory and |
784 | * the time is optional. This parser can parse zoned datetimes. |
785 | * It accepts formats described by the following syntax: |
786 | * <pre> |
787 | * date-opt-time = date-element ['T' [time-element] [offset]] |
788 | * date-element = std-date-element | ord-date-element | week-date-element |
789 | * std-date-element = yyyy ['-' MM ['-' dd]] |
790 | * ord-date-element = yyyy ['-' DDD] |
791 | * week-date-element = xxxx '-W' ww ['-' e] |
792 | * time-element = HH [minute-element] | [fraction] |
793 | * minute-element = ':' mm [second-element] | [fraction] |
794 | * second-element = ':' ss [fraction] |
795 | * fraction = ('.' | ',') digit+ |
796 | * </pre> |
797 | * @since 1.3 |
798 | */ |
799 | public static DateTimeFormatter dateOptionalTimeParser() { |
800 | if (dotp == null) { |
801 | DateTimeParser timeOrOffset = new DateTimeFormatterBuilder() |
802 | .appendLiteral('T') |
803 | .appendOptional(timeElementParser().getParser()) |
804 | .appendOptional(offsetElement().getParser()) |
805 | .toParser(); |
806 | dotp = new DateTimeFormatterBuilder() |
807 | .append(dateElementParser()) |
808 | .appendOptional(timeOrOffset) |
809 | .toFormatter(); |
810 | } |
811 | return dotp; |
812 | } |
813 | |
814 | /** |
815 | * Returns a generic ISO datetime parser where the date is mandatory and |
816 | * the time is optional. This parser only parses local datetimes. |
817 | * This parser is initialised with the local (UTC) time zone. |
818 | * <p> |
819 | * It accepts formats described by the following syntax: |
820 | * <pre> |
821 | * datetime = date-element ['T' time-element] |
822 | * date-element = std-date-element | ord-date-element | week-date-element |
823 | * std-date-element = yyyy ['-' MM ['-' dd]] |
824 | * ord-date-element = yyyy ['-' DDD] |
825 | * week-date-element = xxxx '-W' ww ['-' e] |
826 | * time-element = HH [minute-element] | [fraction] |
827 | * minute-element = ':' mm [second-element] | [fraction] |
828 | * second-element = ':' ss [fraction] |
829 | * fraction = ('.' | ',') digit+ |
830 | * </pre> |
831 | * @since 1.3 |
832 | */ |
833 | public static DateTimeFormatter localDateOptionalTimeParser() { |
834 | if (ldotp == null) { |
835 | DateTimeParser time = new DateTimeFormatterBuilder() |
836 | .appendLiteral('T') |
837 | .append(timeElementParser()) |
838 | .toParser(); |
839 | ldotp = new DateTimeFormatterBuilder() |
840 | .append(dateElementParser()) |
841 | .appendOptional(time) |
842 | .toFormatter().withZone(DateTimeZone.UTC); |
843 | } |
844 | return ldotp; |
845 | } |
846 | |
847 | //----------------------------------------------------------------------- |
848 | /** |
849 | * Returns a formatter for a full date as four digit year, two digit month |
850 | * of year, and two digit day of month (yyyy-MM-dd). |
851 | * |
852 | * @return a formatter for yyyy-MM-dd |
853 | */ |
854 | public static DateTimeFormatter date() { |
855 | return yearMonthDay(); |
856 | } |
857 | |
858 | /** |
859 | * Returns a formatter for a two digit hour of day, two digit minute of |
860 | * hour, two digit second of minute, three digit fraction of second, and |
861 | * time zone offset (HH:mm:ss.SSSZZ). |
862 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
863 | * |
864 | * @return a formatter for HH:mm:ss.SSSZZ |
865 | */ |
866 | public static DateTimeFormatter time() { |
867 | if (t == null) { |
868 | t = new DateTimeFormatterBuilder() |
869 | .append(hourMinuteSecondFraction()) |
870 | .append(offsetElement()) |
871 | .toFormatter(); |
872 | } |
873 | return t; |
874 | } |
875 | |
876 | /** |
877 | * Returns a formatter for a two digit hour of day, two digit minute of |
878 | * hour, two digit second of minute, and time zone offset (HH:mm:ssZZ). |
879 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
880 | * |
881 | * @return a formatter for HH:mm:ssZZ |
882 | */ |
883 | public static DateTimeFormatter timeNoMillis() { |
884 | if (tx == null) { |
885 | tx = new DateTimeFormatterBuilder() |
886 | .append(hourMinuteSecond()) |
887 | .append(offsetElement()) |
888 | .toFormatter(); |
889 | } |
890 | return tx; |
891 | } |
892 | |
893 | /** |
894 | * Returns a formatter for a two digit hour of day, two digit minute of |
895 | * hour, two digit second of minute, three digit fraction of second, and |
896 | * time zone offset prefixed by 'T' ('T'HH:mm:ss.SSSZZ). |
897 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
898 | * |
899 | * @return a formatter for 'T'HH:mm:ss.SSSZZ |
900 | */ |
901 | public static DateTimeFormatter tTime() { |
902 | if (tt == null) { |
903 | tt = new DateTimeFormatterBuilder() |
904 | .append(literalTElement()) |
905 | .append(time()) |
906 | .toFormatter(); |
907 | } |
908 | return tt; |
909 | } |
910 | |
911 | /** |
912 | * Returns a formatter for a two digit hour of day, two digit minute of |
913 | * hour, two digit second of minute, and time zone offset prefixed |
914 | * by 'T' ('T'HH:mm:ssZZ). |
915 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
916 | * |
917 | * @return a formatter for 'T'HH:mm:ssZZ |
918 | */ |
919 | public static DateTimeFormatter tTimeNoMillis() { |
920 | if (ttx == null) { |
921 | ttx = new DateTimeFormatterBuilder() |
922 | .append(literalTElement()) |
923 | .append(timeNoMillis()) |
924 | .toFormatter(); |
925 | } |
926 | return ttx; |
927 | } |
928 | |
929 | /** |
930 | * Returns a formatter that combines a full date and time, separated by a 'T' |
931 | * (yyyy-MM-dd'T'HH:mm:ss.SSSZZ). |
932 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
933 | * |
934 | * @return a formatter for yyyy-MM-dd'T'HH:mm:ss.SSSZZ |
935 | */ |
936 | public static DateTimeFormatter dateTime() { |
937 | if (dt == null) { |
938 | dt = new DateTimeFormatterBuilder() |
939 | .append(date()) |
940 | .append(tTime()) |
941 | .toFormatter(); |
942 | } |
943 | return dt; |
944 | } |
945 | |
946 | /** |
947 | * Returns a formatter that combines a full date and time without millis, |
948 | * separated by a 'T' (yyyy-MM-dd'T'HH:mm:ssZZ). |
949 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
950 | * |
951 | * @return a formatter for yyyy-MM-dd'T'HH:mm:ssZZ |
952 | */ |
953 | public static DateTimeFormatter dateTimeNoMillis() { |
954 | if (dtx == null) { |
955 | dtx = new DateTimeFormatterBuilder() |
956 | .append(date()) |
957 | .append(tTimeNoMillis()) |
958 | .toFormatter(); |
959 | } |
960 | return dtx; |
961 | } |
962 | |
963 | /** |
964 | * Returns a formatter for a full ordinal date, using a four |
965 | * digit year and three digit dayOfYear (yyyy-DDD). |
966 | * |
967 | * @return a formatter for yyyy-DDD |
968 | * @since 1.1 |
969 | */ |
970 | public static DateTimeFormatter ordinalDate() { |
971 | if (od == null) { |
972 | od = new DateTimeFormatterBuilder() |
973 | .append(yearElement()) |
974 | .append(dayOfYearElement()) |
975 | .toFormatter(); |
976 | } |
977 | return od; |
978 | } |
979 | |
980 | /** |
981 | * Returns a formatter for a full ordinal date and time, using a four |
982 | * digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ss.SSSZZ). |
983 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
984 | * |
985 | * @return a formatter for yyyy-DDD'T'HH:mm:ss.SSSZZ |
986 | * @since 1.1 |
987 | */ |
988 | public static DateTimeFormatter ordinalDateTime() { |
989 | if (odt == null) { |
990 | odt = new DateTimeFormatterBuilder() |
991 | .append(ordinalDate()) |
992 | .append(tTime()) |
993 | .toFormatter(); |
994 | } |
995 | return odt; |
996 | } |
997 | |
998 | /** |
999 | * Returns a formatter for a full ordinal date and time without millis, |
1000 | * using a four digit year and three digit dayOfYear (yyyy-DDD'T'HH:mm:ssZZ). |
1001 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
1002 | * |
1003 | * @return a formatter for yyyy-DDD'T'HH:mm:ssZZ |
1004 | * @since 1.1 |
1005 | */ |
1006 | public static DateTimeFormatter ordinalDateTimeNoMillis() { |
1007 | if (odtx == null) { |
1008 | odtx = new DateTimeFormatterBuilder() |
1009 | .append(ordinalDate()) |
1010 | .append(tTimeNoMillis()) |
1011 | .toFormatter(); |
1012 | } |
1013 | return odtx; |
1014 | } |
1015 | |
1016 | /** |
1017 | * Returns a formatter for a full date as four digit weekyear, two digit |
1018 | * week of weekyear, and one digit day of week (xxxx-'W'ww-e). |
1019 | * |
1020 | * @return a formatter for xxxx-'W'ww-e |
1021 | */ |
1022 | public static DateTimeFormatter weekDate() { |
1023 | return weekyearWeekDay(); |
1024 | } |
1025 | |
1026 | /** |
1027 | * Returns a formatter that combines a full weekyear date and time, |
1028 | * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ). |
1029 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
1030 | * |
1031 | * @return a formatter for xxxx-'W'ww-e'T'HH:mm:ss.SSSZZ |
1032 | */ |
1033 | public static DateTimeFormatter weekDateTime() { |
1034 | if (wdt == null) { |
1035 | wdt = new DateTimeFormatterBuilder() |
1036 | .append(weekDate()) |
1037 | .append(tTime()) |
1038 | .toFormatter(); |
1039 | } |
1040 | return wdt; |
1041 | } |
1042 | |
1043 | /** |
1044 | * Returns a formatter that combines a full weekyear date and time without millis, |
1045 | * separated by a 'T' (xxxx-'W'ww-e'T'HH:mm:ssZZ). |
1046 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HH:mm' for non-zero. |
1047 | * |
1048 | * @return a formatter for xxxx-'W'ww-e'T'HH:mm:ssZZ |
1049 | */ |
1050 | public static DateTimeFormatter weekDateTimeNoMillis() { |
1051 | if (wdtx == null) { |
1052 | wdtx = new DateTimeFormatterBuilder() |
1053 | .append(weekDate()) |
1054 | .append(tTimeNoMillis()) |
1055 | .toFormatter(); |
1056 | } |
1057 | return wdtx; |
1058 | } |
1059 | |
1060 | //----------------------------------------------------------------------- |
1061 | /** |
1062 | * Returns a basic formatter for a full date as four digit year, two digit |
1063 | * month of year, and two digit day of month (yyyyMMdd). |
1064 | * |
1065 | * @return a formatter for yyyyMMdd |
1066 | */ |
1067 | public static DateTimeFormatter basicDate() { |
1068 | if (bd == null) { |
1069 | bd = new DateTimeFormatterBuilder() |
1070 | .appendYear(4, 4) |
1071 | .appendFixedDecimal(DateTimeFieldType.monthOfYear(), 2) |
1072 | .appendFixedDecimal(DateTimeFieldType.dayOfMonth(), 2) |
1073 | .toFormatter(); |
1074 | } |
1075 | return bd; |
1076 | } |
1077 | |
1078 | /** |
1079 | * Returns a basic formatter for a two digit hour of day, two digit minute |
1080 | * of hour, two digit second of minute, three digit millis, and time zone |
1081 | * offset (HHmmss.SSSZ). |
1082 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1083 | * |
1084 | * @return a formatter for HHmmss.SSSZ |
1085 | */ |
1086 | public static DateTimeFormatter basicTime() { |
1087 | if (bt == null) { |
1088 | bt = new DateTimeFormatterBuilder() |
1089 | .appendFixedDecimal(DateTimeFieldType.hourOfDay(), 2) |
1090 | .appendFixedDecimal(DateTimeFieldType.minuteOfHour(), 2) |
1091 | .appendFixedDecimal(DateTimeFieldType.secondOfMinute(), 2) |
1092 | .appendLiteral('.') |
1093 | .appendFractionOfSecond(3, 9) |
1094 | .appendTimeZoneOffset("Z", false, 2, 2) |
1095 | .toFormatter(); |
1096 | } |
1097 | return bt; |
1098 | } |
1099 | |
1100 | /** |
1101 | * Returns a basic formatter for a two digit hour of day, two digit minute |
1102 | * of hour, two digit second of minute, and time zone offset (HHmmssZ). |
1103 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1104 | * |
1105 | * @return a formatter for HHmmssZ |
1106 | */ |
1107 | public static DateTimeFormatter basicTimeNoMillis() { |
1108 | if (btx == null) { |
1109 | btx = new DateTimeFormatterBuilder() |
1110 | .appendFixedDecimal(DateTimeFieldType.hourOfDay(), 2) |
1111 | .appendFixedDecimal(DateTimeFieldType.minuteOfHour(), 2) |
1112 | .appendFixedDecimal(DateTimeFieldType.secondOfMinute(), 2) |
1113 | .appendTimeZoneOffset("Z", false, 2, 2) |
1114 | .toFormatter(); |
1115 | } |
1116 | return btx; |
1117 | } |
1118 | |
1119 | /** |
1120 | * Returns a basic formatter for a two digit hour of day, two digit minute |
1121 | * of hour, two digit second of minute, three digit millis, and time zone |
1122 | * offset prefixed by 'T' ('T'HHmmss.SSSZ). |
1123 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1124 | * |
1125 | * @return a formatter for 'T'HHmmss.SSSZ |
1126 | */ |
1127 | public static DateTimeFormatter basicTTime() { |
1128 | if (btt == null) { |
1129 | btt = new DateTimeFormatterBuilder() |
1130 | .append(literalTElement()) |
1131 | .append(basicTime()) |
1132 | .toFormatter(); |
1133 | } |
1134 | return btt; |
1135 | } |
1136 | |
1137 | /** |
1138 | * Returns a basic formatter for a two digit hour of day, two digit minute |
1139 | * of hour, two digit second of minute, and time zone offset prefixed by 'T' |
1140 | * ('T'HHmmssZ). |
1141 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1142 | * |
1143 | * @return a formatter for 'T'HHmmssZ |
1144 | */ |
1145 | public static DateTimeFormatter basicTTimeNoMillis() { |
1146 | if (bttx == null) { |
1147 | bttx = new DateTimeFormatterBuilder() |
1148 | .append(literalTElement()) |
1149 | .append(basicTimeNoMillis()) |
1150 | .toFormatter(); |
1151 | } |
1152 | return bttx; |
1153 | } |
1154 | |
1155 | /** |
1156 | * Returns a basic formatter that combines a basic date and time, separated |
1157 | * by a 'T' (yyyyMMdd'T'HHmmss.SSSZ). |
1158 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1159 | * |
1160 | * @return a formatter for yyyyMMdd'T'HHmmss.SSSZ |
1161 | */ |
1162 | public static DateTimeFormatter basicDateTime() { |
1163 | if (bdt == null) { |
1164 | bdt = new DateTimeFormatterBuilder() |
1165 | .append(basicDate()) |
1166 | .append(basicTTime()) |
1167 | .toFormatter(); |
1168 | } |
1169 | return bdt; |
1170 | } |
1171 | |
1172 | /** |
1173 | * Returns a basic formatter that combines a basic date and time without millis, |
1174 | * separated by a 'T' (yyyyMMdd'T'HHmmssZ). |
1175 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1176 | * |
1177 | * @return a formatter for yyyyMMdd'T'HHmmssZ |
1178 | */ |
1179 | public static DateTimeFormatter basicDateTimeNoMillis() { |
1180 | if (bdtx == null) { |
1181 | bdtx = new DateTimeFormatterBuilder() |
1182 | .append(basicDate()) |
1183 | .append(basicTTimeNoMillis()) |
1184 | .toFormatter(); |
1185 | } |
1186 | return bdtx; |
1187 | } |
1188 | |
1189 | /** |
1190 | * Returns a formatter for a full ordinal date, using a four |
1191 | * digit year and three digit dayOfYear (yyyyDDD). |
1192 | * |
1193 | * @return a formatter for yyyyDDD |
1194 | * @since 1.1 |
1195 | */ |
1196 | public static DateTimeFormatter basicOrdinalDate() { |
1197 | if (bod == null) { |
1198 | bod = new DateTimeFormatterBuilder() |
1199 | .appendYear(4, 4) |
1200 | .appendFixedDecimal(DateTimeFieldType.dayOfYear(), 3) |
1201 | .toFormatter(); |
1202 | } |
1203 | return bod; |
1204 | } |
1205 | |
1206 | /** |
1207 | * Returns a formatter for a full ordinal date and time, using a four |
1208 | * digit year and three digit dayOfYear (yyyyDDD'T'HHmmss.SSSZ). |
1209 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1210 | * |
1211 | * @return a formatter for yyyyDDD'T'HHmmss.SSSZ |
1212 | * @since 1.1 |
1213 | */ |
1214 | public static DateTimeFormatter basicOrdinalDateTime() { |
1215 | if (bodt == null) { |
1216 | bodt = new DateTimeFormatterBuilder() |
1217 | .append(basicOrdinalDate()) |
1218 | .append(basicTTime()) |
1219 | .toFormatter(); |
1220 | } |
1221 | return bodt; |
1222 | } |
1223 | |
1224 | /** |
1225 | * Returns a formatter for a full ordinal date and time without millis, |
1226 | * using a four digit year and three digit dayOfYear (yyyyDDD'T'HHmmssZ). |
1227 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1228 | * |
1229 | * @return a formatter for yyyyDDD'T'HHmmssZ |
1230 | * @since 1.1 |
1231 | */ |
1232 | public static DateTimeFormatter basicOrdinalDateTimeNoMillis() { |
1233 | if (bodtx == null) { |
1234 | bodtx = new DateTimeFormatterBuilder() |
1235 | .append(basicOrdinalDate()) |
1236 | .append(basicTTimeNoMillis()) |
1237 | .toFormatter(); |
1238 | } |
1239 | return bodtx; |
1240 | } |
1241 | |
1242 | /** |
1243 | * Returns a basic formatter for a full date as four digit weekyear, two |
1244 | * digit week of weekyear, and one digit day of week (xxxx'W'wwe). |
1245 | * |
1246 | * @return a formatter for xxxx'W'wwe |
1247 | */ |
1248 | public static DateTimeFormatter basicWeekDate() { |
1249 | if (bwd == null) { |
1250 | bwd = new DateTimeFormatterBuilder() |
1251 | .appendWeekyear(4, 4) |
1252 | .appendLiteral('W') |
1253 | .appendFixedDecimal(DateTimeFieldType.weekOfWeekyear(), 2) |
1254 | .appendFixedDecimal(DateTimeFieldType.dayOfWeek(), 1) |
1255 | .toFormatter(); |
1256 | } |
1257 | return bwd; |
1258 | } |
1259 | |
1260 | /** |
1261 | * Returns a basic formatter that combines a basic weekyear date and time, |
1262 | * separated by a 'T' (xxxx'W'wwe'T'HHmmss.SSSZ). |
1263 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1264 | * |
1265 | * @return a formatter for xxxx'W'wwe'T'HHmmss.SSSZ |
1266 | */ |
1267 | public static DateTimeFormatter basicWeekDateTime() { |
1268 | if (bwdt == null) { |
1269 | bwdt = new DateTimeFormatterBuilder() |
1270 | .append(basicWeekDate()) |
1271 | .append(basicTTime()) |
1272 | .toFormatter(); |
1273 | } |
1274 | return bwdt; |
1275 | } |
1276 | |
1277 | /** |
1278 | * Returns a basic formatter that combines a basic weekyear date and time |
1279 | * without millis, separated by a 'T' (xxxx'W'wwe'T'HHmmssZ). |
1280 | * The time zone offset is 'Z' for zero, and of the form '\u00b1HHmm' for non-zero. |
1281 | * |
1282 | * @return a formatter for xxxx'W'wwe'T'HHmmssZ |
1283 | */ |
1284 | public static DateTimeFormatter basicWeekDateTimeNoMillis() { |
1285 | if (bwdtx == null) { |
1286 | bwdtx = new DateTimeFormatterBuilder() |
1287 | .append(basicWeekDate()) |
1288 | .append(basicTTimeNoMillis()) |
1289 | .toFormatter(); |
1290 | } |
1291 | return bwdtx; |
1292 | } |
1293 | |
1294 | //----------------------------------------------------------------------- |
1295 | /** |
1296 | * Returns a formatter for a four digit year. (yyyy) |
1297 | * |
1298 | * @return a formatter for yyyy |
1299 | */ |
1300 | public static DateTimeFormatter year() { |
1301 | return yearElement(); |
1302 | } |
1303 | |
1304 | /** |
1305 | * Returns a formatter for a four digit year and two digit month of |
1306 | * year. (yyyy-MM) |
1307 | * |
1308 | * @return a formatter for yyyy-MM |
1309 | */ |
1310 | public static DateTimeFormatter yearMonth() { |
1311 | if (ym == null) { |
1312 | ym = new DateTimeFormatterBuilder() |
1313 | .append(yearElement()) |
1314 | .append(monthElement()) |
1315 | .toFormatter(); |
1316 | } |
1317 | return ym; |
1318 | } |
1319 | |
1320 | /** |
1321 | * Returns a formatter for a four digit year, two digit month of year, and |
1322 | * two digit day of month. (yyyy-MM-dd) |
1323 | * |
1324 | * @return a formatter for yyyy-MM-dd |
1325 | */ |
1326 | public static DateTimeFormatter yearMonthDay() { |
1327 | if (ymd == null) { |
1328 | ymd = new DateTimeFormatterBuilder() |
1329 | .append(yearElement()) |
1330 | .append(monthElement()) |
1331 | .append(dayOfMonthElement()) |
1332 | .toFormatter(); |
1333 | } |
1334 | return ymd; |
1335 | } |
1336 | |
1337 | /** |
1338 | * Returns a formatter for a four digit weekyear. (xxxx) |
1339 | * |
1340 | * @return a formatter for xxxx |
1341 | */ |
1342 | public static DateTimeFormatter weekyear() { |
1343 | return weekyearElement(); |
1344 | } |
1345 | |
1346 | /** |
1347 | * Returns a formatter for a four digit weekyear and two digit week of |
1348 | * weekyear. (xxxx-'W'ww) |
1349 | * |
1350 | * @return a formatter for xxxx-'W'ww |
1351 | */ |
1352 | public static DateTimeFormatter weekyearWeek() { |
1353 | if (ww == null) { |
1354 | ww = new DateTimeFormatterBuilder() |
1355 | .append(weekyearElement()) |
1356 | .append(weekElement()) |
1357 | .toFormatter(); |
1358 | } |
1359 | return ww; |
1360 | } |
1361 | |
1362 | /** |
1363 | * Returns a formatter for a four digit weekyear, two digit week of |
1364 | * weekyear, and one digit day of week. (xxxx-'W'ww-e) |
1365 | * |
1366 | * @return a formatter for xxxx-'W'ww-e |
1367 | */ |
1368 | public static DateTimeFormatter weekyearWeekDay() { |
1369 | if (wwd == null) { |
1370 | wwd = new DateTimeFormatterBuilder() |
1371 | .append(weekyearElement()) |
1372 | .append(weekElement()) |
1373 | .append(dayOfWeekElement()) |
1374 | .toFormatter(); |
1375 | } |
1376 | return wwd; |
1377 | } |
1378 | |
1379 | /** |
1380 | * Returns a formatter for a two digit hour of day. (HH) |
1381 | * |
1382 | * @return a formatter for HH |
1383 | */ |
1384 | public static DateTimeFormatter hour() { |
1385 | return hourElement(); |
1386 | } |
1387 | |
1388 | /** |
1389 | * Returns a formatter for a two digit hour of day and two digit minute of |
1390 | * hour. (HH:mm) |
1391 | * |
1392 | * @return a formatter for HH:mm |
1393 | */ |
1394 | public static DateTimeFormatter hourMinute() { |
1395 | if (hm == null) { |
1396 | hm = new DateTimeFormatterBuilder() |
1397 | .append(hourElement()) |
1398 | .append(minuteElement()) |
1399 | .toFormatter(); |
1400 | } |
1401 | return hm; |
1402 | } |
1403 | |
1404 | /** |
1405 | * Returns a formatter for a two digit hour of day, two digit minute of |
1406 | * hour, and two digit second of minute. (HH:mm:ss) |
1407 | * |
1408 | * @return a formatter for HH:mm:ss |
1409 | */ |
1410 | public static DateTimeFormatter hourMinuteSecond() { |
1411 | if (hms == null) { |
1412 | hms = new DateTimeFormatterBuilder() |
1413 | .append(hourElement()) |
1414 | .append(minuteElement()) |
1415 | .append(secondElement()) |
1416 | .toFormatter(); |
1417 | } |
1418 | return hms; |
1419 | } |
1420 | |
1421 | /** |
1422 | * Returns a formatter for a two digit hour of day, two digit minute of |
1423 | * hour, two digit second of minute, and three digit fraction of |
1424 | * second (HH:mm:ss.SSS). Parsing will parse up to 3 fractional second |
1425 | * digits. |
1426 | * |
1427 | * @return a formatter for HH:mm:ss.SSS |
1428 | */ |
1429 | public static DateTimeFormatter hourMinuteSecondMillis() { |
1430 | if (hmsl == null) { |
1431 | hmsl = new DateTimeFormatterBuilder() |
1432 | .append(hourElement()) |
1433 | .append(minuteElement()) |
1434 | .append(secondElement()) |
1435 | .appendLiteral('.') |
1436 | .appendFractionOfSecond(3, 3) |
1437 | .toFormatter(); |
1438 | } |
1439 | return hmsl; |
1440 | } |
1441 | |
1442 | /** |
1443 | * Returns a formatter for a two digit hour of day, two digit minute of |
1444 | * hour, two digit second of minute, and three digit fraction of |
1445 | * second (HH:mm:ss.SSS). Parsing will parse up to 9 fractional second |
1446 | * digits, throwing away all except the first three. |
1447 | * |
1448 | * @return a formatter for HH:mm:ss.SSS |
1449 | */ |
1450 | public static DateTimeFormatter hourMinuteSecondFraction() { |
1451 | if (hmsf == null) { |
1452 | hmsf = new DateTimeFormatterBuilder() |
1453 | .append(hourElement()) |
1454 | .append(minuteElement()) |
1455 | .append(secondElement()) |
1456 | .append(fractionElement()) |
1457 | .toFormatter(); |
1458 | } |
1459 | return hmsf; |
1460 | } |
1461 | |
1462 | /** |
1463 | * Returns a formatter that combines a full date and two digit hour of |
1464 | * day. (yyyy-MM-dd'T'HH) |
1465 | * |
1466 | * @return a formatter for yyyy-MM-dd'T'HH |
1467 | */ |
1468 | public static DateTimeFormatter dateHour() { |
1469 | if (dh == null) { |
1470 | dh = new DateTimeFormatterBuilder() |
1471 | .append(date()) |
1472 | .append(literalTElement()) |
1473 | .append(hour()) |
1474 | .toFormatter(); |
1475 | } |
1476 | return dh; |
1477 | } |
1478 | |
1479 | /** |
1480 | * Returns a formatter that combines a full date, two digit hour of day, |
1481 | * and two digit minute of hour. (yyyy-MM-dd'T'HH:mm) |
1482 | * |
1483 | * @return a formatter for yyyy-MM-dd'T'HH:mm |
1484 | */ |
1485 | public static DateTimeFormatter dateHourMinute() { |
1486 | if (dhm == null) { |
1487 | dhm = new DateTimeFormatterBuilder() |
1488 | .append(date()) |
1489 | .append(literalTElement()) |
1490 | .append(hourMinute()) |
1491 | .toFormatter(); |
1492 | } |
1493 | return dhm; |
1494 | } |
1495 | |
1496 | /** |
1497 | * Returns a formatter that combines a full date, two digit hour of day, |
1498 | * two digit minute of hour, and two digit second of |
1499 | * minute. (yyyy-MM-dd'T'HH:mm:ss) |
1500 | * |
1501 | * @return a formatter for yyyy-MM-dd'T'HH:mm:ss |
1502 | */ |
1503 | public static DateTimeFormatter dateHourMinuteSecond() { |
1504 | if (dhms == null) { |
1505 | dhms = new DateTimeFormatterBuilder() |
1506 | .append(date()) |
1507 | .append(literalTElement()) |
1508 | .append(hourMinuteSecond()) |
1509 | .toFormatter(); |
1510 | } |
1511 | return dhms; |
1512 | } |
1513 | |
1514 | /** |
1515 | * Returns a formatter that combines a full date, two digit hour of day, |
1516 | * two digit minute of hour, two digit second of minute, and three digit |
1517 | * fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). Parsing will parse up |
1518 | * to 3 fractional second digits. |
1519 | * |
1520 | * @return a formatter for yyyy-MM-dd'T'HH:mm:ss.SSS |
1521 | */ |
1522 | public static DateTimeFormatter dateHourMinuteSecondMillis() { |
1523 | if (dhmsl == null) { |
1524 | dhmsl = new DateTimeFormatterBuilder() |
1525 | .append(date()) |
1526 | .append(literalTElement()) |
1527 | .append(hourMinuteSecondMillis()) |
1528 | .toFormatter(); |
1529 | } |
1530 | return dhmsl; |
1531 | } |
1532 | |
1533 | /** |
1534 | * Returns a formatter that combines a full date, two digit hour of day, |
1535 | * two digit minute of hour, two digit second of minute, and three digit |
1536 | * fraction of second (yyyy-MM-dd'T'HH:mm:ss.SSS). Parsing will parse up |
1537 | * to 9 fractional second digits, throwing away all except the first three. |
1538 | * |
1539 | * @return a formatter for yyyy-MM-dd'T'HH:mm:ss.SSS |
1540 | */ |
1541 | public static DateTimeFormatter dateHourMinuteSecondFraction() { |
1542 | if (dhmsf == null) { |
1543 | dhmsf = new DateTimeFormatterBuilder() |
1544 | .append(date()) |
1545 | .append(literalTElement()) |
1546 | .append(hourMinuteSecondFraction()) |
1547 | .toFormatter(); |
1548 | } |
1549 | return dhmsf; |
1550 | } |
1551 | |
1552 | //----------------------------------------------------------------------- |
1553 | private static DateTimeFormatter yearElement() { |
1554 | if (ye == null) { |
1555 | ye = new DateTimeFormatterBuilder() |
1556 | .appendYear(4, 9) |
1557 | .toFormatter(); |
1558 | } |
1559 | return ye; |
1560 | } |
1561 | |
1562 | private static DateTimeFormatter monthElement() { |
1563 | if (mye == null) { |
1564 | mye = new DateTimeFormatterBuilder() |
1565 | .appendLiteral('-') |
1566 | .appendMonthOfYear(2) |
1567 | .toFormatter(); |
1568 | } |
1569 | return mye; |
1570 | } |
1571 | |
1572 | private static DateTimeFormatter dayOfMonthElement() { |
1573 | if (dme == null) { |
1574 | dme = new DateTimeFormatterBuilder() |
1575 | .appendLiteral('-') |
1576 | .appendDayOfMonth(2) |
1577 | .toFormatter(); |
1578 | } |
1579 | return dme; |
1580 | } |
1581 | |
1582 | private static DateTimeFormatter weekyearElement() { |
1583 | if (we == null) { |
1584 | we = new DateTimeFormatterBuilder() |
1585 | .appendWeekyear(4, 9) |
1586 | .toFormatter(); |
1587 | } |
1588 | return we; |
1589 | } |
1590 | |
1591 | private static DateTimeFormatter weekElement() { |
1592 | if (wwe == null) { |
1593 | wwe = new DateTimeFormatterBuilder() |
1594 | .appendLiteral("-W") |
1595 | .appendWeekOfWeekyear(2) |
1596 | .toFormatter(); |
1597 | } |
1598 | return wwe; |
1599 | } |
1600 | |
1601 | private static DateTimeFormatter dayOfWeekElement() { |
1602 | if (dwe == null) { |
1603 | dwe = new DateTimeFormatterBuilder() |
1604 | .appendLiteral('-') |
1605 | .appendDayOfWeek(1) |
1606 | .toFormatter(); |
1607 | } |
1608 | return dwe; |
1609 | } |
1610 | |
1611 | private static DateTimeFormatter dayOfYearElement() { |
1612 | if (dye == null) { |
1613 | dye = new DateTimeFormatterBuilder() |
1614 | .appendLiteral('-') |
1615 | .appendDayOfYear(3) |
1616 | .toFormatter(); |
1617 | } |
1618 | return dye; |
1619 | } |
1620 | |
1621 | private static DateTimeFormatter literalTElement() { |
1622 | if (lte == null) { |
1623 | lte = new DateTimeFormatterBuilder() |
1624 | .appendLiteral('T') |
1625 | .toFormatter(); |
1626 | } |
1627 | return lte; |
1628 | } |
1629 | |
1630 | private static DateTimeFormatter hourElement() { |
1631 | if (hde == null) { |
1632 | hde = new DateTimeFormatterBuilder() |
1633 | .appendHourOfDay(2) |
1634 | .toFormatter(); |
1635 | } |
1636 | return hde; |
1637 | } |
1638 | |
1639 | private static DateTimeFormatter minuteElement() { |
1640 | if (mhe == null) { |
1641 | mhe = new DateTimeFormatterBuilder() |
1642 | .appendLiteral(':') |
1643 | .appendMinuteOfHour(2) |
1644 | .toFormatter(); |
1645 | } |
1646 | return mhe; |
1647 | } |
1648 | |
1649 | private static DateTimeFormatter secondElement() { |
1650 | if (sme == null) { |
1651 | sme = new DateTimeFormatterBuilder() |
1652 | .appendLiteral(':') |
1653 | .appendSecondOfMinute(2) |
1654 | .toFormatter(); |
1655 | } |
1656 | return sme; |
1657 | } |
1658 | |
1659 | private static DateTimeFormatter fractionElement() { |
1660 | if (fse == null) { |
1661 | fse = new DateTimeFormatterBuilder() |
1662 | .appendLiteral('.') |
1663 | // Support parsing up to nanosecond precision even though |
1664 | // those extra digits will be dropped. |
1665 | .appendFractionOfSecond(3, 9) |
1666 | .toFormatter(); |
1667 | } |
1668 | return fse; |
1669 | } |
1670 | |
1671 | private static DateTimeFormatter offsetElement() { |
1672 | if (ze == null) { |
1673 | ze = new DateTimeFormatterBuilder() |
1674 | .appendTimeZoneOffset("Z", true, 2, 4) |
1675 | .toFormatter(); |
1676 | } |
1677 | return ze; |
1678 | } |
1679 | |
1680 | } |