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.LocalDate;
9 import org.joda.time.MutableDateTime;
10 import org.joda.time.Years;
11 import org.joda.time.chrono.GJChronology;
12 import org.joda.time.format.DateTimeFormat;
13 import org.joda.time.format.DateTimeFormatter;
15 public class DateUtils {
16 private static final DateTimeFormatter monthFormatter = DateTimeFormat.forPattern("MMMM");
17 private static final DateTimeFormatter scalarValueFormatter = DateTimeFormat.forPattern("yyyy-MM-dd");
18 private static final DateTimeFormatter timestampFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
20 // The chronology to use for date calculations, which are done using the joda-time library.
21 // See http://www.joda.org/joda-time/apidocs/org/joda/time/Chronology.html for descriptions of
22 // the chronologies supported by joda-time.
24 // GJChronology (http://www.joda.org/joda-time/apidocs/org/joda/time/chrono/GJChronology.html)
25 // seems best for representing a mix of modern and historical dates, as might be seen by an
26 // anthropology museum.
28 private static final Chronology chronology = GJChronology.getInstance();
30 // Define the DateTime that serves as the basis for circa calculations, using the algorithm
31 // ported from the XDB date parser. Its comment states:
33 // We define circa year/century specifications offsets
34 // as +/- 5% of the difference between that year/century
35 // and the present (2100), so that the farther we go back
36 // in time, the wider the range of meaning of "circa."
38 private static final DateTime circaBaseDateTime = new DateTime(2100, 12, 31, 0, 0, 0, 0, chronology);
41 * Gets the number (1-12) of a month for a given name.
43 * @param monthName The name of the month
44 * @return The number of the month, between 1 and 12
46 public static int getMonthByName(String monthName) {
47 // Normalize "sept" to "sep", since DateTimeFormat doesn't
48 // understand the former.
50 if (monthName.equals("sept")) {
54 return monthFormatter.parseDateTime(monthName).getMonthOfYear();
58 * Gets the number of days in a given month.
60 * @param month The month number, between 1 and 12
61 * @param year The year (in order to account for leap years)
62 * @return The number of days in the month
64 public static int getDaysInMonth(int month, int year, Era era) {
66 era = Date.DEFAULT_ERA;
69 DateTime dateTime = new DateTime(chronology)
70 .withEra((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD)
72 .withMonthOfYear(month);
74 return dateTime.dayOfMonth().getMaximumValue();
78 * Gets the Date representing the first day of a given quarter year.
80 * @param year The year
81 * @param quarter The quarter, between 1 and 4
82 * @return The first day of the quarter year
84 public static Date getQuarterYearStartDate(int quarter, int year) {
85 int startMonth = getQuarterYearStartMonth(quarter);
87 return new Date(year, startMonth, 1);
91 * Gets the Date representing the last day of a given quarter year.
93 * @param year The year
94 * @param quarter The quarter, between 1 and 4
95 * @return The last day of the quarter year
97 public static Date getQuarterYearEndDate(int quarter, int year, Era era) {
98 int endMonth = getQuarterYearEndMonth(quarter);
100 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
104 * Gets the first month of a given quarter in a year.
106 * @param quarter The quarter, between 1 and 4
107 * @return The number of the first month in the quarter
109 public static int getQuarterYearStartMonth(int quarter) {
110 return ((3 * (quarter-1)) + 1);
114 * Gets the last month of a given quarter in a year.
116 * @param quarter The quarter, between 1 and 4
117 * @return The number of the last month in the quarter
119 public static int getQuarterYearEndMonth(int quarter) {
120 return (getQuarterYearStartMonth(quarter) + 2);
124 * Gets the Date representing the first day of a given half year.
126 * @param year The year
127 * @param half The half, between 1 and 2
128 * @return The first day of the half year
130 public static Date getHalfYearStartDate(int half, int year) {
131 int startMonth = getHalfYearStartMonth(half);
133 return new Date(year, startMonth, 1);
138 * Gets the Date representing the last day of a given half year.
140 * @param year The year
141 * @param half The half, between 1 and 2
142 * @return The last day of the half year
144 public static Date getHalfYearEndDate(int half, int year, Era era) {
145 int endMonth = getHalfYearEndMonth(half);
147 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
151 * Gets the first month of a given half in a year.
153 * @param half The half, between 1 and 2
154 * @return The number of the first month in the half
156 public static int getHalfYearStartMonth(int half) {
157 return ((6 * (half-1)) + 1);
161 * Gets the last month of a given half in a year.
163 * @param half The half, between 1 and 2
164 * @return The number of the last month in the half
166 public static int getHalfYearEndMonth(int half) {
167 return (getHalfYearStartMonth(half) + 5);
171 * Gets the Date representing the first day of a given partial year.
173 * @param year The year
174 * @param part The part
175 * @return The first day of the partial year
177 public static Date getPartialYearStartDate(Part part, int year) {
178 int startMonth = getPartialYearStartMonth(part);
180 return new Date(year, startMonth, 1);
184 * Gets the Date representing the last day of a given partial year.
186 * @param year The year
187 * @param part The part
188 * @return The last day of the partial year
190 public static Date getPartialYearEndDate(Part part, int year, Era era) {
191 int endMonth = getPartialYearEndMonth(part);
193 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
197 * Gets the first month of a given part of a year.
199 * @param part The part
200 * @return The number of the first month in the part
202 public static int getPartialYearStartMonth(Part part) {
205 if (part == Part.EARLY) {
208 else if (part == Part.MIDDLE) {
211 else if (part == Part.LATE) {
215 throw new IllegalArgumentException("unexpected part");
222 * Gets the last month of a given part of a year.
224 * @param part The part
225 * @return The number of the last month in the part
227 public static int getPartialYearEndMonth(Part part) {
230 if (part == Part.EARLY) {
233 else if (part == Part.MIDDLE) {
236 else if (part == Part.LATE) {
240 throw new IllegalArgumentException("unexpected part");
247 * Gets the Date representing the first day of a given partial decade.
249 * @param decade The decade, specified as a number ending in 0.
250 * For decades A.D., this is the first year of the decade. For
251 * decades B.C., this is the last year of the decade.
252 * @param part The part
253 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
254 * @return The first day of the partial decade
256 public static Date getPartialDecadeStartDate(int decade, Part part, Era era) {
258 era = Date.DEFAULT_ERA;
261 int startYear = getPartialDecadeStartYear(decade, part, era);
263 return new Date(startYear, 1, 1, era);
267 * Gets the Date representing the last day of a given partial decade.
269 * @param decade The decade, specified as a number ending in 0.
270 * For decades A.D., this is the first year of the decade. For
271 * decades B.C., this is the last year of the decade.
272 * @param part The part
273 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
274 * @return The last day of the partial decade
276 public static Date getPartialDecadeEndDate(int decade, Part part, Era era) {
278 era = Date.DEFAULT_ERA;
281 int endYear = getPartialDecadeEndYear(decade, part, era);
283 return new Date(endYear, 12, 31, era);
287 * Gets the first year of a given part of a decade.
289 * @param decade The decade, specified as a number ending in 0.
290 * For decades A.D., this is the first year of the decade. For
291 * decades B.C., this is the last year of the decade.
292 * @param part The part
293 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
294 * @return The first year in the part
296 public static int getPartialDecadeStartYear(int decade, Part part, Era era) {
298 era = Date.DEFAULT_ERA;
303 if (era == Era.BCE) {
304 if (part == Part.EARLY) {
305 startYear = decade + 9;
307 else if (part == Part.MIDDLE) {
308 startYear = decade + 6;
310 else if (part == Part.LATE) {
311 startYear = decade + 3;
314 throw new IllegalArgumentException("unexpected part");
318 if (part == Part.EARLY) {
321 else if (part == Part.MIDDLE) {
322 startYear = decade + 4;
324 else if (part == Part.LATE) {
325 startYear = decade + 7;
328 throw new IllegalArgumentException("unexpected part");
336 * Gets the last year of a given part of a decade.
338 * @param decade The decade, specified as a number ending in 0.
339 * For decades A.D., this is the first year of the decade. For
340 * decades B.C., this is the last year of the decade.
341 * @param part The part
342 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
343 * @return The last year in the part
345 public static int getPartialDecadeEndYear(int decade, Part part, Era era) {
347 era = Date.DEFAULT_ERA;
352 if (era == Era.BCE) {
353 if (part == Part.EARLY) {
354 endYear = decade + 7;
356 else if (part == Part.MIDDLE) {
357 endYear = decade + 4;
359 else if (part == Part.LATE) {
363 throw new IllegalArgumentException("unexpected part");
367 if (part == Part.EARLY) {
368 endYear = decade + 3;
370 else if (part == Part.MIDDLE) {
371 endYear = decade + 6;
373 else if (part == Part.LATE) {
374 endYear = decade + 9;
377 throw new IllegalArgumentException("unexpected part");
385 * Gets the Date representing the first day of a given decade.
387 * @param decade The decade, specified as a number ending in 0.
388 * For decades A.D., this is the first year of the decade. For
389 * decades B.C., this is the last year of the decade.
390 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
391 * @return The first day of the decade
393 public static Date getDecadeStartDate(int decade, Era era) {
395 era = Date.DEFAULT_ERA;
398 int startYear = getDecadeStartYear(decade, era);
400 return new Date(startYear, 1, 1, era);
404 * Gets the Date representing the last day of a given decade.
406 * @param decade The decade, specified as a number ending in 0.
407 * For decades A.D., this is the first year of the decade. For
408 * decades B.C., this is the last year of the decade.
409 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
410 * @return The last day of the decade
412 public static Date getDecadeEndDate(int decade, Era era) {
414 era = Date.DEFAULT_ERA;
417 int endYear = getDecadeEndYear(decade, era);
419 return new Date(endYear, 12, 31, era);
423 * Gets the first year of a given decade.
425 * @param decade The decade, specified as a number ending in 0.
426 * For decades A.D., this is the first year of the decade. For
427 * decades B.C., this is the last year of the decade.
428 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
429 * @return The first year of the decade
431 public static int getDecadeStartYear(int decade, Era era) {
433 era = Date.DEFAULT_ERA;
438 if (era == Era.BCE) {
439 startYear = decade + 9;
449 * Gets the last year of a given decade.
451 * @param decade The decade, specified as a number ending in 0.
452 * For decades A.D., this is the first year of the decade. For
453 * decades B.C., this is the last year of the decade.
454 * @param era The era of the decade. If null, Date.DEFAULT_ERA is assumed.
455 * @return The last year of the decade
457 public static int getDecadeEndYear(int decade, Era era) {
459 era = Date.DEFAULT_ERA;
464 if (era == Era.BCE) {
468 endYear = decade + 9;
475 * Gets the Date representing the first day of a given century.
477 * @param century The century, specified as a number ending in 00 or 01.
478 * For centuries A.D., this is the first year of the century. For
479 * centuries B.C., this is the last year of the century. For example,
480 * the "21st century" would be specified as 2001, whereas the "2000's"
481 * would be specified as 2000. The "2nd century B.C." would be specified
482 * as 101. The "100's B.C." would be specified as 100.
483 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
484 * @return The first day of the century
486 public static Date getCenturyStartDate(int century, Era era) {
488 era = Date.DEFAULT_ERA;
491 int startYear = getCenturyStartYear(century, era);
493 return new Date(startYear, 1, 1, era);
497 * Gets the Date representing the last day of a given century.
499 * @param century The century, specified as a number ending in 00 or 01.
500 * For centuries A.D., this is the first year of the century. For
501 * centuries B.C., this is the last year of the century. For example,
502 * the "21st century" would be specified as 2001, whereas the "2000's"
503 * would be specified as 2000. The "2nd century B.C." would be specified
504 * as 101. The "100's B.C." would be specified as 100.
505 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
506 * @return The last day of the century
508 public static Date getCenturyEndDate(int century, Era era) {
510 era = Date.DEFAULT_ERA;
513 int endYear = getCenturyEndYear(century, era);
515 return new Date(endYear, 12, 31, era);
519 * Gets the first year of a given century.
521 * @param century The century, specified as a number ending in 00 or 01.
522 * For centuries A.D., this is the first year of the century. For
523 * centuries B.C., this is the last year of the century. For example,
524 * the "21st century" would be specified as 2001, whereas the "2000's"
525 * would be specified as 2000. The "2nd century B.C." would be specified
526 * as 101. The "100's B.C." would be specified as 100.
527 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
528 * @return The first year of the century
530 public static int getCenturyStartYear(int century, Era era) {
532 era = Date.DEFAULT_ERA;
537 if (era == Era.BCE) {
538 startYear = century + 99;
548 * Gets the last year of a given century.
550 * @param century The century, specified as a number ending in 00 or 01.
551 * For centuries A.D., this is the first year of the century. For
552 * centuries B.C., this is the last year of the century. For example,
553 * the "21st century" would be specified as 2001, whereas the "2000's"
554 * would be specified as 2000. The "2nd century B.C." would be specified
555 * as 101. The "100's B.C." would be specified as 100.
556 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
557 * @return The last year of the century
559 public static int getCenturyEndYear(int century, Era era) {
561 era = Date.DEFAULT_ERA;
566 if (era == Era.BCE) {
570 endYear = century + 99;
577 * Gets the Date representing the first day of a given partial century.
579 * @param century The century, specified as a number ending in 00 or 01.
580 * For centuries A.D., this is the first year of the century. For
581 * centuries B.C., this is the last year of the century. For example,
582 * the "21st century" would be specified as 2001, whereas the "2000's"
583 * would be specified as 2000. The "2nd century B.C." would be specified
584 * as 101. The "100's B.C." would be specified as 100.
585 * @param part The part
586 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
587 * @return The first day of the partial century
589 public static Date getPartialCenturyStartDate(int century, Part part, Era era) {
591 era = Date.DEFAULT_ERA;
594 int startYear = getPartialCenturyStartYear(century, part, era);
596 return new Date(startYear, 1, 1, era);
600 * Gets the Date representing the last day of a given partial century.
602 * @param century The century, specified as a number ending in 00 or 01.
603 * For centuries A.D., this is the first year of the century. For
604 * centuries B.C., this is the last year of the century. For example,
605 * the "21st century" would be specified as 2001, whereas the "2000's"
606 * would be specified as 2000. The "2nd century B.C." would be specified
607 * as 101. The "100's B.C." would be specified as 100.
608 * @param part The part
609 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
610 * @return The last day of the partial century
612 public static Date getPartialCenturyEndDate(int century, Part part, Era era) {
614 era = Date.DEFAULT_ERA;
617 int endYear = getPartialCenturyEndYear(century, part, era);
619 return new Date(endYear, 12, 31, era);
623 * Gets the first year of a given partial century.
625 * @param century The century, specified as a number ending in 00 or 01.
626 * For centuries A.D., this is the first year of the century. For
627 * centuries B.C., this is the last year of the century. For example,
628 * the "21st century" would be specified as 2001, whereas the "2000's"
629 * would be specified as 2000. The "2nd century B.C." would be specified
630 * as 101. The "100's B.C." would be specified as 100.
631 * @param part The part
632 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
633 * @return The first year of the partial century
635 public static int getPartialCenturyStartYear(int century, Part part, Era era) {
637 era = Date.DEFAULT_ERA;
642 if (era == Era.BCE) {
643 if (part == Part.EARLY) {
644 startYear = century + 99;
646 else if (part == Part.MIDDLE) {
647 startYear = century + 66;
649 else if (part == Part.LATE) {
650 startYear = century + 33;
653 throw new IllegalArgumentException("unexpected part");
657 if (part == Part.EARLY) {
660 else if (part == Part.MIDDLE) {
661 startYear = century + 33;
663 else if (part == Part.LATE) {
664 startYear = century + 66;
667 throw new IllegalArgumentException("unexpected part");
675 * Gets the last year of a given partial century.
677 * @param century The century, specified as a number ending in 00 or 01.
678 * For centuries A.D., this is the first year of the century. For
679 * centuries B.C., this is the last year of the century. For example,
680 * the "21st century" would be specified as 2001, whereas the "2000's"
681 * would be specified as 2000. The "2nd century B.C." would be specified
682 * as 101. The "100's B.C." would be specified as 100.
683 * @param part The part
684 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
685 * @return The last year of the partial century
687 public static int getPartialCenturyEndYear(int century, Part part, Era era) {
689 era = Date.DEFAULT_ERA;
694 if (era == Era.BCE) {
695 if (part == Part.EARLY) {
696 endYear = century + 66;
698 else if (part == Part.MIDDLE) {
699 endYear = century + 33;
701 else if (part == Part.LATE) {
705 throw new IllegalArgumentException("unexpected part");
709 if (part == Part.EARLY) {
710 endYear = century + 33;
712 else if (part == Part.MIDDLE) {
713 endYear = century + 66;
715 else if (part == Part.LATE) {
716 endYear = century + 99;
719 throw new IllegalArgumentException("unexpected part");
727 * Gets the Date representing the first day of a given half century.
729 * @param century The century, specified as a number ending in 00 or 01.
730 * For centuries A.D., this is the first year of the century. For
731 * centuries B.C., this is the last year of the century. For example,
732 * the "21st century" would be specified as 2001, whereas the "2000's"
733 * would be specified as 2000. The "2nd century B.C." would be specified
734 * as 101. The "100's B.C." would be specified as 100.
735 * @param half The half
736 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
737 * @return The first day of the half century
739 public static Date getHalfCenturyStartDate(int century, int half, Era era) {
741 era = Date.DEFAULT_ERA;
744 int startYear = getHalfCenturyStartYear(century, half, era);
746 return new Date(startYear, 1, 1, era);
750 * Gets the Date representing the last day of a given half century.
752 * @param century The century, specified as a number ending in 00 or 01.
753 * For centuries A.D., this is the first year of the century. For
754 * centuries B.C., this is the last year of the century. For example,
755 * the "21st century" would be specified as 2001, whereas the "2000's"
756 * would be specified as 2000. The "2nd century B.C." would be specified
757 * as 101. The "100's B.C." would be specified as 100.
758 * @param half The half
759 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
760 * @return The last day of the half century
762 public static Date getHalfCenturyEndDate(int century, int half, Era era) {
764 era = Date.DEFAULT_ERA;
767 int endYear = getHalfCenturyEndYear(century, half, era);
769 return new Date(endYear, 12, 31, era);
773 * Gets the first year of a given half century.
775 * @param century The century, specified as a number ending in 00 or 01.
776 * For centuries A.D., this is the first year of the century. For
777 * centuries B.C., this is the last year of the century. For example,
778 * the "21st century" would be specified as 2001, whereas the "2000's"
779 * would be specified as 2000. The "2nd century B.C." would be specified
780 * as 101. The "100's B.C." would be specified as 100.
781 * @param half The half
782 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
783 * @return The first year of the half century
785 public static int getHalfCenturyStartYear(int century, int half, Era era) {
787 era = Date.DEFAULT_ERA;
792 if (era == Era.BCE) {
793 startYear = (century + 99) - (50 * (half - 1));
796 startYear = century + (50 * (half - 1));
803 * Gets the last year of a given half century.
805 * @param century The century, specified as a number ending in 00 or 01.
806 * For centuries A.D., this is the first year of the century. For
807 * centuries B.C., this is the last year of the century. For example,
808 * the "21st century" would be specified as 2001, whereas the "2000's"
809 * would be specified as 2000. The "2nd century B.C." would be specified
810 * as 101. The "100's B.C." would be specified as 100.
811 * @param half The half
812 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
813 * @return The last year of the half century
815 public static int getHalfCenturyEndYear(int century, int half, Era era) {
817 era = Date.DEFAULT_ERA;
822 if (era == Era.BCE) {
823 endYear = (century + 99) - (50 * half) + 1;
826 endYear = century + (50 * half) - 1;
833 * Gets the Date representing the first day of a given quarter century.
835 * @param century The century, specified as a number ending in 00 or 01.
836 * For centuries A.D., this is the first year of the century. For
837 * centuries B.C., this is the last year of the century. For example,
838 * the "21st century" would be specified as 2001, whereas the "2000's"
839 * would be specified as 2000. The "2nd century B.C." would be specified
840 * as 101. The "100's B.C." would be specified as 100.
841 * @param quarter The quarter
842 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
843 * @return The first day of the quarter century
845 public static Date getQuarterCenturyStartDate(int century, int quarter, Era era) {
847 era = Date.DEFAULT_ERA;
850 int startYear = getQuarterCenturyStartYear(century, quarter, era);
852 return new Date(startYear, 1, 1, era);
856 * Gets the Date representing the last day of a given quarter century.
858 * @param century The century, specified as a number ending in 00 or 01.
859 * For centuries A.D., this is the first year of the century. For
860 * centuries B.C., this is the last year of the century. For example,
861 * the "21st century" would be specified as 2001, whereas the "2000's"
862 * would be specified as 2000. The "2nd century B.C." would be specified
863 * as 101. The "100's B.C." would be specified as 100.
864 * @param quarter The quarter
865 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
866 * @return The last day of the quarter century
868 public static Date getQuarterCenturyEndDate(int century, int quarter, Era era) {
870 era = Date.DEFAULT_ERA;
873 int endYear = getQuarterCenturyEndYear(century, quarter, era);
875 return new Date(endYear, 12, 31, era);
879 * Gets the first year of a given quarter century.
881 * @param century The century, specified as a number ending in 00 or 01.
882 * For centuries A.D., this is the first year of the century. For
883 * centuries B.C., this is the last year of the century. For example,
884 * the "21st century" would be specified as 2001, whereas the "2000's"
885 * would be specified as 2000. The "2nd century B.C." would be specified
886 * as 101. The "100's B.C." would be specified as 100.
887 * @param quarter The quarter
888 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
889 * @return The first year of the quarter century
891 public static int getQuarterCenturyStartYear(int century, int quarter, Era era) {
893 era = Date.DEFAULT_ERA;
898 if (era == Era.BCE) {
899 startYear = (century + 99) - (25 * (quarter - 1));
902 startYear = century + (25 * (quarter - 1));
909 * Gets the last year of a given quarter century.
911 * @param century The century, specified as a number ending in 00 or 01.
912 * For centuries A.D., this is the first year of the century. For
913 * centuries B.C., this is the last year of the century. For example,
914 * the "21st century" would be specified as 2001, whereas the "2000's"
915 * would be specified as 2000. The "2nd century B.C." would be specified
916 * as 101. The "100's B.C." would be specified as 100.
917 * @param quarter The quarter
918 * @param era The era of the century. If null, Date.DEFAULT_ERA is assumed.
919 * @return The last year of the quarter century
921 public static int getQuarterCenturyEndYear(int century, int quarter, Era era) {
923 era = Date.DEFAULT_ERA;
928 if (era == Era.BCE) {
929 endYear = (century + 99) - (25 * quarter) + 1;
932 endYear = century + (25 * quarter) - 1;
939 * Converts an nth century number to a year. For example, to convert "21st century"
940 * to a year, call nthCenturyToYear(21), which returns 2001. For centuries A.D., the
941 * year returned is the first year of the nth century. For centuries B.C., the
942 * year returned is the last year of the nth century.
944 * @param n The nth century number
945 * @return The first year in the nth century, for centuries A.D.
946 * The last year of the nth century, for centuries B.C.
948 public static int nthCenturyToYear(int n) {
949 int year = (n-1) * 100 + 1;
955 * Gets the Date representing the first day of a given millennium.
957 * @param n The nth millennium number
958 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
959 * @return The first day of the millennium
961 public static Date getMillenniumStartDate(int n, Era era) {
963 era = Date.DEFAULT_ERA;
966 int startYear = getMillenniumStartYear(n, era);
968 return new Date(startYear, 1, 1, era);
972 * Gets the Date representing the last day of a given millennium.
974 * @param n The nth millennium number
975 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
976 * @return The last day of the millennium
978 public static Date getMillenniumEndDate(int n, Era era) {
980 era = Date.DEFAULT_ERA;
983 int endYear = getMillenniumEndYear(n, era);
985 return new Date(endYear, 12, 31, era);
989 * Gets the first year of a given millennium.
991 * @param n The nth millennium number
992 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
993 * @return The first year of the millennium
995 public static int getMillenniumStartYear(int n, Era era) {
997 era = Date.DEFAULT_ERA;
1002 if (era == Era.BCE) {
1006 year = (n - 1) * 1000 + 1;
1013 * Gets the last year of a given millennium.
1015 * @param n The nth millennium number
1016 * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
1017 * @return The last year of the millennium
1019 public static int getMillenniumEndYear(int n, Era era) {
1021 era = Date.DEFAULT_ERA;
1026 if (era == Era.BCE) {
1027 year = (n - 1) * 1000 + 1;
1037 * Calculates the earliest date that may be considered to be "before"
1040 * @param date The date
1041 * @return The earliest date "before" the date
1043 public static Date getEarliestBeforeDate(Date date) {
1044 return getEarliestBeforeDate(date, null);
1048 * Calculates the latest date that may be considered to be "after"
1051 * @param date The date
1052 * @return The latest date "after" the date
1054 public static Date getLatestAfterDate(Date date) {
1055 return getLatestAfterDate(date, null);
1059 * Calculates the earliest date that may be considered to be "before"
1060 * a given date range.
1062 * @param startDate The first date in the range
1063 * @param endDate The last date in the range
1064 * @return The earliest date "before" the range
1066 public static Date getEarliestBeforeDate(Date startDate, Date endDate) {
1068 // Return an empty date to be used in before date cases
1072 // This algorithm is inherited from the XDB fuzzydate parser,
1073 // which considers "before" to mean "within a lifetime before".
1075 if (endDate == null) {
1076 endDate = startDate;
1079 int difference = getYearsBetween(startDate, endDate);
1081 Date earliestDate = startDate.copy();
1082 subtractYears(earliestDate, 1);
1083 earliestDate.setMonth(1);
1084 earliestDate.setDay(1);
1086 if (difference < 100) {
1087 // The comment from the XDB fuzzydate parser states:
1089 // Before/after years are really about birth/death dates
1090 // so we use average life-span of 75 years
1092 subtractYears(earliestDate, 75);
1095 // The comment from the XDB fuzzydate parser states:
1097 // Before/after years are really about birth/death dates
1098 // so we use average life-span of 75 years
1099 // but since the spec was a century, e.g. pre 20th century
1100 // we'll make the range a bit bigger
1103 subtractYears(earliestDate, 175);
1106 return earliestDate;
1111 * Calculates the latest date that may be considered to be "after"
1112 * a given date range.
1114 * @param startDate The first date in the range
1115 * @param endDate The last date in the range
1116 * @return The latest date "after" the range
1118 public static Date getLatestAfterDate(Date startDate, Date endDate) {
1119 Date currentDate = getCurrentDate();
1120 if (endDate == null) {
1124 int comparisonResult = compareDates(currentDate, endDate);
1125 if (comparisonResult == 1 || comparisonResult == 0) {
1132 * Wrapper function for MutableDateTime's comparator.
1133 * @param startDate The first date in the range
1134 * @param endDate The last date in the range
1135 * @return -1 if startDate is before, 0 if they are equal, 1 if startDate is after endDate
1137 public static int compareDates(Date startDate, Date endDate) {
1138 if (startDate.getYear() == null || endDate.getYear() == null) {
1139 throw new IllegalArgumentException("Must provide a start and end date to compare.");
1142 MutableDateTime startDateTime = convertToDateTime(startDate);
1143 MutableDateTime endDateTime = convertToDateTime(endDate);
1145 return startDateTime.compareTo(endDateTime);
1149 * Returns a Date object based on the local date.
1151 public static Date getCurrentDate() {
1152 LocalDate localDate = new LocalDate();
1153 Integer year = (Integer) localDate.getYear();
1154 Integer month = (Integer) localDate.getMonthOfYear();
1155 Integer dayOfMonth = (Integer) localDate.getDayOfMonth();
1156 Era era = (localDate.getEra() == DateTimeConstants.BC) ? Era.BCE : Era.CE;
1157 return new Date(year, month, dayOfMonth, era);
1160 public static int getYearsBetween(Date startDate, Date endDate) {
1161 if (startDate == null || endDate == null) {
1162 throw new InvalidDateException("date must not be null");
1165 Integer startYear = startDate.getYear();
1166 Integer endYear = endDate.getYear();
1168 if (startYear == null || endYear == null) {
1169 throw new IllegalArgumentException("year must not be null");
1172 Era startEra = startDate.getEra();
1173 Era endEra = endDate.getEra();
1175 if (startEra == null || endEra == null) {
1176 throw new IllegalArgumentException("era must not be null");
1179 MutableDateTime startDateTime = convertToDateTime(startDate);
1180 MutableDateTime endDateTime = convertToDateTime(endDate);
1182 int years = Years.yearsBetween(startDateTime, endDateTime).getYears();
1188 * Calculates the interval, in years, that should be padded around a date so
1189 * that any date within that interval may be considered to be "circa" the
1192 * @param year The year of the date
1193 * @param era The era of the date. If null, Date.DEFAULT_ERA is assumed.
1194 * @return The number of "circa" years before and after the date
1196 public static int getCircaIntervalYears(int year, Era era) {
1198 * This algorithm is inherited from the fuzzydate parser
1199 * in XDB. Its comment states:
1201 * We define circa year/century specifications offsets
1202 * as +/- 5% of the difference between that year/century
1203 * and the present (2100), so that the farther we go back
1204 * in time, the wider the range of meaning of "circa."
1209 era = Date.DEFAULT_ERA;
1212 if (era == Era.BCE) {
1213 // Improved precision for BC dates
1216 if (year % 1000 == 0) {
1218 } else if (year % 100 == 0) {
1220 } else if (year % 10 == 0) {
1222 } else if (year % 10 > 0 && year % 10 < 10) {
1228 MutableDateTime dateTime = new MutableDateTime(chronology);
1229 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1230 dateTime.yearOfEra().set(year);
1231 dateTime.monthOfYear().set(1);
1232 dateTime.dayOfMonth().set(1);
1233 dateTime.setTime(0, 0, 0, 0);
1235 int years = Years.yearsBetween(dateTime, circaBaseDateTime).getYears();
1239 return ((int) Math.round(years * 0.05));
1243 * Adds a number of days to a date.
1245 * @param date The date
1246 * @param days The number of days to add to the date
1248 public static void addDays(Date date, int days) {
1249 MutableDateTime dateTime = convertToDateTime(date);
1251 dateTime.add(Days.days(days));
1253 setFromDateTime(date, dateTime);
1257 * Adds a number of years to a date's year.
1259 * @param date The date
1260 * @param years The number of years to add to the date
1262 public static void addYears(Date date, int years) {
1263 MutableDateTime dateTime = convertToDateTime(date);
1265 dateTime.add(Years.years(years));
1267 setFromDateTime(date, dateTime);
1271 * Subtracts a number of years from a date's year.
1273 * @param date The date
1274 * @param years The number of years to subtract from the date
1276 public static void subtractYears(Date date, int years) {
1277 addYears(date, -years);
1280 public static String formatEarliestTimestamp(Date date) {
1281 return formatEarliest(date, timestampFormatter);
1284 public static String formatEarliestScalarValue(Date date) {
1285 return formatEarliest(date, scalarValueFormatter);
1288 public static String formatEarliest(Date date, DateTimeFormatter formatter) {
1289 Era era = date.getEra();
1292 era = Date.DEFAULT_ERA;
1295 MutableDateTime dateTime = null;
1298 dateTime = convertToDateTime(date);
1300 catch(IllegalFieldValueException e) {
1301 throw new InvalidDateException(e.getMessage());
1304 String scalarDate = formatter.print(dateTime);
1309 public static String formatLatestTimestamp(Date date) {
1310 return formatLatest(date, timestampFormatter);
1313 public static String formatLatestScalarValue(Date date) {
1314 return formatLatest(date, scalarValueFormatter);
1317 public static String formatLatest(Date date, DateTimeFormatter formatter) {
1318 Era era = date.getEra();
1321 era = Date.DEFAULT_ERA;
1324 MutableDateTime dateTime = null;
1327 dateTime = convertToDateTime(date);
1329 catch(IllegalFieldValueException e) {
1330 throw new InvalidDateException(e.getMessage());
1333 dateTime.setTime(23, 59, 59, 999);
1335 String scalarDate = formatter.print(dateTime);
1340 public static boolean isValidDate(int year, int month, int day, Era era) {
1341 boolean isValid = true;
1344 convertToDateTime(new Date(year, month,day, era));
1346 catch(IllegalFieldValueException e) {
1354 * Converts Roman numeral to integer. Currently only supports 1-12.
1355 * @param romanNum The Roman number string that needs to be transformed into decimal
1356 * @return decimal representation of Roman number
1357 * Credit: https://www.geeksforgeeks.org/converting-roman-numerals-decimal-lying-1-3999/
1359 public static int romanToDecimal(String romanNum) {
1360 int length = romanNum.length();
1364 for (int i = length - 1; i >= 0; i--) {
1365 int cur = getRomanValue(romanNum.charAt(i));
1367 if (i == length - 1) {
1382 private static int getRomanValue(char c) {
1383 if (c == 'i') return 1;
1384 else if (c == 'v') return 5;
1385 else if (c == 'x') return 10;
1390 * Converts a Date to a joda-time DateTime.
1392 * @param date The Date
1393 * @return A MutableDateTime representing the same date
1395 private static MutableDateTime convertToDateTime(Date date) {
1396 Era era = date.getEra();
1399 era = Date.DEFAULT_ERA;
1402 MutableDateTime dateTime = new MutableDateTime(chronology);
1403 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1404 dateTime.yearOfEra().set(date.getYear());
1405 dateTime.monthOfYear().set(date.getMonth());
1406 dateTime.dayOfMonth().set(date.getDay());
1407 dateTime.setTime(0, 0, 0, 0);
1413 * Sets the fields in a Date so that it represents the same date
1414 * as a given DateTime.
1416 * @param date The Date to set
1417 * @param dateTime A MutableDateTime representing the desired date
1419 private static void setFromDateTime(Date date, MutableDateTime dateTime) {
1420 date.setYear(dateTime.getYearOfEra());
1421 date.setMonth(dateTime.getMonthOfYear());
1422 date.setDay(dateTime.getDayOfMonth());
1423 date.setEra((dateTime.getEra() == DateTimeConstants.BC) ? Era.BCE : Era.CE);