001 /*
002 * Copyright 2001-2006,2008 Stephen Colebourne
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.joda.time.format;
017
018 import java.util.Collection;
019 import java.util.HashSet;
020 import java.util.Set;
021
022 import org.joda.time.DateTimeFieldType;
023 import org.joda.time.DateTimeZone;
024
025 /**
026 * Factory that creates instances of DateTimeFormatter for the ISO8601 standard.
027 * <p>
028 * Datetime formatting is performed by the {@link DateTimeFormatter} class.
029 * Three classes provide factory methods to create formatters, and this is one.
030 * The others are {@link DateTimeFormat} and {@link DateTimeFormatterBuilder}.
031 * <p>
032 * ISO8601 is the international standard for data interchange. It defines a
033 * framework, rather than an absolute standard. As a result this provider has a
034 * number of methods that represent common uses of the framework. The most common
035 * formats are {@link #date() date}, {@link #time() time}, and {@link #dateTime() dateTime}.
036 * <p>
037 * For example, to format a date time in ISO format:
038 * <pre>
039 * DateTime dt = new DateTime();
040 * DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
041 * String str = fmt.print(dt);
042 * </pre>
043 * <p>
044 * It is important to understand that these formatters are not linked to
045 * the <code>ISOChronology</code>. These formatters may be used with any
046 * chronology, however there may be certain side effects with more unusual
047 * chronologies. For example, the ISO formatters rely on dayOfWeek being
048 * single digit, dayOfMonth being two digit and dayOfYear being three digit.
049 * A chronology with a ten day week would thus cause issues. However, in
050 * general, it is safe to use these formatters with other chronologies.
051 * <p>
052 * ISODateTimeFormat is thread-safe and immutable, and the formatters it
053 * returns are as well.
054 *
055 * @author Brian S O'Neill
056 * @since 1.0
057 * @see DateTimeFormat
058 * @see DateTimeFormatterBuilder
059 */
060 public class ISODateTimeFormat {
061
062 //-----------------------------------------------------------------------
063 private static DateTimeFormatter
064 ye, // year element (yyyy)
065 mye, // monthOfYear element (-MM)
066 dme, // dayOfMonth element (-dd)
067 we, // weekyear element (xxxx)
068 wwe, // weekOfWeekyear element (-ww)
069 dwe, // dayOfWeek element (-ee)
070 dye, // dayOfYear element (-DDD)
071 hde, // hourOfDay element (HH)
072 mhe, // minuteOfHour element (:mm)
073 sme, // secondOfMinute element (:ss)
074 fse, // fractionOfSecond element (.SSSSSSSSS)
075 ze, // zone offset element
076 lte, // literal 'T' element
077
078 //y, // year (same as year element)
079 ym, // year month
080 ymd, // year month day
081
082 //w, // weekyear (same as weekyear element)
083 ww, // weekyear week
084 wwd, // weekyear week day
085
086 //h, // hour (same as hour element)
087 hm, // hour minute
088 hms, // hour minute second
089 hmsl, // hour minute second millis
090 hmsf, // hour minute second fraction
091
092 dh, // date hour
093 dhm, // date hour minute
094 dhms, // date hour minute second
095 dhmsl, // date hour minute second millis
096 dhmsf, // date hour minute second fraction
097
098 //d, // date (same as ymd)
099 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 }