1 package org.collectionspace.services.structureddate;
3 import org.joda.time.Chronology;
4 import org.joda.time.DateTime;
5 import org.joda.time.DateTimeConstants;
6 import org.joda.time.Days;
7 import org.joda.time.IllegalFieldValueException;
8 import org.joda.time.MutableDateTime;
9 import org.joda.time.Years;
10 import org.joda.time.chrono.GJChronology;
11 import org.joda.time.format.DateTimeFormat;
12 import org.joda.time.format.DateTimeFormatter;
14 public class DateUtils {
15 private static final DateTimeFormatter monthFormatter = DateTimeFormat.forPattern("MMMM");
16 private static final DateTimeFormatter scalarDateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
18 // The chronology to use for date calculations, which are done using the joda-time library.
19 // See http://www.joda.org/joda-time/apidocs/org/joda/time/Chronology.html for descriptions of
20 // the chronologies supported by joda-time.
22 // GJChronology (http://www.joda.org/joda-time/apidocs/org/joda/time/chrono/GJChronology.html)
23 // seems best for representing a mix of modern and historical dates, as might be seen by an
24 // anthropology museum.
26 private static final Chronology chronology = GJChronology.getInstance();
28 // Define the DateTime that serves as the basis for circa calculations, using the algorithm
29 // ported from the XDB date parser. Its comment states:
31 // We define circa year/century specifications offsets
32 // as +/- 5% of the difference between that year/century
33 // and the present (2100), so that the farther we go back
34 // in time, the wider the range of meaning of "circa."
36 private static final DateTime circaBaseDateTime = new DateTime(2100, 12, 31, 0, 0, 0, 0, chronology);
39 * Gets the number (1-12) of a month for a given name.
41 * @param monthName The name of the month
42 * @return The number of the month, between 1 and 12
44 public static int getMonthByName(String monthName) {
45 // Normalize "sept" to "sep", since DateTimeFormat doesn't
46 // understand the former.
48 if (monthName.equals("sept")) {
52 return monthFormatter.parseDateTime(monthName).getMonthOfYear();
56 * Gets the number of days in a given month.
58 * @param month The month number, between 1 and 12
59 * @param year The year (in order to account for leap years)
60 * @return The number of days in the month
62 public static int getDaysInMonth(int month, int year, Era era) {
64 era = Date.DEFAULT_ERA;
67 DateTime dateTime = new DateTime(chronology)
68 .withEra((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD)
70 .withMonthOfYear(month);
72 return dateTime.dayOfMonth().getMaximumValue();
76 * Gets the Date representing the first day of a given quarter year.
78 * @param year The year
79 * @param quarter The quarter, between 1 and 4
80 * @return The first day of the quarter year
82 public static Date getQuarterYearStartDate(int quarter, int year) {
83 int startMonth = getQuarterYearStartMonth(quarter);
85 return new Date(year, startMonth, 1);
89 * Gets the Date representing the last day of a given quarter year.
91 * @param year The year
92 * @param quarter The quarter, between 1 and 4
93 * @return The last day of the quarter year
95 public static Date getQuarterYearEndDate(int quarter, int year, Era era) {
96 int endMonth = getQuarterYearEndMonth(quarter);
98 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
102 * Gets the first month of a given quarter in a year.
104 * @param quarter The quarter, between 1 and 4
105 * @return The number of the first month in the quarter
107 public static int getQuarterYearStartMonth(int quarter) {
108 return ((3 * (quarter-1)) + 1);
112 * Gets the last month of a given quarter in a year.
114 * @param quarter The quarter, between 1 and 4
115 * @return The number of the last month in the quarter
117 public static int getQuarterYearEndMonth(int quarter) {
118 return (getQuarterYearStartMonth(quarter) + 2);
122 * Gets the Date representing the first day of a given half year.
124 * @param year The year
125 * @param half The half, between 1 and 2
126 * @return The first day of the half year
128 public static Date getHalfYearStartDate(int half, int year) {
129 int startMonth = getHalfYearStartMonth(half);
131 return new Date(year, startMonth, 1);
136 * Gets the Date representing the last day of a given half year.
138 * @param year The year
139 * @param half The half, between 1 and 2
140 * @return The last day of the half year
142 public static Date getHalfYearEndDate(int half, int year, Era era) {
143 int endMonth = getHalfYearEndMonth(half);
145 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
149 * Gets the first month of a given half in a year.
151 * @param half The half, between 1 and 2
152 * @return The number of the first month in the half
154 public static int getHalfYearStartMonth(int half) {
155 return ((6 * (half-1)) + 1);
159 * Gets the last month of a given half in a year.
161 * @param half The half, between 1 and 2
162 * @return The number of the last month in the half
164 public static int getHalfYearEndMonth(int half) {
165 return (getHalfYearStartMonth(half) + 5);
169 * Gets the Date representing the first day of a given partial year.
171 * @param year The year
172 * @param part The part
173 * @return The first day of the partial year
175 public static Date getPartialYearStartDate(Part part, int year) {
176 int startMonth = getPartialYearStartMonth(part);
178 return new Date(year, startMonth, 1);
182 * Gets the Date representing the last day of a given partial year.
184 * @param year The year
185 * @param part The part
186 * @return The last day of the partial year
188 public static Date getPartialYearEndDate(Part part, int year, Era era) {
189 int endMonth = getPartialYearEndMonth(part);
191 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
195 * Gets the first month of a given part of a year.
197 * @param part The part
198 * @return The number of the first month in the part
200 public static int getPartialYearStartMonth(Part part) {
203 if (part == Part.EARLY) {
206 else if (part == Part.MIDDLE) {
209 else if (part == Part.LATE) {
213 throw new IllegalArgumentException("unexpected part");
220 * Gets the last month of a given part of a year.
222 * @param part The part
223 * @return The number of the last month in the part
225 public static int getPartialYearEndMonth(Part part) {
228 if (part == Part.EARLY) {
231 else if (part == Part.MIDDLE) {
234 else if (part == Part.LATE) {
238 throw new IllegalArgumentException("unexpected part");
245 * Gets the Date representing the first day of a given partial decade.
247 * @param decade The decade, specified as a number ending in 0.
248 * For decades A.D., this is the first year of the decade. For
249 * decades B.C., this is the last year of the decade.
250 * @param part The part
251 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
252 * @return The first day of the partial decade
254 public static Date getPartialDecadeStartDate(int decade, Part part, Era era) {
256 era = Date.DEFAULT_ERA;
259 int startYear = getPartialDecadeStartYear(decade, part, era);
261 return new Date(startYear, 1, 1, era);
265 * Gets the Date representing the last day of a given partial decade.
267 * @param decade The decade, specified as a number ending in 0.
268 * For decades A.D., this is the first year of the decade. For
269 * decades B.C., this is the last year of the decade.
270 * @param part The part
271 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
272 * @return The last day of the partial decade
274 public static Date getPartialDecadeEndDate(int decade, Part part, Era era) {
276 era = Date.DEFAULT_ERA;
279 int endYear = getPartialDecadeEndYear(decade, part, era);
281 return new Date(endYear, 12, 31, era);
285 * Gets the first year of a given part of a decade.
287 * @param decade The decade, specified as a number ending in 0.
288 * For decades A.D., this is the first year of the decade. For
289 * decades B.C., this is the last year of the decade.
290 * @param part The part
291 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
292 * @return The first year in the part
294 public static int getPartialDecadeStartYear(int decade, Part part, Era era) {
296 era = Date.DEFAULT_ERA;
301 if (era == Era.BCE) {
302 if (part == Part.EARLY) {
303 startYear = decade + 9;
305 else if (part == Part.MIDDLE) {
306 startYear = decade + 6;
308 else if (part == Part.LATE) {
309 startYear = decade + 3;
312 throw new IllegalArgumentException("unexpected part");
316 if (part == Part.EARLY) {
319 else if (part == Part.MIDDLE) {
320 startYear = decade + 4;
322 else if (part == Part.LATE) {
323 startYear = decade + 7;
326 throw new IllegalArgumentException("unexpected part");
334 * Gets the last year of a given part of a decade.
336 * @param decade The decade, specified as a number ending in 0.
337 * For decades A.D., this is the first year of the decade. For
338 * decades B.C., this is the last year of the decade.
339 * @param part The part
340 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
341 * @return The last year in the part
343 public static int getPartialDecadeEndYear(int decade, Part part, Era era) {
345 era = Date.DEFAULT_ERA;
350 if (era == Era.BCE) {
351 if (part == Part.EARLY) {
352 endYear = decade + 7;
354 else if (part == Part.MIDDLE) {
355 endYear = decade + 4;
357 else if (part == Part.LATE) {
361 throw new IllegalArgumentException("unexpected part");
365 if (part == Part.EARLY) {
366 endYear = decade + 3;
368 else if (part == Part.MIDDLE) {
369 endYear = decade + 6;
371 else if (part == Part.LATE) {
372 endYear = decade + 9;
375 throw new IllegalArgumentException("unexpected part");
383 * Gets the Date representing the first day of a given decade.
385 * @param decade The decade, specified as a number ending in 0.
386 * For decades A.D., this is the first year of the decade. For
387 * decades B.C., this is the last year of the decade.
388 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
389 * @return The first day of the decade
391 public static Date getDecadeStartDate(int decade, Era era) {
393 era = Date.DEFAULT_ERA;
396 int startYear = getDecadeStartYear(decade, era);
398 return new Date(startYear, 1, 1, era);
402 * Gets the Date representing the last day of a given decade.
404 * @param decade The decade, specified as a number ending in 0.
405 * For decades A.D., this is the first year of the decade. For
406 * decades B.C., this is the last year of the decade.
407 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
408 * @return The last day of the decade
410 public static Date getDecadeEndDate(int decade, Era era) {
412 era = Date.DEFAULT_ERA;
415 int endYear = getDecadeEndYear(decade, era);
417 return new Date(endYear, 12, 31, era);
421 * Gets the first year of a given decade.
423 * @param decade The decade, specified as a number ending in 0.
424 * For decades A.D., this is the first year of the decade. For
425 * decades B.C., this is the last year of the decade.
426 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
427 * @return The first year of the decade
429 public static int getDecadeStartYear(int decade, Era era) {
431 era = Date.DEFAULT_ERA;
436 if (era == Era.BCE) {
437 startYear = decade + 9;
447 * Gets the last year of a given decade.
449 * @param decade The decade, specified as a number ending in 0.
450 * For decades A.D., this is the first year of the decade. For
451 * decades B.C., this is the last year of the decade.
452 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
453 * @return The last year of the decade
455 public static int getDecadeEndYear(int decade, Era era) {
457 era = Date.DEFAULT_ERA;
462 if (era == Era.BCE) {
466 endYear = decade + 9;
473 * Gets the Date representing the first day of a given century.
475 * @param century The century, specified as a number ending in 00 or 01.
476 * For centuries A.D., this is the first year of the century. For
477 * centuries B.C., this is the last year of the century. For example,
478 * the "21st century" would be specified as 2001, whereas the "2000's"
479 * would be specified as 2000. The "2nd century B.C." would be specified
480 * as 101. The "100's B.C." would be specified as 100.
481 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
482 * @return The first day of the century
484 public static Date getCenturyStartDate(int century, Era era) {
486 era = Date.DEFAULT_ERA;
489 int startYear = getCenturyStartYear(century, era);
491 return new Date(startYear, 1, 1, era);
495 * Gets the Date representing the last day of a given century.
497 * @param century The century, specified as a number ending in 00 or 01.
498 * For centuries A.D., this is the first year of the century. For
499 * centuries B.C., this is the last year of the century. For example,
500 * the "21st century" would be specified as 2001, whereas the "2000's"
501 * would be specified as 2000. The "2nd century B.C." would be specified
502 * as 101. The "100's B.C." would be specified as 100.
503 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
504 * @return The last day of the century
506 public static Date getCenturyEndDate(int century, Era era) {
508 era = Date.DEFAULT_ERA;
511 int endYear = getCenturyEndYear(century, era);
513 return new Date(endYear, 12, 31, era);
517 * Gets the first year of a given century.
519 * @param century The century, specified as a number ending in 00 or 01.
520 * For centuries A.D., this is the first year of the century. For
521 * centuries B.C., this is the last year of the century. For example,
522 * the "21st century" would be specified as 2001, whereas the "2000's"
523 * would be specified as 2000. The "2nd century B.C." would be specified
524 * as 101. The "100's B.C." would be specified as 100.
525 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
526 * @return The first year of the century
528 public static int getCenturyStartYear(int century, Era era) {
530 era = Date.DEFAULT_ERA;
535 if (era == Era.BCE) {
536 startYear = century + 99;
546 * Gets the last year of a given century.
548 * @param century The century, specified as a number ending in 00 or 01.
549 * For centuries A.D., this is the first year of the century. For
550 * centuries B.C., this is the last year of the century. For example,
551 * the "21st century" would be specified as 2001, whereas the "2000's"
552 * would be specified as 2000. The "2nd century B.C." would be specified
553 * as 101. The "100's B.C." would be specified as 100.
554 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
555 * @return The last year of the century
557 public static int getCenturyEndYear(int century, Era era) {
559 era = Date.DEFAULT_ERA;
564 if (era == Era.BCE) {
568 endYear = century + 99;
575 * Gets the Date representing the first day of a given partial century.
577 * @param century The century, specified as a number ending in 00 or 01.
578 * For centuries A.D., this is the first year of the century. For
579 * centuries B.C., this is the last year of the century. For example,
580 * the "21st century" would be specified as 2001, whereas the "2000's"
581 * would be specified as 2000. The "2nd century B.C." would be specified
582 * as 101. The "100's B.C." would be specified as 100.
583 * @param part The part
584 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
585 * @return The first day of the partial century
587 public static Date getPartialCenturyStartDate(int century, Part part, Era era) {
589 era = Date.DEFAULT_ERA;
592 int startYear = getPartialCenturyStartYear(century, part, era);
594 return new Date(startYear, 1, 1, era);
598 * Gets the Date representing the last day of a given partial century.
600 * @param century The century, specified as a number ending in 00 or 01.
601 * For centuries A.D., this is the first year of the century. For
602 * centuries B.C., this is the last year of the century. For example,
603 * the "21st century" would be specified as 2001, whereas the "2000's"
604 * would be specified as 2000. The "2nd century B.C." would be specified
605 * as 101. The "100's B.C." would be specified as 100.
606 * @param part The part
607 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
608 * @return The last day of the partial century
610 public static Date getPartialCenturyEndDate(int century, Part part, Era era) {
612 era = Date.DEFAULT_ERA;
615 int endYear = getPartialCenturyEndYear(century, part, era);
617 return new Date(endYear, 12, 31, era);
621 * Gets the first year of a given partial century.
623 * @param century The century, specified as a number ending in 00 or 01.
624 * For centuries A.D., this is the first year of the century. For
625 * centuries B.C., this is the last year of the century. For example,
626 * the "21st century" would be specified as 2001, whereas the "2000's"
627 * would be specified as 2000. The "2nd century B.C." would be specified
628 * as 101. The "100's B.C." would be specified as 100.
629 * @param part The part
630 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
631 * @return The first year of the partial century
633 public static int getPartialCenturyStartYear(int century, Part part, Era era) {
635 era = Date.DEFAULT_ERA;
640 if (era == Era.BCE) {
641 if (part == Part.EARLY) {
642 startYear = century + 99;
644 else if (part == Part.MIDDLE) {
645 startYear = century + 66;
647 else if (part == Part.LATE) {
648 startYear = century + 33;
651 throw new IllegalArgumentException("unexpected part");
655 if (part == Part.EARLY) {
658 else if (part == Part.MIDDLE) {
659 startYear = century + 33;
661 else if (part == Part.LATE) {
662 startYear = century + 66;
665 throw new IllegalArgumentException("unexpected part");
673 * Gets the last year of a given partial century.
675 * @param century The century, specified as a number ending in 00 or 01.
676 * For centuries A.D., this is the first year of the century. For
677 * centuries B.C., this is the last year of the century. For example,
678 * the "21st century" would be specified as 2001, whereas the "2000's"
679 * would be specified as 2000. The "2nd century B.C." would be specified
680 * as 101. The "100's B.C." would be specified as 100.
681 * @param part The part
682 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
683 * @return The last year of the partial century
685 public static int getPartialCenturyEndYear(int century, Part part, Era era) {
687 era = Date.DEFAULT_ERA;
692 if (era == Era.BCE) {
693 if (part == Part.EARLY) {
694 endYear = century + 66;
696 else if (part == Part.MIDDLE) {
697 endYear = century + 33;
699 else if (part == Part.LATE) {
703 throw new IllegalArgumentException("unexpected part");
707 if (part == Part.EARLY) {
708 endYear = century + 33;
710 else if (part == Part.MIDDLE) {
711 endYear = century + 66;
713 else if (part == Part.LATE) {
714 endYear = century + 99;
717 throw new IllegalArgumentException("unexpected part");
725 * Gets the Date representing the first day of a given half century.
727 * @param century The century, specified as a number ending in 00 or 01.
728 * For centuries A.D., this is the first year of the century. For
729 * centuries B.C., this is the last year of the century. For example,
730 * the "21st century" would be specified as 2001, whereas the "2000's"
731 * would be specified as 2000. The "2nd century B.C." would be specified
732 * as 101. The "100's B.C." would be specified as 100.
733 * @param half The half
734 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
735 * @return The first day of the half century
737 public static Date getHalfCenturyStartDate(int century, int half, Era era) {
739 era = Date.DEFAULT_ERA;
742 int startYear = getHalfCenturyStartYear(century, half, era);
744 return new Date(startYear, 1, 1, era);
748 * Gets the Date representing the last day of a given half century.
750 * @param century The century, specified as a number ending in 00 or 01.
751 * For centuries A.D., this is the first year of the century. For
752 * centuries B.C., this is the last year of the century. For example,
753 * the "21st century" would be specified as 2001, whereas the "2000's"
754 * would be specified as 2000. The "2nd century B.C." would be specified
755 * as 101. The "100's B.C." would be specified as 100.
756 * @param half The half
757 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
758 * @return The last day of the half century
760 public static Date getHalfCenturyEndDate(int century, int half, Era era) {
762 era = Date.DEFAULT_ERA;
765 int endYear = getHalfCenturyEndYear(century, half, era);
767 return new Date(endYear, 12, 31, era);
771 * Gets the first year of a given half century.
773 * @param century The century, specified as a number ending in 00 or 01.
774 * For centuries A.D., this is the first year of the century. For
775 * centuries B.C., this is the last year of the century. For example,
776 * the "21st century" would be specified as 2001, whereas the "2000's"
777 * would be specified as 2000. The "2nd century B.C." would be specified
778 * as 101. The "100's B.C." would be specified as 100.
779 * @param half The half
780 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
781 * @return The first year of the half century
783 public static int getHalfCenturyStartYear(int century, int half, Era era) {
785 era = Date.DEFAULT_ERA;
790 if (era == Era.BCE) {
791 startYear = (century + 99) - (50 * (half - 1));
794 startYear = century + (50 * (half - 1));
801 * Gets the last year of a given half century.
803 * @param century The century, specified as a number ending in 00 or 01.
804 * For centuries A.D., this is the first year of the century. For
805 * centuries B.C., this is the last year of the century. For example,
806 * the "21st century" would be specified as 2001, whereas the "2000's"
807 * would be specified as 2000. The "2nd century B.C." would be specified
808 * as 101. The "100's B.C." would be specified as 100.
809 * @param half The half
810 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
811 * @return The last year of the half century
813 public static int getHalfCenturyEndYear(int century, int half, Era era) {
815 era = Date.DEFAULT_ERA;
820 if (era == Era.BCE) {
821 endYear = (century + 99) - (50 * half) + 1;
824 endYear = century + (50 * half) - 1;
831 * Gets the Date representing the first day of a given quarter century.
833 * @param century The century, specified as a number ending in 00 or 01.
834 * For centuries A.D., this is the first year of the century. For
835 * centuries B.C., this is the last year of the century. For example,
836 * the "21st century" would be specified as 2001, whereas the "2000's"
837 * would be specified as 2000. The "2nd century B.C." would be specified
838 * as 101. The "100's B.C." would be specified as 100.
839 * @param quarter The quarter
840 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
841 * @return The first day of the quarter century
843 public static Date getQuarterCenturyStartDate(int century, int quarter, Era era) {
845 era = Date.DEFAULT_ERA;
848 int startYear = getQuarterCenturyStartYear(century, quarter, era);
850 return new Date(startYear, 1, 1, era);
854 * Gets the Date representing the last day of a given quarter century.
856 * @param century The century, specified as a number ending in 00 or 01.
857 * For centuries A.D., this is the first year of the century. For
858 * centuries B.C., this is the last year of the century. For example,
859 * the "21st century" would be specified as 2001, whereas the "2000's"
860 * would be specified as 2000. The "2nd century B.C." would be specified
861 * as 101. The "100's B.C." would be specified as 100.
862 * @param quarter The quarter
863 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
864 * @return The last day of the quarter century
866 public static Date getQuarterCenturyEndDate(int century, int quarter, Era era) {
868 era = Date.DEFAULT_ERA;
871 int endYear = getQuarterCenturyEndYear(century, quarter, era);
873 return new Date(endYear, 12, 31, era);
877 * Gets the first year of a given quarter century.
879 * @param century The century, specified as a number ending in 00 or 01.
880 * For centuries A.D., this is the first year of the century. For
881 * centuries B.C., this is the last year of the century. For example,
882 * the "21st century" would be specified as 2001, whereas the "2000's"
883 * would be specified as 2000. The "2nd century B.C." would be specified
884 * as 101. The "100's B.C." would be specified as 100.
885 * @param quarter The quarter
886 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
887 * @return The first year of the quarter century
889 public static int getQuarterCenturyStartYear(int century, int quarter, Era era) {
891 era = Date.DEFAULT_ERA;
896 if (era == Era.BCE) {
897 startYear = (century + 99) - (25 * (quarter - 1));
900 startYear = century + (25 * (quarter - 1));
907 * Gets the last year of a given quarter century.
909 * @param century The century, specified as a number ending in 00 or 01.
910 * For centuries A.D., this is the first year of the century. For
911 * centuries B.C., this is the last year of the century. For example,
912 * the "21st century" would be specified as 2001, whereas the "2000's"
913 * would be specified as 2000. The "2nd century B.C." would be specified
914 * as 101. The "100's B.C." would be specified as 100.
915 * @param quarter The quarter
916 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
917 * @return The last year of the quarter century
919 public static int getQuarterCenturyEndYear(int century, int quarter, Era era) {
921 era = Date.DEFAULT_ERA;
926 if (era == Era.BCE) {
927 endYear = (century + 99) - (25 * quarter) + 1;
930 endYear = century + (25 * quarter) - 1;
937 * Converts an nth century number to a year. For example, to convert "21st century"
938 * to a year, call nthCenturyToYear(21), which returns 2001. For centuries A.D., the
939 * year returned is the first year of the nth century. For centuries B.C., the
940 * year returned is the last year of the nth century.
942 * @param n The nth century number
943 * @return The first year in the nth century, for centuries A.D.
944 * The last year of the nth century, for centuries B.C.
946 public static int nthCenturyToYear(int n) {
947 int year = (n-1) * 100 + 1;
953 * Gets the Date representing the first day of a given millennium.
955 * @param n The nth millennium number
956 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
957 * @return The first day of the millennium
959 public static Date getMillenniumStartDate(int n, Era era) {
961 era = Date.DEFAULT_ERA;
964 int startYear = getMillenniumStartYear(n, era);
966 return new Date(startYear, 1, 1, era);
970 * Gets the Date representing the last day of a given millennium.
972 * @param n The nth millennium number
973 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
974 * @return The last day of the millennium
976 public static Date getMillenniumEndDate(int n, Era era) {
978 era = Date.DEFAULT_ERA;
981 int endYear = getMillenniumEndYear(n, era);
983 return new Date(endYear, 12, 31, era);
987 * Gets the first year of a given millennium.
989 * @param n The nth millennium number
990 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
991 * @return The first year of the millennium
993 public static int getMillenniumStartYear(int n, Era era) {
995 era = Date.DEFAULT_ERA;
1000 if (era == Era.BCE) {
1004 year = (n - 1) * 1000 + 1;
1011 * Gets the last year of a given millennium.
1013 * @param n The nth millennium number
1014 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
1015 * @return The last year of the millennium
1017 public static int getMillenniumEndYear(int n, Era era) {
1019 era = Date.DEFAULT_ERA;
1024 if (era == Era.BCE) {
1025 year = (n - 1) * 1000 + 1;
1035 * Calculates the earliest date that may be considered to be "before"
1038 * @param date The date
1039 * @return The earliest date "before" the date
1041 public static Date getEarliestBeforeDate(Date date) {
1042 return getEarliestBeforeDate(date, null);
1046 * Calculates the latest date that may be considered to be "after"
1049 * @param date The date
1050 * @return The latest date "after" the date
1052 public static Date getLatestAfterDate(Date date) {
1053 return getLatestAfterDate(date, null);
1057 * Calculates the earliest date that may be considered to be "before"
1058 * a given date range.
1060 * @param startDate The first date in the range
1061 * @param endDate The last date in the range
1062 * @return The earliest date "before" the range
1064 public static Date getEarliestBeforeDate(Date startDate, Date endDate) {
1069 // This algorithm is inherited from the XDB fuzzydate parser,
1070 // which considers "before" to mean "within a lifetime before".
1072 if (endDate == null) {
1073 endDate = startDate;
1076 int difference = getYearsBetween(startDate, endDate);
1078 Date earliestDate = startDate.copy();
1079 subtractYears(earliestDate, 1);
1080 earliestDate.setMonth(1);
1081 earliestDate.setDay(1);
1083 if (difference < 100) {
1084 // The comment from the XDB fuzzydate parser states:
1086 // Before/after years are really about birth/death dates
1087 // so we use average life-span of 75 years
1089 subtractYears(earliestDate, 75);
1092 // The comment from the XDB fuzzydate parser states:
1094 // Before/after years are really about birth/death dates
1095 // so we use average life-span of 75 years
1096 // but since the spec was a century, e.g. pre 20th century
1097 // we'll make the range a bit bigger
1100 subtractYears(earliestDate, 175);
1103 return earliestDate;
1108 * Calculates the latest date that may be considered to be "after"
1109 * a given date range.
1111 * @param startDate The first date in the range
1112 * @param endDate The last date in the range
1113 * @return The latest date "after" the range
1115 public static Date getLatestAfterDate(Date startDate, Date endDate) {
1120 public static int getYearsBetween(Date startDate, Date endDate) {
1121 if (startDate == null || endDate == null) {
1122 throw new InvalidDateException("date must not be null");
1125 Integer startYear = startDate.getYear();
1126 Integer endYear = endDate.getYear();
1128 if (startYear == null || endYear == null) {
1129 throw new IllegalArgumentException("year must not be null");
1132 Era startEra = startDate.getEra();
1133 Era endEra = endDate.getEra();
1135 if (startEra == null || endEra == null) {
1136 throw new IllegalArgumentException("era must not be null");
1139 MutableDateTime startDateTime = convertToDateTime(startDate);
1140 MutableDateTime endDateTime = convertToDateTime(endDate);
1142 int years = Years.yearsBetween(startDateTime, endDateTime).getYears();
1148 * Calculates the interval, in years, that should be padded around a date so
1149 * that any date within that interval may be considered to be "circa" the
1152 * @param year The year of the date
1153 * @param era The era of the date. If null, Date.DEFAULT_ERA is assumed.
1154 * @return The number of "circa" years before and after the date
1156 public static int getCircaIntervalYears(int year, Era era) {
1158 * This algorithm is inherited from the fuzzydate parser
1159 * in XDB. Its comment states:
1161 * We define circa year/century specifications offsets
1162 * as +/- 5% of the difference between that year/century
1163 * and the present (2100), so that the farther we go back
1164 * in time, the wider the range of meaning of "circa."
1169 era = Date.DEFAULT_ERA;
1172 MutableDateTime dateTime = new MutableDateTime(chronology);
1173 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1174 dateTime.yearOfEra().set(year);
1175 dateTime.monthOfYear().set(1);
1176 dateTime.dayOfMonth().set(1);
1177 dateTime.setTime(0, 0, 0, 0);
1179 int years = Years.yearsBetween(dateTime, circaBaseDateTime).getYears();
1181 return ((int) Math.round(years * 0.05));
1185 * Adds a number of days to a date.
1187 * @param date The date
1188 * @param days The number of days to add to the date
1190 public static void addDays(Date date, int days) {
1191 MutableDateTime dateTime = convertToDateTime(date);
1193 dateTime.add(Days.days(days));
1195 setFromDateTime(date, dateTime);
1199 * Adds a number of years to a date's year.
1201 * @param date The date
1202 * @param years The number of years to add to the date
1204 public static void addYears(Date date, int years) {
1205 MutableDateTime dateTime = convertToDateTime(date);
1207 dateTime.add(Years.years(years));
1209 setFromDateTime(date, dateTime);
1213 * Subtracts a number of years from a date's year.
1215 * @param date The date
1216 * @param years The number of years to subtract from the date
1218 public static void subtractYears(Date date, int years) {
1219 addYears(date, -years);
1222 public static String getEarliestTimestamp(Date date) {
1223 Era era = date.getEra();
1226 era = Date.DEFAULT_ERA;
1229 MutableDateTime dateTime = null;
1232 dateTime = convertToDateTime(date);
1234 catch(IllegalFieldValueException e) {
1235 throw new InvalidDateException(e.getMessage());
1238 String scalarDate = scalarDateFormatter.print(dateTime);
1243 public static String getLatestTimestamp(Date date) {
1244 Era era = date.getEra();
1247 era = Date.DEFAULT_ERA;
1250 MutableDateTime dateTime = null;
1253 dateTime = convertToDateTime(date);
1255 catch(IllegalFieldValueException e) {
1256 throw new InvalidDateException(e.getMessage());
1259 dateTime.setTime(23, 59, 59, 999);
1261 String scalarDate = scalarDateFormatter.print(dateTime);
1266 public static boolean isValidDate(int year, int month, int day, Era era) {
1267 boolean isValid = true;
1270 convertToDateTime(new Date(year, month,day, era));
1272 catch(IllegalFieldValueException e) {
1280 * Converts a Date to a joda-time DateTime.
1282 * @param date The Date
1283 * @return A MutableDateTime representing the same date
1285 private static MutableDateTime convertToDateTime(Date date) {
1286 Era era = date.getEra();
1289 era = Date.DEFAULT_ERA;
1292 MutableDateTime dateTime = new MutableDateTime(chronology);
1293 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1294 dateTime.yearOfEra().set(date.getYear());
1295 dateTime.monthOfYear().set(date.getMonth());
1296 dateTime.dayOfMonth().set(date.getDay());
1297 dateTime.setTime(0, 0, 0, 0);
1303 * Sets the fields in a Date so that it represents the same date
1304 * as a given DateTime.
1306 * @param date The Date to set
1307 * @param dateTime A MutableDateTime representing the desired date
1309 private static void setFromDateTime(Date date, MutableDateTime dateTime) {
1310 date.setYear(dateTime.getYearOfEra());
1311 date.setMonth(dateTime.getMonthOfYear());
1312 date.setDay(dateTime.getDayOfMonth());
1313 date.setEra((dateTime.getEra() == DateTimeConstants.BC) ? Era.BCE : Era.CE);