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