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;
13 import org.joda.time.LocalDate;
15 public class DateUtils {
16 private static final DateTimeFormatter monthFormatter = DateTimeFormat.forPattern("MMMM");
17 private static final DateTimeFormatter scalarDateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
19 // The chronology to use for date calculations, which are done using the joda-time library.
20 // See http://www.joda.org/joda-time/apidocs/org/joda/time/Chronology.html for descriptions of
21 // the chronologies supported by joda-time.
23 // GJChronology (http://www.joda.org/joda-time/apidocs/org/joda/time/chrono/GJChronology.html)
24 // seems best for representing a mix of modern and historical dates, as might be seen by an
25 // anthropology museum.
27 private static final Chronology chronology = GJChronology.getInstance();
29 // Define the DateTime that serves as the basis for circa calculations, using the algorithm
30 // ported from the XDB date parser. Its comment states:
32 // We define circa year/century specifications offsets
33 // as +/- 5% of the difference between that year/century
34 // and the present (2100), so that the farther we go back
35 // in time, the wider the range of meaning of "circa."
37 private static final DateTime circaBaseDateTime = new DateTime(2100, 12, 31, 0, 0, 0, 0, chronology);
40 * Gets the number (1-12) of a month for a given name.
42 * @param monthName The name of the month
43 * @return The number of the month, between 1 and 12
45 public static int getMonthByName(String monthName) {
46 // Normalize "sept" to "sep", since DateTimeFormat doesn't
47 // understand the former.
49 if (monthName.equals("sept")) {
53 return monthFormatter.parseDateTime(monthName).getMonthOfYear();
57 * Gets the number of days in a given month.
59 * @param month The month number, between 1 and 12
60 * @param year The year (in order to account for leap years)
61 * @return The number of days in the month
63 public static int getDaysInMonth(int month, int year, Era era) {
65 era = Date.DEFAULT_ERA;
68 DateTime dateTime = new DateTime(chronology)
69 .withEra((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD)
71 .withMonthOfYear(month);
73 return dateTime.dayOfMonth().getMaximumValue();
77 * Gets the Date representing the first day of a given quarter year.
79 * @param year The year
80 * @param quarter The quarter, between 1 and 4
81 * @return The first day of the quarter year
83 public static Date getQuarterYearStartDate(int quarter, int year) {
84 int startMonth = getQuarterYearStartMonth(quarter);
86 return new Date(year, startMonth, 1);
90 * Gets the Date representing the last day of a given quarter year.
92 * @param year The year
93 * @param quarter The quarter, between 1 and 4
94 * @return The last day of the quarter year
96 public static Date getQuarterYearEndDate(int quarter, int year, Era era) {
97 int endMonth = getQuarterYearEndMonth(quarter);
99 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
103 * Gets the first month of a given quarter in a year.
105 * @param quarter The quarter, between 1 and 4
106 * @return The number of the first month in the quarter
108 public static int getQuarterYearStartMonth(int quarter) {
109 return ((3 * (quarter-1)) + 1);
113 * Gets the last month of a given quarter in a year.
115 * @param quarter The quarter, between 1 and 4
116 * @return The number of the last month in the quarter
118 public static int getQuarterYearEndMonth(int quarter) {
119 return (getQuarterYearStartMonth(quarter) + 2);
123 * Gets the Date representing the first day of a given half year.
125 * @param year The year
126 * @param half The half, between 1 and 2
127 * @return The first day of the half year
129 public static Date getHalfYearStartDate(int half, int year) {
130 int startMonth = getHalfYearStartMonth(half);
132 return new Date(year, startMonth, 1);
137 * Gets the Date representing the last day of a given half year.
139 * @param year The year
140 * @param half The half, between 1 and 2
141 * @return The last day of the half year
143 public static Date getHalfYearEndDate(int half, int year, Era era) {
144 int endMonth = getHalfYearEndMonth(half);
146 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
150 * Gets the first month of a given half in a year.
152 * @param half The half, between 1 and 2
153 * @return The number of the first month in the half
155 public static int getHalfYearStartMonth(int half) {
156 return ((6 * (half-1)) + 1);
160 * Gets the last month of a given half in a year.
162 * @param half The half, between 1 and 2
163 * @return The number of the last month in the half
165 public static int getHalfYearEndMonth(int half) {
166 return (getHalfYearStartMonth(half) + 5);
170 * Gets the Date representing the first day of a given partial year.
172 * @param year The year
173 * @param part The part
174 * @return The first day of the partial year
176 public static Date getPartialYearStartDate(Part part, int year) {
177 int startMonth = getPartialYearStartMonth(part);
179 return new Date(year, startMonth, 1);
183 * Gets the Date representing the last day of a given partial year.
185 * @param year The year
186 * @param part The part
187 * @return The last day of the partial year
189 public static Date getPartialYearEndDate(Part part, int year, Era era) {
190 int endMonth = getPartialYearEndMonth(part);
192 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
196 * Gets the first month of a given part of a year.
198 * @param part The part
199 * @return The number of the first month in the part
201 public static int getPartialYearStartMonth(Part part) {
204 if (part == Part.EARLY) {
207 else if (part == Part.MIDDLE) {
210 else if (part == Part.LATE) {
214 throw new IllegalArgumentException("unexpected part");
221 * Gets the last month of a given part of a year.
223 * @param part The part
224 * @return The number of the last month in the part
226 public static int getPartialYearEndMonth(Part part) {
229 if (part == Part.EARLY) {
232 else if (part == Part.MIDDLE) {
235 else if (part == Part.LATE) {
239 throw new IllegalArgumentException("unexpected part");
246 * Gets the Date representing the first day of a given partial decade.
248 * @param decade The decade, specified as a number ending in 0.
249 * For decades A.D., this is the first year of the decade. For
250 * decades B.C., this is the last year of the decade.
251 * @param part The part
252 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
253 * @return The first day of the partial decade
255 public static Date getPartialDecadeStartDate(int decade, Part part, Era era) {
257 era = Date.DEFAULT_ERA;
260 int startYear = getPartialDecadeStartYear(decade, part, era);
262 return new Date(startYear, 1, 1, era);
266 * Gets the Date representing the last day of a given partial decade.
268 * @param decade The decade, specified as a number ending in 0.
269 * For decades A.D., this is the first year of the decade. For
270 * decades B.C., this is the last year of the decade.
271 * @param part The part
272 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
273 * @return The last day of the partial decade
275 public static Date getPartialDecadeEndDate(int decade, Part part, Era era) {
277 era = Date.DEFAULT_ERA;
280 int endYear = getPartialDecadeEndYear(decade, part, era);
282 return new Date(endYear, 12, 31, era);
286 * Gets the first year of a given part of a decade.
288 * @param decade The decade, specified as a number ending in 0.
289 * For decades A.D., this is the first year of the decade. For
290 * decades B.C., this is the last year of the decade.
291 * @param part The part
292 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
293 * @return The first year in the part
295 public static int getPartialDecadeStartYear(int decade, Part part, Era era) {
297 era = Date.DEFAULT_ERA;
302 if (era == Era.BCE) {
303 if (part == Part.EARLY) {
304 startYear = decade + 9;
306 else if (part == Part.MIDDLE) {
307 startYear = decade + 6;
309 else if (part == Part.LATE) {
310 startYear = decade + 3;
313 throw new IllegalArgumentException("unexpected part");
317 if (part == Part.EARLY) {
320 else if (part == Part.MIDDLE) {
321 startYear = decade + 4;
323 else if (part == Part.LATE) {
324 startYear = decade + 7;
327 throw new IllegalArgumentException("unexpected part");
335 * Gets the last year of a given part of a decade.
337 * @param decade The decade, specified as a number ending in 0.
338 * For decades A.D., this is the first year of the decade. For
339 * decades B.C., this is the last year of the decade.
340 * @param part The part
341 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
342 * @return The last year in the part
344 public static int getPartialDecadeEndYear(int decade, Part part, Era era) {
346 era = Date.DEFAULT_ERA;
351 if (era == Era.BCE) {
352 if (part == Part.EARLY) {
353 endYear = decade + 7;
355 else if (part == Part.MIDDLE) {
356 endYear = decade + 4;
358 else if (part == Part.LATE) {
362 throw new IllegalArgumentException("unexpected part");
366 if (part == Part.EARLY) {
367 endYear = decade + 3;
369 else if (part == Part.MIDDLE) {
370 endYear = decade + 6;
372 else if (part == Part.LATE) {
373 endYear = decade + 9;
376 throw new IllegalArgumentException("unexpected part");
384 * Gets the Date representing the first day of a given decade.
386 * @param decade The decade, specified as a number ending in 0.
387 * For decades A.D., this is the first year of the decade. For
388 * decades B.C., this is the last year of the decade.
389 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
390 * @return The first day of the decade
392 public static Date getDecadeStartDate(int decade, Era era) {
394 era = Date.DEFAULT_ERA;
397 int startYear = getDecadeStartYear(decade, era);
399 return new Date(startYear, 1, 1, era);
403 * Gets the Date representing the last day of a given decade.
405 * @param decade The decade, specified as a number ending in 0.
406 * For decades A.D., this is the first year of the decade. For
407 * decades B.C., this is the last year of the decade.
408 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
409 * @return The last day of the decade
411 public static Date getDecadeEndDate(int decade, Era era) {
413 era = Date.DEFAULT_ERA;
416 int endYear = getDecadeEndYear(decade, era);
418 return new Date(endYear, 12, 31, era);
422 * Gets the first year of a given decade.
424 * @param decade The decade, specified as a number ending in 0.
425 * For decades A.D., this is the first year of the decade. For
426 * decades B.C., this is the last year of the decade.
427 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
428 * @return The first year of the decade
430 public static int getDecadeStartYear(int decade, Era era) {
432 era = Date.DEFAULT_ERA;
437 if (era == Era.BCE) {
438 startYear = decade + 9;
448 * Gets the last year of a given decade.
450 * @param decade The decade, specified as a number ending in 0.
451 * For decades A.D., this is the first year of the decade. For
452 * decades B.C., this is the last year of the decade.
453 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
454 * @return The last year of the decade
456 public static int getDecadeEndYear(int decade, Era era) {
458 era = Date.DEFAULT_ERA;
463 if (era == Era.BCE) {
467 endYear = decade + 9;
474 * Gets the Date representing the first day of a given century.
476 * @param century The century, specified as a number ending in 00 or 01.
477 * For centuries A.D., this is the first year of the century. For
478 * centuries B.C., this is the last year of the century. For example,
479 * the "21st century" would be specified as 2001, whereas the "2000's"
480 * would be specified as 2000. The "2nd century B.C." would be specified
481 * as 101. The "100's B.C." would be specified as 100.
482 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
483 * @return The first day of the century
485 public static Date getCenturyStartDate(int century, Era era) {
487 era = Date.DEFAULT_ERA;
490 int startYear = getCenturyStartYear(century, era);
492 return new Date(startYear, 1, 1, era);
496 * Gets the Date representing the last day of a given century.
498 * @param century The century, specified as a number ending in 00 or 01.
499 * For centuries A.D., this is the first year of the century. For
500 * centuries B.C., this is the last year of the century. For example,
501 * the "21st century" would be specified as 2001, whereas the "2000's"
502 * would be specified as 2000. The "2nd century B.C." would be specified
503 * as 101. The "100's B.C." would be specified as 100.
504 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
505 * @return The last day of the century
507 public static Date getCenturyEndDate(int century, Era era) {
509 era = Date.DEFAULT_ERA;
512 int endYear = getCenturyEndYear(century, era);
514 return new Date(endYear, 12, 31, era);
518 * Gets the first year of a given century.
520 * @param century The century, specified as a number ending in 00 or 01.
521 * For centuries A.D., this is the first year of the century. For
522 * centuries B.C., this is the last year of the century. For example,
523 * the "21st century" would be specified as 2001, whereas the "2000's"
524 * would be specified as 2000. The "2nd century B.C." would be specified
525 * as 101. The "100's B.C." would be specified as 100.
526 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
527 * @return The first year of the century
529 public static int getCenturyStartYear(int century, Era era) {
531 era = Date.DEFAULT_ERA;
536 if (era == Era.BCE) {
537 startYear = century + 99;
547 * Gets the last year of a given century.
549 * @param century The century, specified as a number ending in 00 or 01.
550 * For centuries A.D., this is the first year of the century. For
551 * centuries B.C., this is the last year of the century. For example,
552 * the "21st century" would be specified as 2001, whereas the "2000's"
553 * would be specified as 2000. The "2nd century B.C." would be specified
554 * as 101. The "100's B.C." would be specified as 100.
555 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
556 * @return The last year of the century
558 public static int getCenturyEndYear(int century, Era era) {
560 era = Date.DEFAULT_ERA;
565 if (era == Era.BCE) {
569 endYear = century + 99;
576 * Gets the Date representing the first day of a given partial century.
578 * @param century The century, specified as a number ending in 00 or 01.
579 * For centuries A.D., this is the first year of the century. For
580 * centuries B.C., this is the last year of the century. For example,
581 * the "21st century" would be specified as 2001, whereas the "2000's"
582 * would be specified as 2000. The "2nd century B.C." would be specified
583 * as 101. The "100's B.C." would be specified as 100.
584 * @param part The part
585 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
586 * @return The first day of the partial century
588 public static Date getPartialCenturyStartDate(int century, Part part, Era era) {
590 era = Date.DEFAULT_ERA;
593 int startYear = getPartialCenturyStartYear(century, part, era);
595 return new Date(startYear, 1, 1, era);
599 * Gets the Date representing the last day of a given partial century.
601 * @param century The century, specified as a number ending in 00 or 01.
602 * For centuries A.D., this is the first year of the century. For
603 * centuries B.C., this is the last year of the century. For example,
604 * the "21st century" would be specified as 2001, whereas the "2000's"
605 * would be specified as 2000. The "2nd century B.C." would be specified
606 * as 101. The "100's B.C." would be specified as 100.
607 * @param part The part
608 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
609 * @return The last day of the partial century
611 public static Date getPartialCenturyEndDate(int century, Part part, Era era) {
613 era = Date.DEFAULT_ERA;
616 int endYear = getPartialCenturyEndYear(century, part, era);
618 return new Date(endYear, 12, 31, era);
622 * Gets the first year of a given partial century.
624 * @param century The century, specified as a number ending in 00 or 01.
625 * For centuries A.D., this is the first year of the century. For
626 * centuries B.C., this is the last year of the century. For example,
627 * the "21st century" would be specified as 2001, whereas the "2000's"
628 * would be specified as 2000. The "2nd century B.C." would be specified
629 * as 101. The "100's B.C." would be specified as 100.
630 * @param part The part
631 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
632 * @return The first year of the partial century
634 public static int getPartialCenturyStartYear(int century, Part part, Era era) {
636 era = Date.DEFAULT_ERA;
641 if (era == Era.BCE) {
642 if (part == Part.EARLY) {
643 startYear = century + 99;
645 else if (part == Part.MIDDLE) {
646 startYear = century + 66;
648 else if (part == Part.LATE) {
649 startYear = century + 33;
652 throw new IllegalArgumentException("unexpected part");
656 if (part == Part.EARLY) {
659 else if (part == Part.MIDDLE) {
660 startYear = century + 33;
662 else if (part == Part.LATE) {
663 startYear = century + 66;
666 throw new IllegalArgumentException("unexpected part");
674 * Gets the last year of a given partial century.
676 * @param century The century, specified as a number ending in 00 or 01.
677 * For centuries A.D., this is the first year of the century. For
678 * centuries B.C., this is the last year of the century. For example,
679 * the "21st century" would be specified as 2001, whereas the "2000's"
680 * would be specified as 2000. The "2nd century B.C." would be specified
681 * as 101. The "100's B.C." would be specified as 100.
682 * @param part The part
683 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
684 * @return The last year of the partial century
686 public static int getPartialCenturyEndYear(int century, Part part, Era era) {
688 era = Date.DEFAULT_ERA;
693 if (era == Era.BCE) {
694 if (part == Part.EARLY) {
695 endYear = century + 66;
697 else if (part == Part.MIDDLE) {
698 endYear = century + 33;
700 else if (part == Part.LATE) {
704 throw new IllegalArgumentException("unexpected part");
708 if (part == Part.EARLY) {
709 endYear = century + 33;
711 else if (part == Part.MIDDLE) {
712 endYear = century + 66;
714 else if (part == Part.LATE) {
715 endYear = century + 99;
718 throw new IllegalArgumentException("unexpected part");
726 * Gets the Date representing the first day of a given half century.
728 * @param century The century, specified as a number ending in 00 or 01.
729 * For centuries A.D., this is the first year of the century. For
730 * centuries B.C., this is the last year of the century. For example,
731 * the "21st century" would be specified as 2001, whereas the "2000's"
732 * would be specified as 2000. The "2nd century B.C." would be specified
733 * as 101. The "100's B.C." would be specified as 100.
734 * @param half The half
735 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
736 * @return The first day of the half century
738 public static Date getHalfCenturyStartDate(int century, int half, Era era) {
740 era = Date.DEFAULT_ERA;
743 int startYear = getHalfCenturyStartYear(century, half, era);
745 return new Date(startYear, 1, 1, era);
749 * Gets the Date representing the last day of a given half century.
751 * @param century The century, specified as a number ending in 00 or 01.
752 * For centuries A.D., this is the first year of the century. For
753 * centuries B.C., this is the last year of the century. For example,
754 * the "21st century" would be specified as 2001, whereas the "2000's"
755 * would be specified as 2000. The "2nd century B.C." would be specified
756 * as 101. The "100's B.C." would be specified as 100.
757 * @param half The half
758 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
759 * @return The last day of the half century
761 public static Date getHalfCenturyEndDate(int century, int half, Era era) {
763 era = Date.DEFAULT_ERA;
766 int endYear = getHalfCenturyEndYear(century, half, era);
768 return new Date(endYear, 12, 31, era);
772 * Gets the first year of a given half century.
774 * @param century The century, specified as a number ending in 00 or 01.
775 * For centuries A.D., this is the first year of the century. For
776 * centuries B.C., this is the last year of the century. For example,
777 * the "21st century" would be specified as 2001, whereas the "2000's"
778 * would be specified as 2000. The "2nd century B.C." would be specified
779 * as 101. The "100's B.C." would be specified as 100.
780 * @param half The half
781 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
782 * @return The first year of the half century
784 public static int getHalfCenturyStartYear(int century, int half, Era era) {
786 era = Date.DEFAULT_ERA;
791 if (era == Era.BCE) {
792 startYear = (century + 99) - (50 * (half - 1));
795 startYear = century + (50 * (half - 1));
802 * Gets the last year of a given half century.
804 * @param century The century, specified as a number ending in 00 or 01.
805 * For centuries A.D., this is the first year of the century. For
806 * centuries B.C., this is the last year of the century. For example,
807 * the "21st century" would be specified as 2001, whereas the "2000's"
808 * would be specified as 2000. The "2nd century B.C." would be specified
809 * as 101. The "100's B.C." would be specified as 100.
810 * @param half The half
811 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
812 * @return The last year of the half century
814 public static int getHalfCenturyEndYear(int century, int half, Era era) {
816 era = Date.DEFAULT_ERA;
821 if (era == Era.BCE) {
822 endYear = (century + 99) - (50 * half) + 1;
825 endYear = century + (50 * half) - 1;
832 * Gets the Date representing the first day of a given quarter century.
834 * @param century The century, specified as a number ending in 00 or 01.
835 * For centuries A.D., this is the first year of the century. For
836 * centuries B.C., this is the last year of the century. For example,
837 * the "21st century" would be specified as 2001, whereas the "2000's"
838 * would be specified as 2000. The "2nd century B.C." would be specified
839 * as 101. The "100's B.C." would be specified as 100.
840 * @param quarter The quarter
841 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
842 * @return The first day of the quarter century
844 public static Date getQuarterCenturyStartDate(int century, int quarter, Era era) {
846 era = Date.DEFAULT_ERA;
849 int startYear = getQuarterCenturyStartYear(century, quarter, era);
851 return new Date(startYear, 1, 1, era);
855 * Gets the Date representing the last day of a given quarter century.
857 * @param century The century, specified as a number ending in 00 or 01.
858 * For centuries A.D., this is the first year of the century. For
859 * centuries B.C., this is the last year of the century. For example,
860 * the "21st century" would be specified as 2001, whereas the "2000's"
861 * would be specified as 2000. The "2nd century B.C." would be specified
862 * as 101. The "100's B.C." would be specified as 100.
863 * @param quarter The quarter
864 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
865 * @return The last day of the quarter century
867 public static Date getQuarterCenturyEndDate(int century, int quarter, Era era) {
869 era = Date.DEFAULT_ERA;
872 int endYear = getQuarterCenturyEndYear(century, quarter, era);
874 return new Date(endYear, 12, 31, era);
878 * Gets the first year of a given quarter century.
880 * @param century The century, specified as a number ending in 00 or 01.
881 * For centuries A.D., this is the first year of the century. For
882 * centuries B.C., this is the last year of the century. For example,
883 * the "21st century" would be specified as 2001, whereas the "2000's"
884 * would be specified as 2000. The "2nd century B.C." would be specified
885 * as 101. The "100's B.C." would be specified as 100.
886 * @param quarter The quarter
887 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
888 * @return The first year of the quarter century
890 public static int getQuarterCenturyStartYear(int century, int quarter, Era era) {
892 era = Date.DEFAULT_ERA;
897 if (era == Era.BCE) {
898 startYear = (century + 99) - (25 * (quarter - 1));
901 startYear = century + (25 * (quarter - 1));
908 * Gets the last year of a given quarter century.
910 * @param century The century, specified as a number ending in 00 or 01.
911 * For centuries A.D., this is the first year of the century. For
912 * centuries B.C., this is the last year of the century. For example,
913 * the "21st century" would be specified as 2001, whereas the "2000's"
914 * would be specified as 2000. The "2nd century B.C." would be specified
915 * as 101. The "100's B.C." would be specified as 100.
916 * @param quarter The quarter
917 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
918 * @return The last year of the quarter century
920 public static int getQuarterCenturyEndYear(int century, int quarter, Era era) {
922 era = Date.DEFAULT_ERA;
927 if (era == Era.BCE) {
928 endYear = (century + 99) - (25 * quarter) + 1;
931 endYear = century + (25 * quarter) - 1;
938 * Converts an nth century number to a year. For example, to convert "21st century"
939 * to a year, call nthCenturyToYear(21), which returns 2001. For centuries A.D., the
940 * year returned is the first year of the nth century. For centuries B.C., the
941 * year returned is the last year of the nth century.
943 * @param n The nth century number
944 * @return The first year in the nth century, for centuries A.D.
945 * The last year of the nth century, for centuries B.C.
947 public static int nthCenturyToYear(int n) {
948 int year = (n-1) * 100 + 1;
954 * Gets the Date representing the first day of a given millennium.
956 * @param n The nth millennium number
957 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
958 * @return The first day of the millennium
960 public static Date getMillenniumStartDate(int n, Era era) {
962 era = Date.DEFAULT_ERA;
965 int startYear = getMillenniumStartYear(n, era);
967 return new Date(startYear, 1, 1, era);
971 * Gets the Date representing the last day of a given millennium.
973 * @param n The nth millennium number
974 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
975 * @return The last day of the millennium
977 public static Date getMillenniumEndDate(int n, Era era) {
979 era = Date.DEFAULT_ERA;
982 int endYear = getMillenniumEndYear(n, era);
984 return new Date(endYear, 12, 31, era);
988 * Gets the first year of a given millennium.
990 * @param n The nth millennium number
991 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
992 * @return The first year of the millennium
994 public static int getMillenniumStartYear(int n, Era era) {
996 era = Date.DEFAULT_ERA;
1001 if (era == Era.BCE) {
1005 year = (n - 1) * 1000 + 1;
1012 * Gets the last year of a given millennium.
1014 * @param n The nth millennium number
1015 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
1016 * @return The last year of the millennium
1018 public static int getMillenniumEndYear(int n, Era era) {
1020 era = Date.DEFAULT_ERA;
1025 if (era == Era.BCE) {
1026 year = (n - 1) * 1000 + 1;
1036 * Calculates the earliest date that may be considered to be "before"
1039 * @param date The date
1040 * @return The earliest date "before" the date
1042 public static Date getEarliestBeforeDate(Date date) {
1043 return getEarliestBeforeDate(date, null);
1047 * Calculates the latest date that may be considered to be "after"
1050 * @param date The date
1051 * @return The latest date "after" the date
1053 public static Date getLatestAfterDate(Date date) {
1054 return getLatestAfterDate(date, null);
1058 * Calculates the earliest date that may be considered to be "before"
1059 * a given date range.
1061 * @param startDate The first date in the range
1062 * @param endDate The last date in the range
1063 * @return The earliest date "before" the range
1065 public static Date getEarliestBeforeDate(Date startDate, Date endDate) {
1067 // Return an empty date to be used in before date cases
1071 // This algorithm is inherited from the XDB fuzzydate parser,
1072 // which considers "before" to mean "within a lifetime before".
1074 if (endDate == null) {
1075 endDate = startDate;
1078 int difference = getYearsBetween(startDate, endDate);
1080 Date earliestDate = startDate.copy();
1081 subtractYears(earliestDate, 1);
1082 earliestDate.setMonth(1);
1083 earliestDate.setDay(1);
1085 if (difference < 100) {
1086 // The comment from the XDB fuzzydate parser states:
1088 // Before/after years are really about birth/death dates
1089 // so we use average life-span of 75 years
1091 subtractYears(earliestDate, 75);
1094 // The comment from the XDB fuzzydate parser states:
1096 // Before/after years are really about birth/death dates
1097 // so we use average life-span of 75 years
1098 // but since the spec was a century, e.g. pre 20th century
1099 // we'll make the range a bit bigger
1102 subtractYears(earliestDate, 175);
1105 return earliestDate;
1110 * Calculates the latest date that may be considered to be "after"
1111 * a given date range. We define "after" as the current date.
1113 * @param startDate The first date in the range
1114 * @param endDate The last date in the range
1115 * @return The latest date "after" the range
1117 public static Date getLatestAfterDate(Date startDate, Date endDate) {
1119 LocalDate localDate = new LocalDate();
1120 Integer year = (Integer) localDate.getYear();
1121 Integer month = (Integer) localDate.getMonthOfYear();
1122 Integer dayOfMonth = (Integer) localDate.getDayOfMonth();
1123 return new Date(year, month, dayOfMonth, Date.DEFAULT_ERA);
1127 public static int getYearsBetween(Date startDate, Date endDate) {
1128 if (startDate == null || endDate == null) {
1129 throw new InvalidDateException("date must not be null");
1132 Integer startYear = startDate.getYear();
1133 Integer endYear = endDate.getYear();
1135 if (startYear == null || endYear == null) {
1136 throw new IllegalArgumentException("year must not be null");
1139 Era startEra = startDate.getEra();
1140 Era endEra = endDate.getEra();
1142 if (startEra == null || endEra == null) {
1143 throw new IllegalArgumentException("era must not be null");
1146 MutableDateTime startDateTime = convertToDateTime(startDate);
1147 MutableDateTime endDateTime = convertToDateTime(endDate);
1149 int years = Years.yearsBetween(startDateTime, endDateTime).getYears();
1155 * Calculates the interval, in years, that should be padded around a date so
1156 * that any date within that interval may be considered to be "circa" the
1159 * @param year The year of the date
1160 * @param era The era of the date. If null, Date.DEFAULT_ERA is assumed.
1161 * @return The number of "circa" years before and after the date
1163 public static int getCircaIntervalYears(int year, Era era) {
1165 * This algorithm is inherited from the fuzzydate parser
1166 * in XDB. Its comment states:
1168 * We define circa year/century specifications offsets
1169 * as +/- 5% of the difference between that year/century
1170 * and the present (2100), so that the farther we go back
1171 * in time, the wider the range of meaning of "circa."
1176 era = Date.DEFAULT_ERA;
1179 MutableDateTime dateTime = new MutableDateTime(chronology);
1180 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1181 dateTime.yearOfEra().set(year);
1182 dateTime.monthOfYear().set(1);
1183 dateTime.dayOfMonth().set(1);
1184 dateTime.setTime(0, 0, 0, 0);
1186 int years = Years.yearsBetween(dateTime, circaBaseDateTime).getYears();
1188 return ((int) Math.round(years * 0.05));
1192 * Adds a number of days to a date.
1194 * @param date The date
1195 * @param days The number of days to add to the date
1197 public static void addDays(Date date, int days) {
1198 MutableDateTime dateTime = convertToDateTime(date);
1200 dateTime.add(Days.days(days));
1202 setFromDateTime(date, dateTime);
1206 * Adds a number of years to a date's year.
1208 * @param date The date
1209 * @param years The number of years to add to the date
1211 public static void addYears(Date date, int years) {
1212 MutableDateTime dateTime = convertToDateTime(date);
1214 dateTime.add(Years.years(years));
1216 setFromDateTime(date, dateTime);
1220 * Subtracts a number of years from a date's year.
1222 * @param date The date
1223 * @param years The number of years to subtract from the date
1225 public static void subtractYears(Date date, int years) {
1226 addYears(date, -years);
1229 public static String getEarliestTimestamp(Date date) {
1230 Era era = date.getEra();
1233 era = Date.DEFAULT_ERA;
1236 MutableDateTime dateTime = null;
1239 dateTime = convertToDateTime(date);
1241 catch(IllegalFieldValueException e) {
1242 throw new InvalidDateException(e.getMessage());
1245 String scalarDate = scalarDateFormatter.print(dateTime);
1250 public static String getLatestTimestamp(Date date) {
1251 Era era = date.getEra();
1254 era = Date.DEFAULT_ERA;
1257 MutableDateTime dateTime = null;
1260 dateTime = convertToDateTime(date);
1262 catch(IllegalFieldValueException e) {
1263 throw new InvalidDateException(e.getMessage());
1266 dateTime.setTime(23, 59, 59, 999);
1268 String scalarDate = scalarDateFormatter.print(dateTime);
1273 public static boolean isValidDate(int year, int month, int day, Era era) {
1274 boolean isValid = true;
1277 convertToDateTime(new Date(year, month,day, era));
1279 catch(IllegalFieldValueException e) {
1287 * Converts a Date to a joda-time DateTime.
1289 * @param date The Date
1290 * @return A MutableDateTime representing the same date
1292 private static MutableDateTime convertToDateTime(Date date) {
1293 Era era = date.getEra();
1296 era = Date.DEFAULT_ERA;
1299 MutableDateTime dateTime = new MutableDateTime(chronology);
1300 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1301 dateTime.yearOfEra().set(date.getYear());
1302 dateTime.monthOfYear().set(date.getMonth());
1303 dateTime.dayOfMonth().set(date.getDay());
1304 dateTime.setTime(0, 0, 0, 0);
1310 * Sets the fields in a Date so that it represents the same date
1311 * as a given DateTime.
1313 * @param date The Date to set
1314 * @param dateTime A MutableDateTime representing the desired date
1316 private static void setFromDateTime(Date date, MutableDateTime dateTime) {
1317 date.setYear(dateTime.getYearOfEra());
1318 date.setMonth(dateTime.getMonthOfYear());
1319 date.setDay(dateTime.getDayOfMonth());
1320 date.setEra((dateTime.getEra() == DateTimeConstants.BC) ? Era.BCE : Era.CE);