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 }