001: /*
002: * Copyright 2001-2005 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.chrono;
017:
018: import java.io.Serializable;
019:
020: import org.joda.time.Chronology;
021: import org.joda.time.DateTimeField;
022: import org.joda.time.DateTimeFieldType;
023: import org.joda.time.DateTimeZone;
024: import org.joda.time.DurationField;
025: import org.joda.time.DurationFieldType;
026: import org.joda.time.IllegalFieldValueException;
027: import org.joda.time.ReadablePartial;
028: import org.joda.time.ReadablePeriod;
029: import org.joda.time.field.FieldUtils;
030: import org.joda.time.field.UnsupportedDateTimeField;
031: import org.joda.time.field.UnsupportedDurationField;
032:
033: /**
034: * BaseChronology provides a skeleton implementation for chronology
035: * classes. Many utility methods are defined, but all fields are unsupported.
036: * <p>
037: * BaseChronology is thread-safe and immutable, and all subclasses must be
038: * as well.
039: *
040: * @author Brian S O'Neill
041: * @since 1.0
042: */
043: public abstract class BaseChronology extends Chronology implements
044: Serializable {
045:
046: /** Serialization version. */
047: private static final long serialVersionUID = -7310865996721419676L;
048:
049: /**
050: * Restricted constructor.
051: */
052: protected BaseChronology() {
053: super ();
054: }
055:
056: /**
057: * Returns the DateTimeZone that this Chronology operates in, or null if
058: * unspecified.
059: *
060: * @return DateTimeZone null if unspecified
061: */
062: public abstract DateTimeZone getZone();
063:
064: /**
065: * Returns an instance of this Chronology that operates in the UTC time
066: * zone. Chronologies that do not operate in a time zone or are already
067: * UTC must return themself.
068: *
069: * @return a version of this chronology that ignores time zones
070: */
071: public abstract Chronology withUTC();
072:
073: /**
074: * Returns an instance of this Chronology that operates in any time zone.
075: *
076: * @return a version of this chronology with a specific time zone
077: * @param zone to use, or default if null
078: * @see org.joda.time.chrono.ZonedChronology
079: */
080: public abstract Chronology withZone(DateTimeZone zone);
081:
082: /**
083: * Returns a datetime millisecond instant, formed from the given year,
084: * month, day, and millisecond values. The set of given values must refer
085: * to a valid datetime, or else an IllegalArgumentException is thrown.
086: * <p>
087: * The default implementation calls upon separate DateTimeFields to
088: * determine the result. Subclasses are encouraged to provide a more
089: * efficient implementation.
090: *
091: * @param year year to use
092: * @param monthOfYear month to use
093: * @param dayOfMonth day of month to use
094: * @param millisOfDay millisecond to use
095: * @return millisecond instant from 1970-01-01T00:00:00Z
096: */
097: public long getDateTimeMillis(int year, int monthOfYear,
098: int dayOfMonth, int millisOfDay)
099: throws IllegalArgumentException {
100: long instant = year().set(0, year);
101: instant = monthOfYear().set(instant, monthOfYear);
102: instant = dayOfMonth().set(instant, dayOfMonth);
103: return millisOfDay().set(instant, millisOfDay);
104: }
105:
106: /**
107: * Returns a datetime millisecond instant, formed from the given year,
108: * month, day, hour, minute, second, and millisecond values. The set of
109: * given values must refer to a valid datetime, or else an
110: * IllegalArgumentException is thrown.
111: * <p>
112: * The default implementation calls upon separate DateTimeFields to
113: * determine the result. Subclasses are encouraged to provide a more
114: * efficient implementation.
115: *
116: * @param year year to use
117: * @param monthOfYear month to use
118: * @param dayOfMonth day of month to use
119: * @param hourOfDay hour to use
120: * @param minuteOfHour minute to use
121: * @param secondOfMinute second to use
122: * @param millisOfSecond millisecond to use
123: * @return millisecond instant from 1970-01-01T00:00:00Z
124: */
125: public long getDateTimeMillis(int year, int monthOfYear,
126: int dayOfMonth, int hourOfDay, int minuteOfHour,
127: int secondOfMinute, int millisOfSecond)
128: throws IllegalArgumentException {
129: long instant = year().set(0, year);
130: instant = monthOfYear().set(instant, monthOfYear);
131: instant = dayOfMonth().set(instant, dayOfMonth);
132: instant = hourOfDay().set(instant, hourOfDay);
133: instant = minuteOfHour().set(instant, minuteOfHour);
134: instant = secondOfMinute().set(instant, secondOfMinute);
135: return millisOfSecond().set(instant, millisOfSecond);
136: }
137:
138: /**
139: * Returns a datetime millisecond instant, from from the given instant,
140: * hour, minute, second, and millisecond values. The set of given values
141: * must refer to a valid datetime, or else an IllegalArgumentException is
142: * thrown.
143: * <p>
144: * The default implementation calls upon separate DateTimeFields to
145: * determine the result. Subclasses are encouraged to provide a more
146: * efficient implementation.
147: *
148: * @param instant instant to start from
149: * @param hourOfDay hour to use
150: * @param minuteOfHour minute to use
151: * @param secondOfMinute second to use
152: * @param millisOfSecond millisecond to use
153: * @return millisecond instant from 1970-01-01T00:00:00Z
154: */
155: public long getDateTimeMillis(long instant, int hourOfDay,
156: int minuteOfHour, int secondOfMinute, int millisOfSecond)
157: throws IllegalArgumentException {
158: instant = hourOfDay().set(instant, hourOfDay);
159: instant = minuteOfHour().set(instant, minuteOfHour);
160: instant = secondOfMinute().set(instant, secondOfMinute);
161: return millisOfSecond().set(instant, millisOfSecond);
162: }
163:
164: //-----------------------------------------------------------------------
165: /**
166: * Validates whether the fields stored in a partial instant are valid.
167: * <p>
168: * This implementation uses {@link DateTimeField#getMinimumValue(ReadablePartial, int[])}
169: * and {@link DateTimeField#getMaximumValue(ReadablePartial, int[])}.
170: *
171: * @param partial the partial instant to validate
172: * @param values the values to validate, not null
173: * @throws IllegalArgumentException if the instant is invalid
174: */
175: public void validate(ReadablePartial partial, int[] values) {
176: // check values in standard range, catching really stupid cases like -1
177: // this means that the second check will not hit trouble
178: int size = partial.size();
179: for (int i = 0; i < size; i++) {
180: int value = values[i];
181: DateTimeField field = partial.getField(i);
182: if (value < field.getMinimumValue()) {
183: throw new IllegalFieldValueException(field.getType(),
184: new Integer(value), new Integer(field
185: .getMinimumValue()), null);
186: }
187: if (value > field.getMaximumValue()) {
188: throw new IllegalFieldValueException(field.getType(),
189: new Integer(value), null, new Integer(field
190: .getMaximumValue()));
191: }
192: }
193: // check values in specific range, catching really odd cases like 30th Feb
194: for (int i = 0; i < size; i++) {
195: int value = values[i];
196: DateTimeField field = partial.getField(i);
197: if (value < field.getMinimumValue(partial, values)) {
198: throw new IllegalFieldValueException(field.getType(),
199: new Integer(value), new Integer(field
200: .getMinimumValue(partial, values)),
201: null);
202: }
203: if (value > field.getMaximumValue(partial, values)) {
204: throw new IllegalFieldValueException(field.getType(),
205: new Integer(value), null, new Integer(field
206: .getMaximumValue(partial, values)));
207: }
208: }
209: }
210:
211: /**
212: * Gets the values of a partial from an instant.
213: *
214: * @param partial the partial instant to use
215: * @param instant the instant to query
216: * @return the values of the partial extracted from the instant
217: */
218: public int[] get(ReadablePartial partial, long instant) {
219: int size = partial.size();
220: int[] values = new int[size];
221: for (int i = 0; i < size; i++) {
222: values[i] = partial.getFieldType(i).getField(this ).get(
223: instant);
224: }
225: return values;
226: }
227:
228: /**
229: * Sets the partial into the instant.
230: *
231: * @param partial the partial instant to use
232: * @param instant the instant to update
233: * @return the updated instant
234: */
235: public long set(ReadablePartial partial, long instant) {
236: for (int i = 0, isize = partial.size(); i < isize; i++) {
237: instant = partial.getFieldType(i).getField(this ).set(
238: instant, partial.getValue(i));
239: }
240: return instant;
241: }
242:
243: //-----------------------------------------------------------------------
244: /**
245: * Gets the values of a period from an interval.
246: *
247: * @param period the period instant to use
248: * @param startInstant the start instant of an interval to query
249: * @param endInstant the start instant of an interval to query
250: * @return the values of the period extracted from the interval
251: */
252: public int[] get(ReadablePeriod period, long startInstant,
253: long endInstant) {
254: int size = period.size();
255: int[] values = new int[size];
256: if (startInstant != endInstant) {
257: for (int i = 0; i < size; i++) {
258: DurationField field = period.getFieldType(i).getField(
259: this );
260: int value = field.getDifference(endInstant,
261: startInstant);
262: startInstant = field.add(startInstant, value);
263: values[i] = value;
264: }
265: }
266: return values;
267: }
268:
269: /**
270: * Gets the values of a period from an interval.
271: *
272: * @param period the period instant to use
273: * @param duration the duration to query
274: * @return the values of the period extracted from the duration
275: */
276: public int[] get(ReadablePeriod period, long duration) {
277: int size = period.size();
278: int[] values = new int[size];
279: if (duration != 0) {
280: long current = 0;
281: for (int i = 0; i < size; i++) {
282: DurationField field = period.getFieldType(i).getField(
283: this );
284: if (field.isPrecise()) {
285: int value = field.getDifference(duration, current);
286: current = field.add(current, value);
287: values[i] = value;
288: }
289: }
290: }
291: return values;
292: }
293:
294: /**
295: * Adds the period to the instant, specifying the number of times to add.
296: *
297: * @param period the period to add, null means add nothing
298: * @param instant the instant to add to
299: * @param scalar the number of times to add
300: * @return the updated instant
301: */
302: public long add(ReadablePeriod period, long instant, int scalar) {
303: if (scalar != 0 && period != null) {
304: for (int i = 0, isize = period.size(); i < isize; i++) {
305: long value = period.getValue(i); // use long to allow for multiplication (fits OK)
306: if (value != 0) {
307: instant = period.getFieldType(i).getField(this )
308: .add(instant, value * scalar);
309: }
310: }
311: }
312: return instant;
313: }
314:
315: //-----------------------------------------------------------------------
316: /**
317: * Adds the duration to the instant, specifying the number of times to add.
318: *
319: * @param instant the instant to add to
320: * @param duration the duration to add
321: * @param scalar the number of times to add
322: * @return the updated instant
323: */
324: public long add(long instant, long duration, int scalar) {
325: if (duration == 0 || scalar == 0) {
326: return instant;
327: }
328: long add = FieldUtils.safeMultiply(duration, scalar);
329: return FieldUtils.safeAdd(instant, add);
330: }
331:
332: // Millis
333: //-----------------------------------------------------------------------
334: /**
335: * Get the millis duration field for this chronology.
336: *
337: * @return DurationField or UnsupportedDurationField if unsupported
338: */
339: public DurationField millis() {
340: return UnsupportedDurationField.getInstance(DurationFieldType
341: .millis());
342: }
343:
344: /**
345: * Get the millis of second field for this chronology.
346: *
347: * @return DateTimeField or UnsupportedDateTimeField if unsupported
348: */
349: public DateTimeField millisOfSecond() {
350: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
351: .millisOfSecond(), millis());
352: }
353:
354: /**
355: * Get the millis of day field for this chronology.
356: *
357: * @return DateTimeField or UnsupportedDateTimeField if unsupported
358: */
359: public DateTimeField millisOfDay() {
360: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
361: .millisOfDay(), millis());
362: }
363:
364: // Second
365: //-----------------------------------------------------------------------
366: /**
367: * Get the seconds duration field for this chronology.
368: *
369: * @return DurationField or UnsupportedDurationField if unsupported
370: */
371: public DurationField seconds() {
372: return UnsupportedDurationField.getInstance(DurationFieldType
373: .seconds());
374: }
375:
376: /**
377: * Get the second of minute field for this chronology.
378: *
379: * @return DateTimeField or UnsupportedDateTimeField if unsupported
380: */
381: public DateTimeField secondOfMinute() {
382: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
383: .secondOfMinute(), seconds());
384: }
385:
386: /**
387: * Get the second of day field for this chronology.
388: *
389: * @return DateTimeField or UnsupportedDateTimeField if unsupported
390: */
391: public DateTimeField secondOfDay() {
392: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
393: .secondOfDay(), seconds());
394: }
395:
396: // Minute
397: //-----------------------------------------------------------------------
398: /**
399: * Get the minutes duration field for this chronology.
400: *
401: * @return DurationField or UnsupportedDurationField if unsupported
402: */
403: public DurationField minutes() {
404: return UnsupportedDurationField.getInstance(DurationFieldType
405: .minutes());
406: }
407:
408: /**
409: * Get the minute of hour field for this chronology.
410: *
411: * @return DateTimeField or UnsupportedDateTimeField if unsupported
412: */
413: public DateTimeField minuteOfHour() {
414: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
415: .minuteOfHour(), minutes());
416: }
417:
418: /**
419: * Get the minute of day field for this chronology.
420: *
421: * @return DateTimeField or UnsupportedDateTimeField if unsupported
422: */
423: public DateTimeField minuteOfDay() {
424: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
425: .minuteOfDay(), minutes());
426: }
427:
428: // Hour
429: //-----------------------------------------------------------------------
430: /**
431: * Get the hours duration field for this chronology.
432: *
433: * @return DurationField or UnsupportedDurationField if unsupported
434: */
435: public DurationField hours() {
436: return UnsupportedDurationField.getInstance(DurationFieldType
437: .hours());
438: }
439:
440: /**
441: * Get the hour of day (0-23) field for this chronology.
442: *
443: * @return DateTimeField or UnsupportedDateTimeField if unsupported
444: */
445: public DateTimeField hourOfDay() {
446: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
447: .hourOfDay(), hours());
448: }
449:
450: /**
451: * Get the hour of day (offset to 1-24) field for this chronology.
452: *
453: * @return DateTimeField or UnsupportedDateTimeField if unsupported
454: */
455: public DateTimeField clockhourOfDay() {
456: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
457: .clockhourOfDay(), hours());
458: }
459:
460: // Halfday
461: //-----------------------------------------------------------------------
462: /**
463: * Get the halfdays duration field for this chronology.
464: *
465: * @return DurationField or UnsupportedDurationField if unsupported
466: */
467: public DurationField halfdays() {
468: return UnsupportedDurationField.getInstance(DurationFieldType
469: .halfdays());
470: }
471:
472: /**
473: * Get the hour of am/pm (0-11) field for this chronology.
474: *
475: * @return DateTimeField or UnsupportedDateTimeField if unsupported
476: */
477: public DateTimeField hourOfHalfday() {
478: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
479: .hourOfHalfday(), hours());
480: }
481:
482: /**
483: * Get the hour of am/pm (offset to 1-12) field for this chronology.
484: *
485: * @return DateTimeField or UnsupportedDateTimeField if unsupported
486: */
487: public DateTimeField clockhourOfHalfday() {
488: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
489: .clockhourOfHalfday(), hours());
490: }
491:
492: /**
493: * Get the AM(0) PM(1) field for this chronology.
494: *
495: * @return DateTimeField or UnsupportedDateTimeField if unsupported
496: */
497: public DateTimeField halfdayOfDay() {
498: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
499: .halfdayOfDay(), halfdays());
500: }
501:
502: // Day
503: //-----------------------------------------------------------------------
504: /**
505: * Get the days duration field for this chronology.
506: *
507: * @return DurationField or UnsupportedDurationField if unsupported
508: */
509: public DurationField days() {
510: return UnsupportedDurationField.getInstance(DurationFieldType
511: .days());
512: }
513:
514: /**
515: * Get the day of week field for this chronology.
516: *
517: * <p>DayOfWeek values are defined in
518: * {@link org.joda.time.DateTimeConstants DateTimeConstants}.
519: * They use the ISO definitions, where 1 is Monday and 7 is Sunday.
520: *
521: * @return DateTimeField or UnsupportedDateTimeField if unsupported
522: */
523: public DateTimeField dayOfWeek() {
524: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
525: .dayOfWeek(), days());
526: }
527:
528: /**
529: * Get the day of month field for this chronology.
530: *
531: * @return DateTimeField or UnsupportedDateTimeField if unsupported
532: */
533: public DateTimeField dayOfMonth() {
534: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
535: .dayOfMonth(), days());
536: }
537:
538: /**
539: * Get the day of year field for this chronology.
540: *
541: * @return DateTimeField or UnsupportedDateTimeField if unsupported
542: */
543: public DateTimeField dayOfYear() {
544: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
545: .dayOfYear(), days());
546: }
547:
548: // Week
549: //-----------------------------------------------------------------------
550: /**
551: * Get the weeks duration field for this chronology.
552: *
553: * @return DurationField or UnsupportedDurationField if unsupported
554: */
555: public DurationField weeks() {
556: return UnsupportedDurationField.getInstance(DurationFieldType
557: .weeks());
558: }
559:
560: /**
561: * Get the week of a week based year field for this chronology.
562: *
563: * @return DateTimeField or UnsupportedDateTimeField if unsupported
564: */
565: public DateTimeField weekOfWeekyear() {
566: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
567: .weekOfWeekyear(), weeks());
568: }
569:
570: // Weekyear
571: //-----------------------------------------------------------------------
572: /**
573: * Get the weekyears duration field for this chronology.
574: *
575: * @return DurationField or UnsupportedDurationField if unsupported
576: */
577: public DurationField weekyears() {
578: return UnsupportedDurationField.getInstance(DurationFieldType
579: .weekyears());
580: }
581:
582: /**
583: * Get the year of a week based year field for this chronology.
584: *
585: * @return DateTimeField or UnsupportedDateTimeField if unsupported
586: */
587: public DateTimeField weekyear() {
588: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
589: .weekyear(), weekyears());
590: }
591:
592: /**
593: * Get the year of a week based year in a century field for this chronology.
594: *
595: * @return DateTimeField or UnsupportedDateTimeField if unsupported
596: */
597: public DateTimeField weekyearOfCentury() {
598: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
599: .weekyearOfCentury(), weekyears());
600: }
601:
602: // Month
603: //-----------------------------------------------------------------------
604: /**
605: * Get the months duration field for this chronology.
606: *
607: * @return DurationField or UnsupportedDurationField if unsupported
608: */
609: public DurationField months() {
610: return UnsupportedDurationField.getInstance(DurationFieldType
611: .months());
612: }
613:
614: /**
615: * Get the month of year field for this chronology.
616: *
617: * @return DateTimeField or UnsupportedDateTimeField if unsupported
618: */
619: public DateTimeField monthOfYear() {
620: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
621: .monthOfYear(), months());
622: }
623:
624: // Year
625: //-----------------------------------------------------------------------
626: /**
627: * Get the years duration field for this chronology.
628: *
629: * @return DurationField or UnsupportedDurationField if unsupported
630: */
631: public DurationField years() {
632: return UnsupportedDurationField.getInstance(DurationFieldType
633: .years());
634: }
635:
636: /**
637: * Get the year field for this chronology.
638: *
639: * @return DateTimeField or UnsupportedDateTimeField if unsupported
640: */
641: public DateTimeField year() {
642: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
643: .year(), years());
644: }
645:
646: /**
647: * Get the year of era field for this chronology.
648: *
649: * @return DateTimeField or UnsupportedDateTimeField if unsupported
650: */
651: public DateTimeField yearOfEra() {
652: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
653: .yearOfEra(), years());
654: }
655:
656: /**
657: * Get the year of century field for this chronology.
658: *
659: * @return DateTimeField or UnsupportedDateTimeField if unsupported
660: */
661: public DateTimeField yearOfCentury() {
662: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
663: .yearOfCentury(), years());
664: }
665:
666: // Century
667: //-----------------------------------------------------------------------
668: /**
669: * Get the centuries duration field for this chronology.
670: *
671: * @return DurationField or UnsupportedDurationField if unsupported
672: */
673: public DurationField centuries() {
674: return UnsupportedDurationField.getInstance(DurationFieldType
675: .centuries());
676: }
677:
678: /**
679: * Get the century of era field for this chronology.
680: *
681: * @return DateTimeField or UnsupportedDateTimeField if unsupported
682: */
683: public DateTimeField centuryOfEra() {
684: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
685: .centuryOfEra(), centuries());
686: }
687:
688: // Era
689: //-----------------------------------------------------------------------
690: /**
691: * Get the eras duration field for this chronology.
692: *
693: * @return DurationField or UnsupportedDurationField if unsupported
694: */
695: public DurationField eras() {
696: return UnsupportedDurationField.getInstance(DurationFieldType
697: .eras());
698: }
699:
700: /**
701: * Get the era field for this chronology.
702: *
703: * @return DateTimeField or UnsupportedDateTimeField if unsupported
704: */
705: public DateTimeField era() {
706: return UnsupportedDateTimeField.getInstance(DateTimeFieldType
707: .era(), eras());
708: }
709:
710: //-----------------------------------------------------------------------
711: /**
712: * Gets a debugging toString.
713: *
714: * @return a debugging string
715: */
716: public abstract String toString();
717:
718: }
|