001/* 002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos 003 * 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions are met: 008 * 009 * * Redistributions of source code must retain the above copyright notice, 010 * this list of conditions and the following disclaimer. 011 * 012 * * Redistributions in binary form must reproduce the above copyright notice, 013 * this list of conditions and the following disclaimer in the documentation 014 * and/or other materials provided with the distribution. 015 * 016 * * Neither the name of JSR-310 nor the names of its contributors 017 * may be used to endorse or promote products derived from this software 018 * without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 031 */ 032package org.threeten.bp.format; 033 034import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; 035import static org.threeten.bp.temporal.ChronoField.DAY_OF_WEEK; 036import static org.threeten.bp.temporal.ChronoField.DAY_OF_YEAR; 037import static org.threeten.bp.temporal.ChronoField.HOUR_OF_DAY; 038import static org.threeten.bp.temporal.ChronoField.MINUTE_OF_HOUR; 039import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR; 040import static org.threeten.bp.temporal.ChronoField.NANO_OF_SECOND; 041import static org.threeten.bp.temporal.ChronoField.SECOND_OF_MINUTE; 042import static org.threeten.bp.temporal.ChronoField.YEAR; 043 044import java.util.HashMap; 045import java.util.Locale; 046import java.util.Map; 047import java.util.Objects; 048 049import org.threeten.bp.ZoneId; 050import org.threeten.bp.ZoneOffset; 051import org.threeten.bp.temporal.ChronoField; 052import org.threeten.bp.temporal.ISOFields; 053import org.threeten.bp.temporal.TemporalField; 054 055/** 056 * Provides common implementations of {@code DateTimeFormatter}. 057 * <p> 058 * This utility class provides three different ways to obtain a formatter. 059 * <p><ul> 060 * <li>Using pattern letters, such as {@code yyyy-MMM-dd} 061 * <li>Using localized styles, such as {@code long} or {@code medium} 062 * <li>Using predefined constants, such as {@code isoLocalDate()} 063 * </ul><p> 064 * 065 * <h3>Specification for implementors</h3> 066 * This is a thread-safe utility class. 067 * All returned formatters are immutable and thread-safe. 068 */ 069public final class DateTimeFormatters { 070 071 /** 072 * Private constructor since this is a utility class. 073 */ 074 private DateTimeFormatters() { 075 } 076 077 //----------------------------------------------------------------------- 078 /** 079 * Creates a formatter using the specified pattern. 080 * <p> 081 * This method will create a formatter based on a simple pattern of letters and symbols. 082 * For example, {@code d MMM yyyy} will format 2011-12-03 as '3 Dec 2011'. 083 * <p> 084 * The returned formatter will use the default locale, but this can be changed 085 * using {@link DateTimeFormatter#withLocale(Locale)}. 086 * <p> 087 * All letters 'A' to 'Z' and 'a' to 'z' are reserved as pattern letters. 088 * The following pattern letters are defined: 089 * <pre> 090 * Symbol Meaning Presentation Examples 091 * ------ ------- ------------ ------- 092 * G era number/text 1; 01; AD; Anno Domini 093 * y year year 2004; 04 094 * D day-of-year number 189 095 * M month-of-year number/text 7; 07; Jul; July; J 096 * d day-of-month number 10 097 * 098 * Q quarter-of-year number/text 3; 03; Q3 099 * Y week-based-year year 1996; 96 100 * w week-of-year number 27 101 * W week-of-month number 27 102 * e localized day-of-week number 2; Tue; Tuesday; T 103 * E day-of-week number/text 2; Tue; Tuesday; T 104 * F week-of-month number 3 105 * 106 * a am-pm-of-day text PM 107 * h clock-hour-of-am-pm (1-12) number 12 108 * K hour-of-am-pm (0-11) number 0 109 * k clock-hour-of-am-pm (1-24) number 0 110 * 111 * H hour-of-day (0-23) number 0 112 * m minute-of-hour number 30 113 * s second-of-minute number 55 114 * S fraction-of-second fraction 978 115 * A milli-of-day number 1234 116 * n nano-of-second number 987654321 117 * N nano-of-day number 1234000000 118 * 119 * I time-zone ID zoneId America/Los_Angeles 120 * z time-zone name text Pacific Standard Time; PST 121 * Z zone-offset offset-Z +0000; -0800; -08:00; 122 * X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15; 123 * 124 * p pad next pad modifier 1 125 * 126 * ' escape for text delimiter 127 * '' single quote literal ' 128 * [ optional section start 129 * ] optional section end 130 * {} reserved for future use 131 * </pre> 132 * <p> 133 * The count of pattern letters determine the format. 134 * <p> 135 * <b>Text</b>: The text style is determined based on the number of pattern letters used. 136 * Less than 4 pattern letters will use the {@link TextStyle#SHORT short form}. 137 * Exactly 4 pattern letters will use the {@link TextStyle#FULL full form}. 138 * Exactly 5 pattern letters will use the {@link TextStyle#NARROW narrow form}. 139 * <p> 140 * <b>Number</b>: If the count of letters is one, then the value is printed using the minimum number 141 * of digits and without padding as per {@link DateTimeFormatterBuilder#appendValue(TemporalField)}. 142 * Otherwise, the count of digits is used as the width of the output field as per 143 * {@link DateTimeFormatterBuilder#appendValue(TemporalField, int)}. 144 * <p> 145 * <b>Number/Text</b>: If the count of pattern letters is 3 or greater, use the Text rules above. 146 * Otherwise use the Number rules above. 147 * <p> 148 * <b>Fraction</b>: Outputs the nano-of-second field as a fraction-of-second. 149 * The nano-of-second value has nine digits, thus the count of pattern letters is from 1 to 9. 150 * If it is less than 9, then the nano-of-second value is truncated, with only the most 151 * significant digits being output. 152 * When parsing in strict mode, the number of parsed digits must match the count of pattern letters. 153 * When parsing in lenient mode, the number of parsed digits must be at least the count of pattern 154 * letters, up to 9 digits. 155 * <p> 156 * <b>Year</b>: The count of letters determines the minimum field width below which padding is used. 157 * If the count of letters is two, then a {@link DateTimeFormatterBuilder#appendValueReduced reduced} 158 * two digit form is used. 159 * For printing, this outputs the rightmost two digits. For parsing, this will parse using the 160 * base value of 2000, resulting in a year within the range 2000 to 2099 inclusive. 161 * If the count of letters is less than four (but not two), then the sign is only output for negative 162 * years as per {@link SignStyle#NORMAL}. 163 * Otherwise, the sign is output if the pad width is exceeded, as per {@link SignStyle#EXCEEDS_PAD} 164 * <p> 165 * <b>ZoneId</b>: 'I' outputs the zone ID, such as 'Europe/Paris'. 166 * <p> 167 * <b>Offset X</b>: This formats the offset using 'Z' when the offset is zero. 168 * One letter outputs just the hour', such as '+01' 169 * Two letters outputs the hour and minute, without a colon, such as '+0130'. 170 * Three letters outputs the hour and minute, with a colon, such as '+01:30'. 171 * Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'. 172 * Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'. 173 * <p> 174 * <b>Offset Z</b>: This formats the offset using '+0000' or '+00:00' when the offset is zero. 175 * One or two letters outputs the hour and minute, without a colon, such as '+0130'. 176 * Three letters outputs the hour and minute, with a colon, such as '+01:30'. 177 * <p> 178 * <b>Zone names</b>: Time zone names ('z') cannot be parsed. 179 * <p> 180 * <b>Optional section</b>: The optional section markers work exactly like calling 181 * {@link DateTimeFormatterBuilder#optionalStart()} and {@link DateTimeFormatterBuilder#optionalEnd()}. 182 * <p> 183 * <b>Pad modifier</b>: Modifies the pattern that immediately follows to be padded with spaces. 184 * The pad width is determined by the number of pattern letters. 185 * This is the same as calling {@link DateTimeFormatterBuilder#padNext(int)}. 186 * <p> 187 * For example, 'ppH' outputs the hour-of-day padded on the left with spaces to a width of 2. 188 * <p> 189 * Any unrecognized letter is an error. 190 * Any non-letter character, other than '[', ']', '{', '}' and the single quote will be output directly. 191 * Despite this, it is recommended to use single quotes around all characters that you want to 192 * output directly to ensure that future changes do not break your application. 193 * <p> 194 * The pattern string is similar, but not identical, to {@link java.text.SimpleDateFormat SimpleDateFormat}. 195 * Pattern letters 'E' and 'u' are merged, which changes the meaning of "E" and "EE" to be numeric. 196 * Pattern letters 'Z' and 'X' are extended. 197 * Pattern letter 'y' and 'Y' parse years of two digits and more than 4 digits differently. 198 * Pattern letters 'n', 'A', 'N', 'I' and 'p' are added. 199 * Number types will reject large numbers. 200 * The pattern string is also similar, but not identical, to that defined by the 201 * Unicode Common Locale Data Repository (CLDR). 202 * 203 * @param pattern the pattern to use, not null 204 * @return the formatter based on the pattern, not null 205 * @throws IllegalArgumentException if the pattern is invalid 206 * @see DateTimeFormatterBuilder#appendPattern(String) 207 */ 208 public static DateTimeFormatter pattern(String pattern) { 209 return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(); 210 } 211 212 /** 213 * Creates a formatter using the specified pattern. 214 * <p> 215 * This method will create a formatter based on a simple pattern of letters and symbols. 216 * For example, {@code d MMM yyyy} will format 2011-12-03 as '3 Dec 2011'. 217 * <p> 218 * See {@link #pattern(String)} for details of the pattern. 219 * <p> 220 * The returned formatter will use the specified locale, but this can be changed 221 * using {@link DateTimeFormatter#withLocale(Locale)}. 222 * 223 * @param pattern the pattern to use, not null 224 * @param locale the locale to use, not null 225 * @return the formatter based on the pattern, not null 226 * @throws IllegalArgumentException if the pattern is invalid 227 * @see DateTimeFormatterBuilder#appendPattern(String) 228 */ 229 public static DateTimeFormatter pattern(String pattern, Locale locale) { 230 return new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale); 231 } 232 233 //----------------------------------------------------------------------- 234 /** 235 * Returns a locale specific date format. 236 * <p> 237 * This returns a formatter that will print/parse a date. 238 * The exact format pattern used varies by locale. 239 * <p> 240 * The locale is determined from the formatter. The formatter returned directly by 241 * this method will use the {@link Locale#getDefault() default locale}. 242 * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} 243 * on the result of this method. 244 * <p> 245 * Note that the localized pattern is looked up lazily. 246 * This {@code DateTimeFormatter} holds the style required and the locale, 247 * looking up the pattern required on demand. 248 * 249 * @param dateStyle the formatter style to obtain, not null 250 * @return the date formatter, not null 251 */ 252 public static DateTimeFormatter localizedDate(FormatStyle dateStyle) { 253 Objects.requireNonNull(dateStyle, "dateStyle"); 254 return new DateTimeFormatterBuilder().appendLocalized(dateStyle, null).toFormatter(); 255 } 256 257 /** 258 * Returns a locale specific time format. 259 * <p> 260 * This returns a formatter that will print/parse a time. 261 * The exact format pattern used varies by locale. 262 * <p> 263 * The locale is determined from the formatter. The formatter returned directly by 264 * this method will use the {@link Locale#getDefault() default locale}. 265 * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} 266 * on the result of this method. 267 * <p> 268 * Note that the localized pattern is looked up lazily. 269 * This {@code DateTimeFormatter} holds the style required and the locale, 270 * looking up the pattern required on demand. 271 * 272 * @param timeStyle the formatter style to obtain, not null 273 * @return the time formatter, not null 274 */ 275 public static DateTimeFormatter localizedTime(FormatStyle timeStyle) { 276 Objects.requireNonNull(timeStyle, "timeStyle"); 277 return new DateTimeFormatterBuilder().appendLocalized(null, timeStyle).toFormatter(); 278 } 279 280 /** 281 * Returns a locale specific date-time format, which is typically of short length. 282 * <p> 283 * This returns a formatter that will print/parse a date-time. 284 * The exact format pattern used varies by locale. 285 * <p> 286 * The locale is determined from the formatter. The formatter returned directly by 287 * this method will use the {@link Locale#getDefault() default locale}. 288 * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} 289 * on the result of this method. 290 * <p> 291 * Note that the localized pattern is looked up lazily. 292 * This {@code DateTimeFormatter} holds the style required and the locale, 293 * looking up the pattern required on demand. 294 * 295 * @param dateTimeStyle the formatter style to obtain, not null 296 * @return the date-time formatter, not null 297 */ 298 public static DateTimeFormatter localizedDateTime(FormatStyle dateTimeStyle) { 299 Objects.requireNonNull(dateTimeStyle, "dateTimeStyle"); 300 return new DateTimeFormatterBuilder().appendLocalized(dateTimeStyle, dateTimeStyle).toFormatter(); 301 } 302 303 /** 304 * Returns a locale specific date and time format. 305 * <p> 306 * This returns a formatter that will print/parse a date-time. 307 * The exact format pattern used varies by locale. 308 * <p> 309 * The locale is determined from the formatter. The formatter returned directly by 310 * this method will use the {@link Locale#getDefault() default locale}. 311 * The locale can be controlled using {@link DateTimeFormatter#withLocale(Locale) withLocale(Locale)} 312 * on the result of this method. 313 * <p> 314 * Note that the localized pattern is looked up lazily. 315 * This {@code DateTimeFormatter} holds the style required and the locale, 316 * looking up the pattern required on demand. 317 * 318 * @param dateStyle the date formatter style to obtain, not null 319 * @param timeStyle the time formatter style to obtain, not null 320 * @return the date, time or date-time formatter, not null 321 */ 322 public static DateTimeFormatter localizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle) { 323 Objects.requireNonNull(dateStyle, "dateStyle"); 324 Objects.requireNonNull(timeStyle, "timeStyle"); 325 return new DateTimeFormatterBuilder().appendLocalized(dateStyle, timeStyle).toFormatter(); 326 } 327 328 //----------------------------------------------------------------------- 329 /** 330 * Returns the ISO date formatter that prints/parses a date without an offset, 331 * such as '2011-12-03'. 332 * <p> 333 * This returns an immutable formatter capable of printing and parsing 334 * the ISO-8601 extended local date format. 335 * The format consists of: 336 * <p><ul> 337 * <li>Four digits or more for the {@link ChronoField#YEAR year}. 338 * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. 339 * Years outside that range will have a prefixed positive or negative symbol. 340 * <li>A dash 341 * <li>Two digits for the {@link ChronoField#MONTH_OF_YEAR month-of-year}. 342 * This is pre-padded by zero to ensure two digits. 343 * <li>A dash 344 * <li>Two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}. 345 * This is pre-padded by zero to ensure two digits. 346 * </ul><p> 347 * 348 * @return the ISO local date formatter, not null 349 */ 350 public static DateTimeFormatter isoLocalDate() { 351 return ISO_LOCAL_DATE; 352 } 353 354 /** Singleton date formatter. */ 355 private static final DateTimeFormatter ISO_LOCAL_DATE; 356 static { 357 ISO_LOCAL_DATE = new DateTimeFormatterBuilder() 358 .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) 359 .appendLiteral('-') 360 .appendValue(MONTH_OF_YEAR, 2) 361 .appendLiteral('-') 362 .appendValue(DAY_OF_MONTH, 2) 363 .toFormatter(); 364 } 365 366 //----------------------------------------------------------------------- 367 /** 368 * Returns the ISO date formatter that prints/parses a date with an offset, 369 * such as '2011-12-03+01:00'. 370 * <p> 371 * This returns an immutable formatter capable of printing and parsing 372 * the ISO-8601 extended offset date format. 373 * The format consists of: 374 * <p><ul> 375 * <li>The {@link #isoLocalDate()} 376 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 377 * they will be handled even though this is not part of the ISO-8601 standard. 378 * Parsing is case insensitive. 379 * </ul><p> 380 * 381 * @return the ISO offset date formatter, not null 382 */ 383 public static DateTimeFormatter isoOffsetDate() { 384 return ISO_OFFSET_DATE; 385 } 386 387 /** Singleton date formatter. */ 388 private static final DateTimeFormatter ISO_OFFSET_DATE; 389 static { 390 ISO_OFFSET_DATE = new DateTimeFormatterBuilder() 391 .parseCaseInsensitive() 392 .append(ISO_LOCAL_DATE) 393 .appendOffsetId() 394 .toFormatter(); 395 } 396 397 //----------------------------------------------------------------------- 398 /** 399 * Returns the ISO date formatter that prints/parses a date with the 400 * offset if available, such as '2011-12-03' or '2011-12-03+01:00'. 401 * <p> 402 * This returns an immutable formatter capable of printing and parsing 403 * the ISO-8601 extended date format. 404 * The format consists of: 405 * <p><ul> 406 * <li>The {@link #isoLocalDate()} 407 * <li>If the offset is not available to print/parse then the format is complete. 408 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 409 * they will be handled even though this is not part of the ISO-8601 standard. 410 * Parsing is case insensitive. 411 * </ul><p> 412 * As this formatter has an optional element, it may be necessary to parse using 413 * {@link DateTimeFormatter#parseBest}. 414 * 415 * @return the ISO date formatter, not null 416 */ 417 public static DateTimeFormatter isoDate() { 418 return ISO_DATE; 419 } 420 421 /** Singleton date formatter. */ 422 private static final DateTimeFormatter ISO_DATE; 423 static { 424 ISO_DATE = new DateTimeFormatterBuilder() 425 .parseCaseInsensitive() 426 .append(ISO_LOCAL_DATE) 427 .optionalStart() 428 .appendOffsetId() 429 .toFormatter(); 430 } 431 432 //----------------------------------------------------------------------- 433 /** 434 * Returns the ISO time formatter that prints/parses a time without an offset, 435 * such as '10:15' or '10:15:30'. 436 * <p> 437 * This returns an immutable formatter capable of printing and parsing 438 * the ISO-8601 extended local time format. 439 * The format consists of: 440 * <p><ul> 441 * <li>Two digits for the {@link ChronoField#HOUR_OF_DAY hour-of-day}. 442 * This is pre-padded by zero to ensure two digits. 443 * <li>A colon 444 * <li>Two digits for the {@link ChronoField#MINUTE_OF_HOUR minute-of-hour}. 445 * This is pre-padded by zero to ensure two digits. 446 * <li>If the second-of-minute is not available to print/parse then the format is complete. 447 * <li>A colon 448 * <li>Two digits for the {@link ChronoField#SECOND_OF_MINUTE second-of-minute}. 449 * This is pre-padded by zero to ensure two digits. 450 * <li>If the nano-of-second is zero or not available to print/parse then the format is complete. 451 * <li>A decimal point 452 * <li>One to nine digits for the {@link ChronoField#NANO_OF_SECOND nano-of-second}. 453 * As many digits will be printed as required. 454 * </ul><p> 455 * 456 * @return the ISO local time formatter, not null 457 */ 458 public static DateTimeFormatter isoLocalTime() { 459 return ISO_LOCAL_TIME; 460 } 461 462 /** Singleton date formatter. */ 463 private static final DateTimeFormatter ISO_LOCAL_TIME; 464 static { 465 ISO_LOCAL_TIME = new DateTimeFormatterBuilder() 466 .appendValue(HOUR_OF_DAY, 2) 467 .appendLiteral(':') 468 .appendValue(MINUTE_OF_HOUR, 2) 469 .optionalStart() 470 .appendLiteral(':') 471 .appendValue(SECOND_OF_MINUTE, 2) 472 .optionalStart() 473 .appendFraction(NANO_OF_SECOND, 0, 9, true) 474 .toFormatter(); 475 } 476 477 //----------------------------------------------------------------------- 478 /** 479 * Returns the ISO time formatter that prints/parses a time with an offset, 480 * such as '10:15+01:00' or '10:15:30+01:00'. 481 * <p> 482 * This returns an immutable formatter capable of printing and parsing 483 * the ISO-8601 extended offset time format. 484 * The format consists of: 485 * <p><ul> 486 * <li>The {@link #isoLocalTime()} 487 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 488 * they will be handled even though this is not part of the ISO-8601 standard. 489 * Parsing is case insensitive. 490 * </ul><p> 491 * 492 * @return the ISO offset time formatter, not null 493 */ 494 public static DateTimeFormatter isoOffsetTime() { 495 return ISO_OFFSET_TIME; 496 } 497 498 /** Singleton date formatter. */ 499 private static final DateTimeFormatter ISO_OFFSET_TIME; 500 static { 501 ISO_OFFSET_TIME = new DateTimeFormatterBuilder() 502 .parseCaseInsensitive() 503 .append(ISO_LOCAL_TIME) 504 .appendOffsetId() 505 .toFormatter(); 506 } 507 508 //----------------------------------------------------------------------- 509 /** 510 * Returns the ISO time formatter that prints/parses a time, with the 511 * offset if available, such as '10:15', '10:15:30' or '10:15:30+01:00'. 512 * <p> 513 * This returns an immutable formatter capable of printing and parsing 514 * the ISO-8601 extended offset time format. 515 * The format consists of: 516 * <p><ul> 517 * <li>The {@link #isoLocalTime()} 518 * <li>If the offset is not available to print/parse then the format is complete. 519 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 520 * they will be handled even though this is not part of the ISO-8601 standard. 521 * Parsing is case insensitive. 522 * </ul><p> 523 * As this formatter has an optional element, it may be necessary to parse using 524 * {@link DateTimeFormatter#parseBest}. 525 * 526 * @return the ISO time formatter, not null 527 */ 528 public static DateTimeFormatter isoTime() { 529 return ISO_TIME; 530 } 531 532 /** Singleton date formatter. */ 533 private static final DateTimeFormatter ISO_TIME; 534 static { 535 ISO_TIME = new DateTimeFormatterBuilder() 536 .parseCaseInsensitive() 537 .append(ISO_LOCAL_TIME) 538 .optionalStart() 539 .appendOffsetId() 540 .toFormatter(); 541 } 542 543 //----------------------------------------------------------------------- 544 /** 545 * Returns the ISO date formatter that prints/parses a date-time 546 * without an offset, such as '2011-12-03T10:15:30'. 547 * <p> 548 * This returns an immutable formatter capable of printing and parsing 549 * the ISO-8601 extended offset date-time format. 550 * The format consists of: 551 * <p><ul> 552 * <li>The {@link #isoLocalDate()} 553 * <li>The letter 'T'. Parsing is case insensitive. 554 * <li>The {@link #isoLocalTime()} 555 * </ul><p> 556 * 557 * @return the ISO local date-time formatter, not null 558 */ 559 public static DateTimeFormatter isoLocalDateTime() { 560 return ISO_LOCAL_DATE_TIME; 561 } 562 563 /** Singleton date formatter. */ 564 private static final DateTimeFormatter ISO_LOCAL_DATE_TIME; 565 static { 566 ISO_LOCAL_DATE_TIME = new DateTimeFormatterBuilder() 567 .parseCaseInsensitive() 568 .append(ISO_LOCAL_DATE) 569 .appendLiteral('T') 570 .append(ISO_LOCAL_TIME) 571 .toFormatter(); 572 } 573 574 //----------------------------------------------------------------------- 575 /** 576 * Returns the ISO date formatter that prints/parses a date-time 577 * with an offset, such as '2011-12-03T10:15:30+01:00'. 578 * <p> 579 * This returns an immutable formatter capable of printing and parsing 580 * the ISO-8601 extended offset date-time format. 581 * The format consists of: 582 * <p><ul> 583 * <li>The {@link #isoLocalDateTime()} 584 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 585 * they will be handled even though this is not part of the ISO-8601 standard. 586 * Parsing is case insensitive. 587 * </ul><p> 588 * 589 * @return the ISO offset date-time formatter, not null 590 */ 591 public static DateTimeFormatter isoOffsetDateTime() { 592 return ISO_OFFSET_DATE_TIME; 593 } 594 595 /** Singleton date formatter. */ 596 private static final DateTimeFormatter ISO_OFFSET_DATE_TIME; 597 static { 598 ISO_OFFSET_DATE_TIME = new DateTimeFormatterBuilder() 599 .parseCaseInsensitive() 600 .append(ISO_LOCAL_DATE_TIME) 601 .appendOffsetId() 602 .toFormatter(); 603 } 604 605 //----------------------------------------------------------------------- 606 /** 607 * Returns the ISO date formatter that prints/parses a date-time with 608 * offset and zone, such as '2011-12-03T10:15:30+01:00[Europe/Paris]'. 609 * <p> 610 * This returns an immutable formatter capable of printing and parsing 611 * a format that extends the ISO-8601 extended offset date-time format 612 * to add the time-zone. 613 * The format consists of: 614 * <p><ul> 615 * <li>The {@link #isoOffsetDateTime()} 616 * <li>If the zone ID is not available or is a {@code ZoneOffset} then the format is complete. 617 * <li>An open square bracket '['. 618 * <li>The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard. 619 * Parsing is case sensitive. 620 * <li>A close square bracket ']'. 621 * </ul><p> 622 * 623 * @return the ISO zoned date-time formatter, not null 624 */ 625 public static DateTimeFormatter isoZonedDateTime() { 626 return ISO_ZONED_DATE_TIME; 627 } 628 629 /** Singleton date formatter. */ 630 private static final DateTimeFormatter ISO_ZONED_DATE_TIME; 631 static { 632 ISO_ZONED_DATE_TIME = new DateTimeFormatterBuilder() 633 .append(ISO_OFFSET_DATE_TIME) 634 .optionalStart() 635 .appendLiteral('[') 636 .parseCaseSensitive() 637 .appendZoneRegionId() 638 .appendLiteral(']') 639 .toFormatter(); 640 } 641 642 //----------------------------------------------------------------------- 643 /** 644 * Returns the ISO date formatter that prints/parses a date-time 645 * with the offset and zone if available, such as '2011-12-03T10:15:30', 646 * '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'. 647 * <p> 648 * This returns an immutable formatter capable of printing and parsing 649 * the ISO-8601 extended offset date-time format. 650 * The format consists of: 651 * <p><ul> 652 * <li>The {@link #isoLocalDateTime()} 653 * <li>If the offset is not available to print/parse then the format is complete. 654 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 655 * they will be handled even though this is not part of the ISO-8601 standard. 656 * <li>If the zone ID is not available or is a {@code ZoneOffset} then the format is complete. 657 * <li>An open square bracket '['. 658 * <li>The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard. 659 * Parsing is case sensitive. 660 * <li>A close square bracket ']'. 661 * </ul><p> 662 * As this formatter has an optional element, it may be necessary to parse using 663 * {@link DateTimeFormatter#parseBest}. 664 * 665 * @return the ISO date-time formatter, not null 666 */ 667 public static DateTimeFormatter isoDateTime() { 668 return ISO_DATE_TIME; 669 } 670 671 /** Singleton date formatter. */ 672 private static final DateTimeFormatter ISO_DATE_TIME; 673 static { 674 ISO_DATE_TIME = new DateTimeFormatterBuilder() 675 .append(ISO_LOCAL_DATE_TIME) 676 .optionalStart() 677 .appendOffsetId() 678 .optionalStart() 679 .appendLiteral('[') 680 .parseCaseSensitive() 681 .appendZoneRegionId() 682 .appendLiteral(']') 683 .toFormatter(); 684 } 685 686 //----------------------------------------------------------------------- 687 /** 688 * Returns the ISO date formatter that prints/parses the ordinal date 689 * without an offset, such as '2012-337'. 690 * <p> 691 * This returns an immutable formatter capable of printing and parsing 692 * the ISO-8601 extended ordinal date format. 693 * The format consists of: 694 * <p><ul> 695 * <li>Four digits or more for the {@link ChronoField#YEAR year}. 696 * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. 697 * Years outside that range will have a prefixed positive or negative symbol. 698 * <li>A dash 699 * <li>Three digits for the {@link ChronoField#DAY_OF_YEAR day-of-year}. 700 * This is pre-padded by zero to ensure three digits. 701 * <li>If the offset is not available to print/parse then the format is complete. 702 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 703 * they will be handled even though this is not part of the ISO-8601 standard. 704 * Parsing is case insensitive. 705 * </ul><p> 706 * As this formatter has an optional element, it may be necessary to parse using 707 * {@link DateTimeFormatter#parseBest}. 708 * 709 * @return the ISO ordinal date formatter, not null 710 */ 711 public static DateTimeFormatter isoOrdinalDate() { 712 return ISO_ORDINAL_DATE; 713 } 714 715 /** Singleton date formatter. */ 716 private static final DateTimeFormatter ISO_ORDINAL_DATE; 717 static { 718 ISO_ORDINAL_DATE = new DateTimeFormatterBuilder() 719 .parseCaseInsensitive() 720 .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) 721 .appendLiteral('-') 722 .appendValue(DAY_OF_YEAR, 3) 723 .optionalStart() 724 .appendOffsetId() 725 .toFormatter(); 726 } 727 728 //----------------------------------------------------------------------- 729 /** 730 * Returns the ISO date formatter that prints/parses the week-based date 731 * without an offset, such as '2012-W48-6'. 732 * <p> 733 * This returns an immutable formatter capable of printing and parsing 734 * the ISO-8601 extended week-based date format. 735 * The format consists of: 736 * <p><ul> 737 * <li>Four digits or more for the {@link ISOFields#WEEK_BASED_YEAR week-based-year}. 738 * Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. 739 * Years outside that range will have a prefixed positive or negative symbol. 740 * <li>A dash 741 * <li>The letter 'W'. Parsing is case insensitive. 742 * <li>Two digits for the {@link ISOFields#WEEK_OF_WEEK_BASED_YEAR week-of-week-based-year}. 743 * This is pre-padded by zero to ensure three digits. 744 * <li>A dash 745 * <li>One digit for the {@link ChronoField#DAY_OF_WEEK day-of-week}. 746 * The value run from Monday (1) to Sunday (7). 747 * <li>If the offset is not available to print/parse then the format is complete. 748 * <li>The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then 749 * they will be handled even though this is not part of the ISO-8601 standard. 750 * Parsing is case insensitive. 751 * </ul><p> 752 * As this formatter has an optional element, it may be necessary to parse using 753 * {@link DateTimeFormatter#parseBest}. 754 * 755 * @return the ISO week-based date formatter, not null 756 */ 757 public static DateTimeFormatter isoWeekDate() { 758 return ISO_WEEK_DATE; 759 } 760 761 /** Singleton date formatter. */ 762 private static final DateTimeFormatter ISO_WEEK_DATE; 763 static { 764 ISO_WEEK_DATE = new DateTimeFormatterBuilder() 765 .parseCaseInsensitive() 766 .appendValue(ISOFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD) 767 .appendLiteral("-W") 768 .appendValue(ISOFields.WEEK_OF_WEEK_BASED_YEAR, 2) 769 .appendLiteral('-') 770 .appendValue(DAY_OF_WEEK, 1) 771 .optionalStart() 772 .appendOffsetId() 773 .toFormatter(); 774 } 775 776 //----------------------------------------------------------------------- 777 /** 778 * Returns the ISO instant formatter that prints/parses an instant in UTC. 779 * <p> 780 * This returns an immutable formatter capable of printing and parsing 781 * the ISO-8601 instant format. 782 * The format consists of: 783 * <p><ul> 784 * <li>The {@link #isoOffsetDateTime()} where the instant is converted from 785 * {@link ChronoField#INSTANT_SECONDS} and {@link ChronoField#NANO_OF_SECOND} 786 * using the {@code UTC} offset. Parsing is case insensitive. 787 * </ul><p> 788 * 789 * @return the ISO instant formatter, not null 790 */ 791 public static DateTimeFormatter isoInstant() { 792 return ISO_INSTANT; 793 } 794 795 /** Singleton formatter. */ 796 private static final DateTimeFormatter ISO_INSTANT; 797 static { 798 ISO_INSTANT = new DateTimeFormatterBuilder() 799 .parseCaseInsensitive() 800 .appendInstant() 801 .toFormatter(); 802 } 803 804 //----------------------------------------------------------------------- 805 /** 806 * Returns the ISO date formatter that prints/parses a date without an offset, 807 * such as '20111203'. 808 * <p> 809 * This returns an immutable formatter capable of printing and parsing 810 * the ISO-8601 basic local date format. 811 * The format consists of: 812 * <p><ul> 813 * <li>Four digits for the {@link ChronoField#YEAR year}. 814 * Only years in the range 0000 to 9999 are supported. 815 * <li>Two digits for the {@link ChronoField#MONTH_OF_YEAR month-of-year}. 816 * This is pre-padded by zero to ensure two digits. 817 * <li>Two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}. 818 * This is pre-padded by zero to ensure two digits. 819 * <li>If the offset is not available to print/parse then the format is complete. 820 * <li>The {@link ZoneOffset#getId() offset ID} without colons. If the offset has 821 * seconds then they will be handled even though this is not part of the ISO-8601 standard. 822 * Parsing is case insensitive. 823 * </ul><p> 824 * As this formatter has an optional element, it may be necessary to parse using 825 * {@link DateTimeFormatter#parseBest}. 826 * 827 * @return the ISO basic local date formatter, not null 828 */ 829 public static DateTimeFormatter basicIsoDate() { 830 return BASIC_ISO_DATE; 831 } 832 833 /** Singleton date formatter. */ 834 private static final DateTimeFormatter BASIC_ISO_DATE; 835 static { 836 BASIC_ISO_DATE = new DateTimeFormatterBuilder() 837 .parseCaseInsensitive() 838 .appendValue(YEAR, 4) 839 .appendValue(MONTH_OF_YEAR, 2) 840 .appendValue(DAY_OF_MONTH, 2) 841 .optionalStart() 842 .appendOffset("+HHMMss", "Z") 843 .toFormatter(); 844 } 845 846 //----------------------------------------------------------------------- 847 /** 848 * Returns the RFC-1123 date-time formatter, such as 'Tue, 3 Jun 2008 11:05:30 GMT'. 849 * <p> 850 * This returns an immutable formatter capable of printing and parsing 851 * most of the RFC-1123 format. 852 * RFC-1123 updates RFC-822 changing the year from two digits to four. 853 * This implementation requires a four digit year. 854 * This implementation also does not handle North American or military zone 855 * names, only 'GMT' and offset amounts. 856 * <p> 857 * The format consists of: 858 * <p><ul> 859 * <li>If the day-of-week is not available to print/parse then jump to day-of-month. 860 * <li>Three letter {@link ChronoField#DAY_OF_WEEK day-of-week} in English. 861 * <li>A comma 862 * <li>A space 863 * <li>One or two digits for the {@link ChronoField#DAY_OF_MONTH day-of-month}. 864 * <li>A space 865 * <li>Three letter {@link ChronoField#MONTH_OF_YEAR month-of-year} in English. 866 * <li>A space 867 * <li>Four digits for the {@link ChronoField#YEAR year}. 868 * Only years in the range 0000 to 9999 are supported. 869 * <li>A space 870 * <li>Two digits for the {@link ChronoField#HOUR_OF_DAY hour-of-day}. 871 * This is pre-padded by zero to ensure two digits. 872 * <li>A colon 873 * <li>Two digits for the {@link ChronoField#MINUTE_OF_HOUR minute-of-hour}. 874 * This is pre-padded by zero to ensure two digits. 875 * <li>If the second-of-minute is not available to print/parse then jump to the next space. 876 * <li>A colon 877 * <li>Two digits for the {@link ChronoField#SECOND_OF_MINUTE second-of-minute}. 878 * This is pre-padded by zero to ensure two digits. 879 * <li>A space 880 * <li>The {@link ZoneOffset#getId() offset ID} without colons or seconds. 881 * An offset of zero uses "GMT". North American zone names and military zone names are not handled. 882 * </ul><p> 883 * Parsing is case insensitive. 884 * 885 * @return the RFC-1123 formatter, not null 886 */ 887 public static DateTimeFormatter rfc1123() { 888 return RFC_1123_DATE_TIME; 889 } 890 891 /** Singleton date formatter. */ 892 private static final DateTimeFormatter RFC_1123_DATE_TIME; 893 static { 894 // manually code maps to ensure correct data always used 895 // (locale data can be changed by application code) 896 Map<Long, String> dow = new HashMap<>(); 897 dow.put(1L, "Mon"); 898 dow.put(2L, "Tue"); 899 dow.put(3L, "Wed"); 900 dow.put(4L, "Thu"); 901 dow.put(5L, "Fri"); 902 dow.put(6L, "Sat"); 903 dow.put(7L, "Sun"); 904 Map<Long, String> moy = new HashMap<>(); 905 moy.put(1L, "Jan"); 906 moy.put(2L, "Feb"); 907 moy.put(3L, "Mar"); 908 moy.put(4L, "Apr"); 909 moy.put(5L, "May"); 910 moy.put(6L, "Jun"); 911 moy.put(7L, "Jul"); 912 moy.put(8L, "Aug"); 913 moy.put(9L, "Sep"); 914 moy.put(10L, "Oct"); 915 moy.put(11L, "Nov"); 916 moy.put(12L, "Dec"); 917 RFC_1123_DATE_TIME = new DateTimeFormatterBuilder() 918 .parseCaseInsensitive() 919 .parseLenient() 920 .optionalStart() 921 .appendText(DAY_OF_WEEK, dow) 922 .appendLiteral(", ") 923 .optionalEnd() 924 .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) 925 .appendLiteral(' ') 926 .appendText(MONTH_OF_YEAR, moy) 927 .appendLiteral(' ') 928 .appendValue(YEAR, 4) // 2 digit year not handled 929 .appendLiteral(' ') 930 .appendValue(HOUR_OF_DAY, 2) 931 .appendLiteral(':') 932 .appendValue(MINUTE_OF_HOUR, 2) 933 .optionalStart() 934 .appendLiteral(':') 935 .appendValue(SECOND_OF_MINUTE, 2) 936 .optionalEnd() 937 .appendLiteral(' ') 938 .appendOffset("+HHMM", "GMT") // should handle UT/Z/EST/EDT/CST/CDT/MST/MDT/PST/MDT 939 .toFormatter(); 940 } 941 942}