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 */
016package org.joda.time.format;
017
018import java.io.IOException;
019import java.io.Writer;
020import java.util.Locale;
021
022import org.joda.time.Chronology;
023import org.joda.time.DateTime;
024import org.joda.time.DateTimeUtils;
025import org.joda.time.DateTimeZone;
026import org.joda.time.LocalDate;
027import org.joda.time.LocalDateTime;
028import org.joda.time.LocalTime;
029import org.joda.time.MutableDateTime;
030import org.joda.time.ReadWritableInstant;
031import org.joda.time.ReadableInstant;
032import 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 */
081public 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}