001 /* 002 * Copyright 2001-2011 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.io.IOException; 019 import java.io.Writer; 020 import java.util.Locale; 021 022 import org.joda.time.Chronology; 023 import org.joda.time.DateTime; 024 import org.joda.time.DateTimeUtils; 025 import org.joda.time.DateTimeZone; 026 import org.joda.time.LocalDate; 027 import org.joda.time.LocalDateTime; 028 import org.joda.time.LocalTime; 029 import org.joda.time.MutableDateTime; 030 import org.joda.time.ReadWritableInstant; 031 import org.joda.time.ReadableInstant; 032 import org.joda.time.ReadablePartial; 033 034 /** 035 * Controls the printing and parsing of a datetime to and from a string. 036 * <p> 037 * This class is the main API for printing and parsing used by most applications. 038 * Instances of this class are created via one of three factory classes: 039 * <ul> 040 * <li>{@link DateTimeFormat} - formats by pattern and style</li> 041 * <li>{@link ISODateTimeFormat} - ISO8601 formats</li> 042 * <li>{@link DateTimeFormatterBuilder} - complex formats created via method calls</li> 043 * </ul> 044 * <p> 045 * An instance of this class holds a reference internally to one printer and 046 * one parser. It is possible that one of these may be null, in which case the 047 * formatter cannot print/parse. This can be checked via the {@link #isPrinter()} 048 * and {@link #isParser()} methods. 049 * <p> 050 * The underlying printer/parser can be altered to behave exactly as required 051 * by using one of the decorator modifiers: 052 * <ul> 053 * <li>{@link #withLocale(Locale)} - returns a new formatter that uses the specified locale</li> 054 * <li>{@link #withZone(DateTimeZone)} - returns a new formatter that uses the specified time zone</li> 055 * <li>{@link #withChronology(Chronology)} - returns a new formatter that uses the specified chronology</li> 056 * <li>{@link #withOffsetParsed()} - returns a new formatter that returns the parsed time zone offset</li> 057 * </ul> 058 * Each of these returns a new formatter (instances of this class are immutable). 059 * <p> 060 * The main methods of the class are the <code>printXxx</code> and 061 * <code>parseXxx</code> methods. These are used as follows: 062 * <pre> 063 * // print using the defaults (default locale, chronology/zone of the datetime) 064 * String dateStr = formatter.print(dt); 065 * // print using the French locale 066 * String dateStr = formatter.withLocale(Locale.FRENCH).print(dt); 067 * // print using the UTC zone 068 * String dateStr = formatter.withZone(DateTimeZone.UTC).print(dt); 069 * 070 * // parse using the Paris zone 071 * DateTime date = formatter.withZone(DateTimeZone.forID("Europe/Paris")).parseDateTime(str); 072 * </pre> 073 * 074 * @author Brian S O'Neill 075 * @author Stephen Colebourne 076 * @author Fredrik Borgh 077 * @since 1.0 078 */ 079 public class DateTimeFormatter { 080 081 /** The internal printer used to output the datetime. */ 082 private final DateTimePrinter iPrinter; 083 /** The internal parser used to output the datetime. */ 084 private final DateTimeParser iParser; 085 /** The locale to use for printing and parsing. */ 086 private final Locale iLocale; 087 /** Whether the offset is parsed. */ 088 private final boolean iOffsetParsed; 089 /** The chronology to use as an override. */ 090 private final Chronology iChrono; 091 /** The zone to use as an override. */ 092 private final DateTimeZone iZone; 093 /** The pivot year to use for two-digit year parsing. */ 094 private final Integer iPivotYear; 095 /** The default year for parsing month/day without year. */ 096 private final int iDefaultYear; 097 098 /** 099 * Creates a new formatter, however you will normally use the factory 100 * or the builder. 101 * 102 * @param printer the internal printer, null if cannot print 103 * @param parser the internal parser, null if cannot parse 104 */ 105 public DateTimeFormatter( 106 DateTimePrinter printer, DateTimeParser parser) { 107 super(); 108 iPrinter = printer; 109 iParser = parser; 110 iLocale = null; 111 iOffsetParsed = false; 112 iChrono = null; 113 iZone = null; 114 iPivotYear = null; 115 iDefaultYear = 2000; 116 } 117 118 /** 119 * Constructor. 120 */ 121 private DateTimeFormatter( 122 DateTimePrinter printer, DateTimeParser parser, 123 Locale locale, boolean offsetParsed, 124 Chronology chrono, DateTimeZone zone, 125 Integer pivotYear, int defaultYear) { 126 super(); 127 iPrinter = printer; 128 iParser = parser; 129 iLocale = locale; 130 iOffsetParsed = offsetParsed; 131 iChrono = chrono; 132 iZone = zone; 133 iPivotYear = pivotYear; 134 iDefaultYear = defaultYear; 135 } 136 137 //----------------------------------------------------------------------- 138 /** 139 * Is this formatter capable of printing. 140 * 141 * @return true if this is a printer 142 */ 143 public boolean isPrinter() { 144 return (iPrinter != null); 145 } 146 147 /** 148 * Gets the internal printer object that performs the real printing work. 149 * 150 * @return the internal printer; is null if printing not supported 151 */ 152 public DateTimePrinter getPrinter() { 153 return iPrinter; 154 } 155 156 /** 157 * Is this formatter capable of parsing. 158 * 159 * @return true if this is a parser 160 */ 161 public boolean isParser() { 162 return (iParser != null); 163 } 164 165 /** 166 * Gets the internal parser object that performs the real parsing work. 167 * 168 * @return the internal parser; is null if parsing not supported 169 */ 170 public DateTimeParser getParser() { 171 return iParser; 172 } 173 174 //----------------------------------------------------------------------- 175 /** 176 * Returns a new formatter with a different locale that will be used 177 * for printing and parsing. 178 * <p> 179 * A DateTimeFormatter is immutable, so a new instance is returned, 180 * and the original is unaltered and still usable. 181 * 182 * @param locale the locale to use; if null, formatter uses default locale 183 * at invocation time 184 * @return the new formatter 185 */ 186 public DateTimeFormatter withLocale(Locale locale) { 187 if (locale == getLocale() || (locale != null && locale.equals(getLocale()))) { 188 return this; 189 } 190 return new DateTimeFormatter(iPrinter, iParser, locale, 191 iOffsetParsed, iChrono, iZone, iPivotYear, iDefaultYear); 192 } 193 194 /** 195 * Gets the locale that will be used for printing and parsing. 196 * 197 * @return the locale to use; if null, formatter uses default locale at 198 * invocation time 199 */ 200 public Locale getLocale() { 201 return iLocale; 202 } 203 204 //----------------------------------------------------------------------- 205 /** 206 * Returns a new formatter that will create a datetime with a time zone 207 * equal to that of the offset of the parsed string. 208 * <p> 209 * After calling this method, a string '2004-06-09T10:20:30-08:00' will 210 * create a datetime with a zone of -08:00 (a fixed zone, with no daylight 211 * savings rules). If the parsed string represents a local time (no zone 212 * offset) the parsed datetime will be in the default zone. 213 * <p> 214 * Calling this method sets the override zone to null. 215 * Calling the override zone method sets this flag off. 216 * 217 * @return the new formatter 218 */ 219 public DateTimeFormatter withOffsetParsed() { 220 if (iOffsetParsed == true) { 221 return this; 222 } 223 return new DateTimeFormatter(iPrinter, iParser, iLocale, 224 true, iChrono, null, iPivotYear, iDefaultYear); 225 } 226 227 /** 228 * Checks whether the offset from the string is used as the zone of 229 * the parsed datetime. 230 * 231 * @return true if the offset from the string is used as the zone 232 */ 233 public boolean isOffsetParsed() { 234 return iOffsetParsed; 235 } 236 237 //----------------------------------------------------------------------- 238 /** 239 * Returns a new formatter that will use the specified chronology in 240 * preference to that of the printed object, or ISO on a parse. 241 * <p> 242 * When printing, this chronolgy will be used in preference to the chronology 243 * from the datetime that would otherwise be used. 244 * <p> 245 * When parsing, this chronology will be set on the parsed datetime. 246 * <p> 247 * A null chronology means no-override. 248 * If both an override chronology and an override zone are set, the 249 * override zone will take precedence over the zone in the chronology. 250 * 251 * @param chrono the chronology to use as an override 252 * @return the new formatter 253 */ 254 public DateTimeFormatter withChronology(Chronology chrono) { 255 if (iChrono == chrono) { 256 return this; 257 } 258 return new DateTimeFormatter(iPrinter, iParser, iLocale, 259 iOffsetParsed, chrono, iZone, iPivotYear, iDefaultYear); 260 } 261 262 /** 263 * Gets the chronology to use as an override. 264 * 265 * @return the chronology to use as an override 266 */ 267 public Chronology getChronology() { 268 return iChrono; 269 } 270 271 /** 272 * Gets the chronology to use as an override. 273 * 274 * @return the chronology to use as an override 275 * @deprecated Use the method with the correct spelling 276 */ 277 @Deprecated 278 public Chronology getChronolgy() { 279 return iChrono; 280 } 281 282 //----------------------------------------------------------------------- 283 /** 284 * Returns a new formatter that will use the UTC zone in preference 285 * to the zone of the printed object, or default zone on a parse. 286 * <p> 287 * When printing, UTC will be used in preference to the zone 288 * from the datetime that would otherwise be used. 289 * <p> 290 * When parsing, UTC will be set on the parsed datetime. 291 * <p> 292 * If both an override chronology and an override zone are set, the 293 * override zone will take precedence over the zone in the chronology. 294 * 295 * @return the new formatter, never null 296 * @since 2.0 297 */ 298 public DateTimeFormatter withZoneUTC() { 299 return withZone(DateTimeZone.UTC); 300 } 301 302 /** 303 * Returns a new formatter that will use the specified zone in preference 304 * to the zone of the printed object, or default zone on a parse. 305 * <p> 306 * When printing, this zone will be used in preference to the zone 307 * from the datetime that would otherwise be used. 308 * <p> 309 * When parsing, this zone will be set on the parsed datetime. 310 * <p> 311 * A null zone means of no-override. 312 * If both an override chronology and an override zone are set, the 313 * override zone will take precedence over the zone in the chronology. 314 * 315 * @param zone the zone to use as an override 316 * @return the new formatter 317 */ 318 public DateTimeFormatter withZone(DateTimeZone zone) { 319 if (iZone == zone) { 320 return this; 321 } 322 return new DateTimeFormatter(iPrinter, iParser, iLocale, 323 false, iChrono, zone, iPivotYear, iDefaultYear); 324 } 325 326 /** 327 * Gets the zone to use as an override. 328 * 329 * @return the zone to use as an override 330 */ 331 public DateTimeZone getZone() { 332 return iZone; 333 } 334 335 //----------------------------------------------------------------------- 336 /** 337 * Returns a new formatter that will use the specified pivot year for two 338 * digit year parsing in preference to that stored in the parser. 339 * <p> 340 * This setting is useful for changing the pivot year of formats built 341 * using a pattern - {@link DateTimeFormat#forPattern(String)}. 342 * <p> 343 * When parsing, this pivot year is used. Null means no-override. 344 * There is no effect when printing. 345 * <p> 346 * The pivot year enables a two digit year to be converted to a four 347 * digit year. The pivot represents the year in the middle of the 348 * supported range of years. Thus the full range of years that will 349 * be built is <code>(pivot - 50) .. (pivot + 49)</code>. 350 * 351 * <pre> 352 * pivot supported range 00 is 20 is 40 is 60 is 80 is 353 * --------------------------------------------------------------- 354 * 1950 1900..1999 1900 1920 1940 1960 1980 355 * 1975 1925..2024 2000 2020 1940 1960 1980 356 * 2000 1950..2049 2000 2020 2040 1960 1980 357 * 2025 1975..2074 2000 2020 2040 2060 1980 358 * 2050 2000..2099 2000 2020 2040 2060 2080 359 * </pre> 360 * 361 * @param pivotYear the pivot year to use as an override when parsing 362 * @return the new formatter 363 * @since 1.1 364 */ 365 public DateTimeFormatter withPivotYear(Integer pivotYear) { 366 if (iPivotYear == pivotYear || (iPivotYear != null && iPivotYear.equals(pivotYear))) { 367 return this; 368 } 369 return new DateTimeFormatter(iPrinter, iParser, iLocale, 370 iOffsetParsed, iChrono, iZone, pivotYear, iDefaultYear); 371 } 372 373 /** 374 * Returns a new formatter that will use the specified pivot year for two 375 * digit year parsing in preference to that stored in the parser. 376 * <p> 377 * This setting is useful for changing the pivot year of formats built 378 * using a pattern - {@link DateTimeFormat#forPattern(String)}. 379 * <p> 380 * When parsing, this pivot year is used. 381 * There is no effect when printing. 382 * <p> 383 * The pivot year enables a two digit year to be converted to a four 384 * digit year. The pivot represents the year in the middle of the 385 * supported range of years. Thus the full range of years that will 386 * be built is <code>(pivot - 50) .. (pivot + 49)</code>. 387 * 388 * <pre> 389 * pivot supported range 00 is 20 is 40 is 60 is 80 is 390 * --------------------------------------------------------------- 391 * 1950 1900..1999 1900 1920 1940 1960 1980 392 * 1975 1925..2024 2000 2020 1940 1960 1980 393 * 2000 1950..2049 2000 2020 2040 1960 1980 394 * 2025 1975..2074 2000 2020 2040 2060 1980 395 * 2050 2000..2099 2000 2020 2040 2060 2080 396 * </pre> 397 * 398 * @param pivotYear the pivot year to use as an override when parsing 399 * @return the new formatter 400 * @since 1.1 401 */ 402 public DateTimeFormatter withPivotYear(int pivotYear) { 403 return withPivotYear(Integer.valueOf(pivotYear)); 404 } 405 406 /** 407 * Gets the pivot year to use as an override. 408 * 409 * @return the pivot year to use as an override 410 * @since 1.1 411 */ 412 public Integer getPivotYear() { 413 return iPivotYear; 414 } 415 416 //----------------------------------------------------------------------- 417 /** 418 * Returns a new formatter that will use the specified default year. 419 * <p> 420 * The default year is used when parsing in the case where there is a 421 * month or a day but not a year. Specifically, it is used if there is 422 * a field parsed with a duration between the length of a month and the 423 * length of a day inclusive. 424 * <p> 425 * This value is typically used to move the year from 1970 to a leap year 426 * to enable February 29th to be parsed. 427 * Unless customised, the year 2000 is used. 428 * <p> 429 * This setting has no effect when printing. 430 * 431 * @param defaultYear the default year to use 432 * @return the new formatter, not null 433 * @since 2.0 434 */ 435 public DateTimeFormatter withDefaultYear(int defaultYear) { 436 return new DateTimeFormatter(iPrinter, iParser, iLocale, 437 iOffsetParsed, iChrono, iZone, iPivotYear, defaultYear); 438 } 439 440 /** 441 * Gets the default year for parsing months and days. 442 * 443 * @return the default year for parsing months and days 444 * @since 2.0 445 */ 446 public int getDefaultYear() { 447 return iDefaultYear; 448 } 449 450 //----------------------------------------------------------------------- 451 /** 452 * Prints a ReadableInstant, using the chronology supplied by the instant. 453 * 454 * @param buf the destination to format to, not null 455 * @param instant instant to format, null means now 456 */ 457 public void printTo(StringBuffer buf, ReadableInstant instant) { 458 long millis = DateTimeUtils.getInstantMillis(instant); 459 Chronology chrono = DateTimeUtils.getInstantChronology(instant); 460 printTo(buf, millis, chrono); 461 } 462 463 /** 464 * Prints a ReadableInstant, using the chronology supplied by the instant. 465 * 466 * @param out the destination to format to, not null 467 * @param instant instant to format, null means now 468 */ 469 public void printTo(Writer out, ReadableInstant instant) throws IOException { 470 long millis = DateTimeUtils.getInstantMillis(instant); 471 Chronology chrono = DateTimeUtils.getInstantChronology(instant); 472 printTo(out, millis, chrono); 473 } 474 475 /** 476 * Prints a ReadableInstant, using the chronology supplied by the instant. 477 * 478 * @param appendable the destination to format to, not null 479 * @param instant instant to format, null means now 480 * @since 2.0 481 */ 482 public void printTo(Appendable appendable, ReadableInstant instant) throws IOException { 483 appendable.append(print(instant)); 484 } 485 486 //----------------------------------------------------------------------- 487 /** 488 * Prints an instant from milliseconds since 1970-01-01T00:00:00Z, 489 * using ISO chronology in the default DateTimeZone. 490 * 491 * @param buf the destination to format to, not null 492 * @param instant millis since 1970-01-01T00:00:00Z 493 */ 494 public void printTo(StringBuffer buf, long instant) { 495 printTo(buf, instant, null); 496 } 497 498 /** 499 * Prints an instant from milliseconds since 1970-01-01T00:00:00Z, 500 * using ISO chronology in the default DateTimeZone. 501 * 502 * @param out the destination to format to, not null 503 * @param instant millis since 1970-01-01T00:00:00Z 504 */ 505 public void printTo(Writer out, long instant) throws IOException { 506 printTo(out, instant, null); 507 } 508 509 /** 510 * Prints an instant from milliseconds since 1970-01-01T00:00:00Z, 511 * using ISO chronology in the default DateTimeZone. 512 * 513 * @param appendable the destination to format to, not null 514 * @param instant millis since 1970-01-01T00:00:00Z 515 * @since 2.0 516 */ 517 public void printTo(Appendable appendable, long instant) throws IOException { 518 appendable.append(print(instant)); 519 } 520 521 //----------------------------------------------------------------------- 522 /** 523 * Prints a ReadablePartial. 524 * <p> 525 * Neither the override chronology nor the override zone are used 526 * by this method. 527 * 528 * @param buf the destination to format to, not null 529 * @param partial partial to format 530 */ 531 public void printTo(StringBuffer buf, ReadablePartial partial) { 532 DateTimePrinter printer = requirePrinter(); 533 if (partial == null) { 534 throw new IllegalArgumentException("The partial must not be null"); 535 } 536 printer.printTo(buf, partial, iLocale); 537 } 538 539 /** 540 * Prints a ReadablePartial. 541 * <p> 542 * Neither the override chronology nor the override zone are used 543 * by this method. 544 * 545 * @param out the destination to format to, not null 546 * @param partial partial to format 547 */ 548 public void printTo(Writer out, ReadablePartial partial) throws IOException { 549 DateTimePrinter printer = requirePrinter(); 550 if (partial == null) { 551 throw new IllegalArgumentException("The partial must not be null"); 552 } 553 printer.printTo(out, partial, iLocale); 554 } 555 556 /** 557 * Prints a ReadablePartial. 558 * <p> 559 * Neither the override chronology nor the override zone are used 560 * by this method. 561 * 562 * @param appendable the destination to format to, not null 563 * @param partial partial to format 564 * @since 2.0 565 */ 566 public void printTo(Appendable appendable, ReadablePartial partial) throws IOException { 567 appendable.append(print(partial)); 568 } 569 570 //----------------------------------------------------------------------- 571 /** 572 * Prints a ReadableInstant to a String. 573 * <p> 574 * This method will use the override zone and the override chronololgy if 575 * they are set. Otherwise it will use the chronology and zone of the instant. 576 * 577 * @param instant instant to format, null means now 578 * @return the printed result 579 */ 580 public String print(ReadableInstant instant) { 581 StringBuffer buf = new StringBuffer(requirePrinter().estimatePrintedLength()); 582 printTo(buf, instant); 583 return buf.toString(); 584 } 585 586 /** 587 * Prints a millisecond instant to a String. 588 * <p> 589 * This method will use the override zone and the override chronololgy if 590 * they are set. Otherwise it will use the ISO chronology and default zone. 591 * 592 * @param instant millis since 1970-01-01T00:00:00Z 593 * @return the printed result 594 */ 595 public String print(long instant) { 596 StringBuffer buf = new StringBuffer(requirePrinter().estimatePrintedLength()); 597 printTo(buf, instant); 598 return buf.toString(); 599 } 600 601 /** 602 * Prints a ReadablePartial to a new String. 603 * <p> 604 * Neither the override chronology nor the override zone are used 605 * by this method. 606 * 607 * @param partial partial to format 608 * @return the printed result 609 */ 610 public String print(ReadablePartial partial) { 611 StringBuffer buf = new StringBuffer(requirePrinter().estimatePrintedLength()); 612 printTo(buf, partial); 613 return buf.toString(); 614 } 615 616 private void printTo(StringBuffer buf, long instant, Chronology chrono) { 617 DateTimePrinter printer = requirePrinter(); 618 chrono = selectChronology(chrono); 619 // Shift instant into local time (UTC) to avoid excessive offset 620 // calculations when printing multiple fields in a composite printer. 621 DateTimeZone zone = chrono.getZone(); 622 int offset = zone.getOffset(instant); 623 long adjustedInstant = instant + offset; 624 if ((instant ^ adjustedInstant) < 0 && (instant ^ offset) >= 0) { 625 // Time zone offset overflow, so revert to UTC. 626 zone = DateTimeZone.UTC; 627 offset = 0; 628 adjustedInstant = instant; 629 } 630 printer.printTo(buf, adjustedInstant, chrono.withUTC(), offset, zone, iLocale); 631 } 632 633 private void printTo(Writer buf, long instant, Chronology chrono) throws IOException { 634 DateTimePrinter printer = requirePrinter(); 635 chrono = selectChronology(chrono); 636 // Shift instant into local time (UTC) to avoid excessive offset 637 // calculations when printing multiple fields in a composite printer. 638 DateTimeZone zone = chrono.getZone(); 639 int offset = zone.getOffset(instant); 640 long adjustedInstant = instant + offset; 641 if ((instant ^ adjustedInstant) < 0 && (instant ^ offset) >= 0) { 642 // Time zone offset overflow, so revert to UTC. 643 zone = DateTimeZone.UTC; 644 offset = 0; 645 adjustedInstant = instant; 646 } 647 printer.printTo(buf, adjustedInstant, chrono.withUTC(), offset, zone, iLocale); 648 } 649 650 /** 651 * Checks whether printing is supported. 652 * 653 * @throws UnsupportedOperationException if printing is not supported 654 */ 655 private DateTimePrinter requirePrinter() { 656 DateTimePrinter printer = iPrinter; 657 if (printer == null) { 658 throw new UnsupportedOperationException("Printing not supported"); 659 } 660 return printer; 661 } 662 663 //----------------------------------------------------------------------- 664 /** 665 * Parses a datetime from the given text, at the given position, saving the 666 * result into the fields of the given ReadWritableInstant. If the parse 667 * succeeds, the return value is the new text position. Note that the parse 668 * may succeed without fully reading the text and in this case those fields 669 * that were read will be set. 670 * <p> 671 * Only those fields present in the string will be changed in the specified 672 * instant. All other fields will remain unaltered. Thus if the string only 673 * contains a year and a month, then the day and time will be retained from 674 * the input instant. If this is not the behaviour you want, then reset the 675 * fields before calling this method, or use {@link #parseDateTime(String)} 676 * or {@link #parseMutableDateTime(String)}. 677 * <p> 678 * If it fails, the return value is negative, but the instant may still be 679 * modified. To determine the position where the parse failed, apply the 680 * one's complement operator (~) on the return value. 681 * <p> 682 * The parse will use the chronology of the instant. 683 * 684 * @param instant an instant that will be modified, not null 685 * @param text the text to parse 686 * @param position position to start parsing from 687 * @return new position, negative value means parse failed - 688 * apply complement operator (~) to get position of failure 689 * @throws UnsupportedOperationException if parsing is not supported 690 * @throws IllegalArgumentException if the instant is null 691 * @throws IllegalArgumentException if any field is out of range 692 */ 693 public int parseInto(ReadWritableInstant instant, String text, int position) { 694 DateTimeParser parser = requireParser(); 695 if (instant == null) { 696 throw new IllegalArgumentException("Instant must not be null"); 697 } 698 699 long instantMillis = instant.getMillis(); 700 Chronology chrono = instant.getChronology(); 701 long instantLocal = instantMillis + chrono.getZone().getOffset(instantMillis); 702 chrono = selectChronology(chrono); 703 704 DateTimeParserBucket bucket = new DateTimeParserBucket( 705 instantLocal, chrono, iLocale, iPivotYear, iDefaultYear); 706 int newPos = parser.parseInto(bucket, text, position); 707 instant.setMillis(bucket.computeMillis(false, text)); 708 if (iOffsetParsed && bucket.getOffsetInteger() != null) { 709 int parsedOffset = bucket.getOffsetInteger(); 710 DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); 711 chrono = chrono.withZone(parsedZone); 712 } else if (bucket.getZone() != null) { 713 chrono = chrono.withZone(bucket.getZone()); 714 } 715 instant.setChronology(chrono); 716 if (iZone != null) { 717 instant.setZone(iZone); 718 } 719 return newPos; 720 } 721 722 /** 723 * Parses a datetime from the given text, returning the number of 724 * milliseconds since the epoch, 1970-01-01T00:00:00Z. 725 * <p> 726 * The parse will use the ISO chronology, and the default time zone. 727 * If the text contains a time zone string then that will be taken into account. 728 * 729 * @param text text to parse 730 * @return parsed value expressed in milliseconds since the epoch 731 * @throws UnsupportedOperationException if parsing is not supported 732 * @throws IllegalArgumentException if the text to parse is invalid 733 */ 734 public long parseMillis(String text) { 735 DateTimeParser parser = requireParser(); 736 737 Chronology chrono = selectChronology(iChrono); 738 DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear); 739 int newPos = parser.parseInto(bucket, text, 0); 740 if (newPos >= 0) { 741 if (newPos >= text.length()) { 742 return bucket.computeMillis(true, text); 743 } 744 } else { 745 newPos = ~newPos; 746 } 747 throw new IllegalArgumentException(FormatUtils.createErrorMessage(text, newPos)); 748 } 749 750 /** 751 * Parses only the local date from the given text, returning a new LocalDate. 752 * <p> 753 * This will parse the text fully according to the formatter, using the UTC zone. 754 * Once parsed, only the local date will be used. 755 * This means that any parsed time, time-zone or offset field is completely ignored. 756 * It also means that the zone and offset-parsed settings are ignored. 757 * 758 * @param text the text to parse, not null 759 * @return the parsed date, never null 760 * @throws UnsupportedOperationException if parsing is not supported 761 * @throws IllegalArgumentException if the text to parse is invalid 762 * @since 2.0 763 */ 764 public LocalDate parseLocalDate(String text) { 765 return parseLocalDateTime(text).toLocalDate(); 766 } 767 768 /** 769 * Parses only the local time from the given text, returning a new LocalDate. 770 * <p> 771 * This will parse the text fully according to the formatter, using the UTC zone. 772 * Once parsed, only the local time will be used. 773 * This means that any parsed date, time-zone or offset field is completely ignored. 774 * It also means that the zone and offset-parsed settings are ignored. 775 * 776 * @param text the text to parse, not null 777 * @return the parsed time, never null 778 * @throws UnsupportedOperationException if parsing is not supported 779 * @throws IllegalArgumentException if the text to parse is invalid 780 * @since 2.0 781 */ 782 public LocalTime parseLocalTime(String text) { 783 return parseLocalDateTime(text).toLocalTime(); 784 } 785 786 /** 787 * Parses only the local date-time from the given text, returning a new LocalDate. 788 * <p> 789 * This will parse the text fully according to the formatter, using the UTC zone. 790 * Once parsed, only the local date-time will be used. 791 * This means that any parsed time-zone or offset field is completely ignored. 792 * It also means that the zone and offset-parsed settings are ignored. 793 * 794 * @param text the text to parse, not null 795 * @return the parsed date-time, never null 796 * @throws UnsupportedOperationException if parsing is not supported 797 * @throws IllegalArgumentException if the text to parse is invalid 798 * @since 2.0 799 */ 800 public LocalDateTime parseLocalDateTime(String text) { 801 DateTimeParser parser = requireParser(); 802 803 Chronology chrono = selectChronology(null).withUTC(); // always use UTC, avoiding DST gaps 804 DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear); 805 int newPos = parser.parseInto(bucket, text, 0); 806 if (newPos >= 0) { 807 if (newPos >= text.length()) { 808 long millis = bucket.computeMillis(true, text); 809 if (bucket.getOffsetInteger() != null) { // treat withOffsetParsed() as being true 810 int parsedOffset = bucket.getOffsetInteger(); 811 DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); 812 chrono = chrono.withZone(parsedZone); 813 } else if (bucket.getZone() != null) { 814 chrono = chrono.withZone(bucket.getZone()); 815 } 816 return new LocalDateTime(millis, chrono); 817 } 818 } else { 819 newPos = ~newPos; 820 } 821 throw new IllegalArgumentException(FormatUtils.createErrorMessage(text, newPos)); 822 } 823 824 /** 825 * Parses a date-time from the given text, returning a new DateTime. 826 * <p> 827 * The parse will use the zone and chronology specified on this formatter. 828 * <p> 829 * If the text contains a time zone string then that will be taken into 830 * account in adjusting the time of day as follows. 831 * If the {@link #withOffsetParsed()} has been called, then the resulting 832 * DateTime will have a fixed offset based on the parsed time zone. 833 * Otherwise the resulting DateTime will have the zone of this formatter, 834 * but the parsed zone may have caused the time to be adjusted. 835 * 836 * @param text the text to parse, not null 837 * @return the parsed date-time, never null 838 * @throws UnsupportedOperationException if parsing is not supported 839 * @throws IllegalArgumentException if the text to parse is invalid 840 */ 841 public DateTime parseDateTime(String text) { 842 DateTimeParser parser = requireParser(); 843 844 Chronology chrono = selectChronology(null); 845 DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear); 846 int newPos = parser.parseInto(bucket, text, 0); 847 if (newPos >= 0) { 848 if (newPos >= text.length()) { 849 long millis = bucket.computeMillis(true, text); 850 if (iOffsetParsed && bucket.getOffsetInteger() != null) { 851 int parsedOffset = bucket.getOffsetInteger(); 852 DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); 853 chrono = chrono.withZone(parsedZone); 854 } else if (bucket.getZone() != null) { 855 chrono = chrono.withZone(bucket.getZone()); 856 } 857 DateTime dt = new DateTime(millis, chrono); 858 if (iZone != null) { 859 dt = dt.withZone(iZone); 860 } 861 return dt; 862 } 863 } else { 864 newPos = ~newPos; 865 } 866 throw new IllegalArgumentException(FormatUtils.createErrorMessage(text, newPos)); 867 } 868 869 /** 870 * Parses a date-time from the given text, returning a new MutableDateTime. 871 * <p> 872 * The parse will use the zone and chronology specified on this formatter. 873 * <p> 874 * If the text contains a time zone string then that will be taken into 875 * account in adjusting the time of day as follows. 876 * If the {@link #withOffsetParsed()} has been called, then the resulting 877 * DateTime will have a fixed offset based on the parsed time zone. 878 * Otherwise the resulting DateTime will have the zone of this formatter, 879 * but the parsed zone may have caused the time to be adjusted. 880 * 881 * @param text the text to parse, not null 882 * @return the parsed date-time, never null 883 * @throws UnsupportedOperationException if parsing is not supported 884 * @throws IllegalArgumentException if the text to parse is invalid 885 */ 886 public MutableDateTime parseMutableDateTime(String text) { 887 DateTimeParser parser = requireParser(); 888 889 Chronology chrono = selectChronology(null); 890 DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear); 891 int newPos = parser.parseInto(bucket, text, 0); 892 if (newPos >= 0) { 893 if (newPos >= text.length()) { 894 long millis = bucket.computeMillis(true, text); 895 if (iOffsetParsed && bucket.getOffsetInteger() != null) { 896 int parsedOffset = bucket.getOffsetInteger(); 897 DateTimeZone parsedZone = DateTimeZone.forOffsetMillis(parsedOffset); 898 chrono = chrono.withZone(parsedZone); 899 } else if (bucket.getZone() != null) { 900 chrono = chrono.withZone(bucket.getZone()); 901 } 902 MutableDateTime dt = new MutableDateTime(millis, chrono); 903 if (iZone != null) { 904 dt.setZone(iZone); 905 } 906 return dt; 907 } 908 } else { 909 newPos = ~newPos; 910 } 911 throw new IllegalArgumentException(FormatUtils.createErrorMessage(text, newPos)); 912 } 913 914 /** 915 * Checks whether parsing is supported. 916 * 917 * @throws UnsupportedOperationException if parsing is not supported 918 */ 919 private DateTimeParser requireParser() { 920 DateTimeParser parser = iParser; 921 if (parser == null) { 922 throw new UnsupportedOperationException("Parsing not supported"); 923 } 924 return parser; 925 } 926 927 //----------------------------------------------------------------------- 928 /** 929 * Determines the correct chronology to use. 930 * 931 * @param chrono the proposed chronology 932 * @return the actual chronology 933 */ 934 private Chronology selectChronology(Chronology chrono) { 935 chrono = DateTimeUtils.getChronology(chrono); 936 if (iChrono != null) { 937 chrono = iChrono; 938 } 939 if (iZone != null) { 940 chrono = chrono.withZone(iZone); 941 } 942 return chrono; 943 } 944 945 }