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