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