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 }