1 /*
2 * Copyright 2001-2011 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.time.format;
17
18 import java.io.IOException;
19 import java.io.Writer;
20 import java.util.Locale;
21
22 import org.joda.time.Chronology;
23 import org.joda.time.DateTime;
24 import org.joda.time.DateTimeUtils;
25 import org.joda.time.DateTimeZone;
26 import org.joda.time.LocalDate;
27 import org.joda.time.LocalDateTime;
28 import org.joda.time.LocalTime;
29 import org.joda.time.MutableDateTime;
30 import org.joda.time.ReadWritableInstant;
31 import org.joda.time.ReadableInstant;
32 import org.joda.time.ReadablePartial;
33
34 /**
35 * Controls the printing and parsing of a datetime to and from a string.
36 * <p>
37 * This class is the main API for printing and parsing used by most applications.
38 * Instances of this class are created via one of three factory classes:
39 * <ul>
40 * <li>{@link DateTimeFormat} - formats by pattern and style</li>
41 * <li>{@link ISODateTimeFormat} - ISO8601 formats</li>
42 * <li>{@link DateTimeFormatterBuilder} - complex formats created via method calls</li>
43 * </ul>
44 * <p>
45 * An instance of this class holds a reference internally to one printer and
46 * one parser. It is possible that one of these may be null, in which case the
47 * formatter cannot print/parse. This can be checked via the {@link #isPrinter()}
48 * and {@link #isParser()} methods.
49 * <p>
50 * The underlying printer/parser can be altered to behave exactly as required
51 * by using one of the decorator modifiers:
52 * <ul>
53 * <li>{@link #withLocale(Locale)} - returns a new formatter that uses the specified locale</li>
54 * <li>{@link #withZone(DateTimeZone)} - returns a new formatter that uses the specified time zone</li>
55 * <li>{@link #withChronology(Chronology)} - returns a new formatter that uses the specified chronology</li>
56 * <li>{@link #withOffsetParsed()} - returns a new formatter that returns the parsed time zone offset</li>
57 * <li>{@link #withPivotYear()} - returns a new formatter with the specified pivot year</li>
58 * <li>{@link #withDefaultYear()} - returns a new formatter with the specified default year</li>
59 * </ul>
60 * Each of these returns a new formatter (instances of this class are immutable).
61 * <p>
62 * The main methods of the class are the <code>printXxx</code> and
63 * <code>parseXxx</code> methods. These are used as follows:
64 * <pre>
65 * // print using the defaults (default locale, chronology/zone of the datetime)
66 * String dateStr = formatter.print(dt);
67 * // print using the French locale
68 * String dateStr = formatter.withLocale(Locale.FRENCH).print(dt);
69 * // print using the UTC zone
70 * String dateStr = formatter.withZone(DateTimeZone.UTC).print(dt);
71 *
72 * // parse using the Paris zone
73 * DateTime date = formatter.withZone(DateTimeZone.forID("Europe/Paris")).parseDateTime(str);
74 * </pre>
75 *
76 * @author Brian S O'Neill
77 * @author Stephen Colebourne
78 * @author Fredrik Borgh
79 * @since 1.0
80 */
81 public class DateTimeFormatter {
82
83 /** The internal printer used to output the datetime. */
84 private final DateTimePrinter iPrinter;
85 /** The internal parser used to output the datetime. */
86 private final DateTimeParser iParser;
87 /** The locale to use for printing and parsing. */
88 private final Locale iLocale;
89 /** Whether the offset is parsed. */
90 private final boolean iOffsetParsed;
91 /** The chronology to use as an override. */
92 private final Chronology iChrono;
93 /** The zone to use as an override. */
94 private final DateTimeZone iZone;
95 /** The pivot year to use for two-digit year parsing. */
96 private final Integer iPivotYear;
97 /** The default year for parsing month/day without year. */
98 private final int iDefaultYear;
99
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 }