]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
77b6a7f76ebe3ac7a0699991de3c982c8b857339
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.structureddate;
2
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;
14
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'");
19
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.
23
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.
27
28         private static final Chronology chronology = GJChronology.getInstance();
29
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:
32         //
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."
37
38         private static final DateTime circaBaseDateTime = new DateTime(2100, 12, 31, 0, 0, 0, 0, chronology);
39
40         /**
41          * Gets the number (1-12) of a month for a given name.
42          *
43          * @param monthName The name of the month
44          * @return          The number of the month, between 1 and 12
45          */
46         public static int getMonthByName(String monthName) {
47                 // Normalize "sept" to "sep", since DateTimeFormat doesn't
48                 // understand the former.
49
50                 if (monthName.equals("sept")) {
51                         monthName = "sep";
52                 }
53
54                 return monthFormatter.parseDateTime(monthName).getMonthOfYear();
55         }
56
57         /**
58          * Gets the number of days in a given month.
59          *
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
63          */
64         public static int getDaysInMonth(int month, int year, Era era) {
65                 if (era == null) {
66                         era = Date.DEFAULT_ERA;
67                 }
68
69                 DateTime dateTime = new DateTime(chronology)
70                                 .withEra((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD)
71                                 .withYearOfEra(year)
72                                 .withMonthOfYear(month);
73
74                 return dateTime.dayOfMonth().getMaximumValue();
75         }
76
77         /**
78          * Gets the Date representing the first day of a given quarter year.
79          *
80          * @param year    The year
81          * @param quarter The quarter, between 1 and 4
82          * @return        The first day of the quarter year
83          */
84         public static Date getQuarterYearStartDate(int quarter, int year) {
85                 int startMonth = getQuarterYearStartMonth(quarter);
86
87                 return new Date(year, startMonth, 1);
88         }
89
90         /**
91          * Gets the Date representing the last day of a given quarter year.
92          *
93          * @param year    The year
94          * @param quarter The quarter, between 1 and 4
95          * @return        The last day of the quarter year
96          */
97         public static Date getQuarterYearEndDate(int quarter, int year, Era era) {
98                 int endMonth = getQuarterYearEndMonth(quarter);
99
100                 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
101         }
102
103         /**
104          * Gets the first month of a given quarter in a year.
105          *
106          * @param quarter The quarter, between 1 and 4
107          * @return        The number of the first month in the quarter
108          */
109         public static int getQuarterYearStartMonth(int quarter) {
110                 return ((3 * (quarter-1)) + 1);
111         }
112
113         /**
114          * Gets the last month of a given quarter in a year.
115          *
116          * @param quarter The quarter, between 1 and 4
117          * @return        The number of the last month in the quarter
118          */
119         public static int getQuarterYearEndMonth(int quarter) {
120                 return (getQuarterYearStartMonth(quarter) + 2);
121         }
122
123         /**
124          * Gets the Date representing the first day of a given half year.
125          *
126          * @param year The year
127          * @param half The half, between 1 and 2
128          * @return     The first day of the half year
129          */
130         public static Date getHalfYearStartDate(int half, int year) {
131                 int startMonth = getHalfYearStartMonth(half);
132
133                 return new Date(year, startMonth, 1);
134         }
135
136
137         /**
138          * Gets the Date representing the last day of a given half year.
139          *
140          * @param year The year
141          * @param half The half, between 1 and 2
142          * @return     The last day of the half year
143          */
144         public static Date getHalfYearEndDate(int half, int year, Era era) {
145                 int endMonth = getHalfYearEndMonth(half);
146
147                 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
148         }
149
150         /**
151          * Gets the first month of a given half in a year.
152          *
153          * @param half The half, between 1 and 2
154          * @return     The number of the first month in the half
155          */
156         public static int getHalfYearStartMonth(int half) {
157                 return ((6 * (half-1)) + 1);
158         }
159
160         /**
161          * Gets the last month of a given half in a year.
162          *
163          * @param half The half, between 1 and 2
164          * @return     The number of the last month in the half
165          */
166         public static int getHalfYearEndMonth(int half) {
167                 return (getHalfYearStartMonth(half) + 5);
168         }
169
170         /**
171          * Gets the Date representing the first day of a given partial year.
172          *
173          * @param year The year
174          * @param part The part
175          * @return     The first day of the partial year
176          */
177         public static Date getPartialYearStartDate(Part part, int year) {
178                 int startMonth = getPartialYearStartMonth(part);
179
180                 return new Date(year, startMonth, 1);
181         }
182
183         /**
184          * Gets the Date representing the last day of a given partial year.
185          *
186          * @param year The year
187          * @param part The part
188          * @return     The last day of the partial year
189          */
190         public static Date getPartialYearEndDate(Part part, int year, Era era) {
191                 int endMonth = getPartialYearEndMonth(part);
192
193                 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
194         }
195
196         /**
197          * Gets the first month of a given part of a year.
198          *
199          * @param part The part
200          * @return     The number of the first month in the part
201          */
202         public static int getPartialYearStartMonth(Part part) {
203                 int month;
204
205                 if (part == Part.EARLY) {
206                         month = 1;
207                 }
208                 else if (part == Part.MIDDLE) {
209                         month = 5;
210                 }
211                 else if (part == Part.LATE) {
212                         month = 9;
213                 }
214                 else {
215                         throw new IllegalArgumentException("unexpected part");
216                 }
217
218                 return month;
219         }
220
221         /**
222          * Gets the last month of a given part of a year.
223          *
224          * @param part The part
225          * @return     The number of the last month in the part
226          */
227         public static int getPartialYearEndMonth(Part part) {
228                 int month;
229
230                 if (part == Part.EARLY) {
231                         month = 4;
232                 }
233                 else if (part == Part.MIDDLE) {
234                         month = 8;
235                 }
236                 else if (part == Part.LATE) {
237                         month = 12;
238                 }
239                 else {
240                         throw new IllegalArgumentException("unexpected part");
241                 }
242
243                 return month;
244         }
245
246         /**
247          * Gets the Date representing the first day of a given partial decade.
248          *
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
255          */
256         public static Date getPartialDecadeStartDate(int decade, Part part, Era era) {
257                 if (era == null) {
258                         era = Date.DEFAULT_ERA;
259                 }
260
261                 int startYear = getPartialDecadeStartYear(decade, part, era);
262
263                 return new Date(startYear, 1, 1, era);
264         }
265
266         /**
267          * Gets the Date representing the last day of a given partial decade.
268          *
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
275          */
276         public static Date getPartialDecadeEndDate(int decade, Part part, Era era) {
277                 if (era == null) {
278                         era = Date.DEFAULT_ERA;
279                 }
280
281                 int endYear = getPartialDecadeEndYear(decade, part, era);
282
283                 return new Date(endYear, 12, 31, era);
284         }
285
286         /**
287          * Gets the first year of a given part of a decade.
288          *
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
295          */
296         public static int getPartialDecadeStartYear(int decade, Part part, Era era) {
297                 if (era == null) {
298                         era = Date.DEFAULT_ERA;
299                 }
300
301                 int startYear;
302
303                 if (era == Era.BCE) {
304                         if (part == Part.EARLY) {
305                                 startYear = decade + 9;
306                         }
307                         else if (part == Part.MIDDLE) {
308                                 startYear = decade + 6;
309                         }
310                         else if (part == Part.LATE) {
311                                 startYear = decade + 3;
312                         }
313                         else {
314                                 throw new IllegalArgumentException("unexpected part");
315                         }
316                 }
317                 else {
318                         if (part == Part.EARLY) {
319                                 startYear = decade;
320                         }
321                         else if (part == Part.MIDDLE) {
322                                 startYear = decade + 4;
323                         }
324                         else if (part == Part.LATE) {
325                                 startYear = decade + 7;
326                         }
327                         else {
328                                 throw new IllegalArgumentException("unexpected part");
329                         }
330                 }
331
332                 return startYear;
333         }
334
335         /**
336          * Gets the last year of a given part of a decade.
337          *
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
344          */
345         public static int getPartialDecadeEndYear(int decade, Part part, Era era) {
346                 if (era == null) {
347                         era = Date.DEFAULT_ERA;
348                 }
349
350                 int endYear;
351
352                 if (era == Era.BCE) {
353                         if (part == Part.EARLY) {
354                                 endYear = decade + 7;
355                         }
356                         else if (part == Part.MIDDLE) {
357                                 endYear = decade + 4;
358                         }
359                         else if (part == Part.LATE) {
360                                 endYear = decade;
361                         }
362                         else {
363                                 throw new IllegalArgumentException("unexpected part");
364                         }
365                 }
366                 else {
367                         if (part == Part.EARLY) {
368                                 endYear = decade + 3;
369                         }
370                         else if (part == Part.MIDDLE) {
371                                 endYear = decade + 6;
372                         }
373                         else if (part == Part.LATE) {
374                                 endYear = decade + 9;
375                         }
376                         else {
377                                 throw new IllegalArgumentException("unexpected part");
378                         }
379                 }
380
381                 return endYear;
382         }
383
384         /**
385          * Gets the Date representing the first day of a given decade.
386          *
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
392          */
393         public static Date getDecadeStartDate(int decade, Era era) {
394                 if (era == null) {
395                         era = Date.DEFAULT_ERA;
396                 }
397
398                 int startYear = getDecadeStartYear(decade, era);
399
400                 return new Date(startYear, 1, 1, era);
401         }
402
403         /**
404          * Gets the Date representing the last day of a given decade.
405          *
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
411          */
412         public static Date getDecadeEndDate(int decade, Era era) {
413                 if (era == null) {
414                         era = Date.DEFAULT_ERA;
415                 }
416
417                 int endYear = getDecadeEndYear(decade, era);
418
419                 return new Date(endYear, 12, 31, era);
420         }
421
422         /**
423          * Gets the first year of a given decade.
424          *
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
430          */
431         public static int getDecadeStartYear(int decade, Era era) {
432                 if (era == null) {
433                         era = Date.DEFAULT_ERA;
434                 }
435
436                 int startYear;
437
438                 if (era == Era.BCE) {
439                         startYear = decade + 9;
440                 }
441                 else {
442                         startYear = decade;
443                 }
444
445                 return startYear;
446         }
447
448         /**
449          * Gets the last year of a given decade.
450          *
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
456          */
457         public static int getDecadeEndYear(int decade, Era era) {
458                 if (era == null) {
459                         era = Date.DEFAULT_ERA;
460                 }
461
462                 int endYear;
463
464                 if (era == Era.BCE) {
465                         endYear = decade;
466                 }
467                 else {
468                         endYear = decade + 9;
469                 }
470
471                 return endYear;
472         }
473
474         /**
475          * Gets the Date representing the first day of a given century.
476          *
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
485          */
486         public static Date getCenturyStartDate(int century, Era era) {
487                 if (era == null) {
488                         era = Date.DEFAULT_ERA;
489                 }
490
491                 int startYear = getCenturyStartYear(century, era);
492
493                 return new Date(startYear, 1, 1, era);
494         }
495
496         /**
497          * Gets the Date representing the last day of a given century.
498          *
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
507          */
508         public static Date getCenturyEndDate(int century, Era era) {
509                 if (era == null) {
510                         era = Date.DEFAULT_ERA;
511                 }
512
513                 int endYear = getCenturyEndYear(century, era);
514
515                 return new Date(endYear, 12, 31, era);
516         }
517
518         /**
519          * Gets the first year of a given century.
520          *
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
529          */
530         public static int getCenturyStartYear(int century, Era era) {
531                 if (era == null) {
532                         era = Date.DEFAULT_ERA;
533                 }
534
535                 int startYear;
536
537                 if (era == Era.BCE) {
538                         startYear = century + 99;
539                 }
540                 else {
541                         startYear = century;
542                 }
543
544                 return startYear;
545         }
546
547         /**
548          * Gets the last year of a given century.
549          *
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
558          */
559         public static int getCenturyEndYear(int century, Era era) {
560                 if (era == null) {
561                         era = Date.DEFAULT_ERA;
562                 }
563
564                 int endYear;
565
566                 if (era == Era.BCE) {
567                         endYear = century;
568                 }
569                 else {
570                         endYear = century + 99;
571                 }
572
573                 return endYear;
574         }
575
576         /**
577          * Gets the Date representing the first day of a given partial century.
578          *
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
588          */
589         public static Date getPartialCenturyStartDate(int century, Part part, Era era) {
590                 if (era == null) {
591                         era = Date.DEFAULT_ERA;
592                 }
593
594                 int startYear = getPartialCenturyStartYear(century, part, era);
595
596                 return new Date(startYear, 1, 1, era);
597         }
598
599         /**
600          * Gets the Date representing the last day of a given partial century.
601          *
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
611          */
612         public static Date getPartialCenturyEndDate(int century, Part part, Era era) {
613                 if (era == null) {
614                         era = Date.DEFAULT_ERA;
615                 }
616
617                 int endYear = getPartialCenturyEndYear(century, part, era);
618
619                 return new Date(endYear, 12, 31, era);
620         }
621
622         /**
623          * Gets the first year of a given partial century.
624          *
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
634          */
635         public static int getPartialCenturyStartYear(int century, Part part, Era era) {
636                 if (era == null) {
637                         era = Date.DEFAULT_ERA;
638                 }
639
640                 int startYear;
641
642                 if (era == Era.BCE) {
643                         if (part == Part.EARLY) {
644                                 startYear = century + 99;
645                         }
646                         else if (part == Part.MIDDLE) {
647                                 startYear = century + 66;
648                         }
649                         else if (part == Part.LATE) {
650                                 startYear = century + 33;
651                         }
652                         else {
653                                 throw new IllegalArgumentException("unexpected part");
654                         }
655                 }
656                 else {
657                         if (part == Part.EARLY) {
658                                 startYear = century;
659                         }
660                         else if (part == Part.MIDDLE) {
661                                 startYear = century + 33;
662                         }
663                         else if (part == Part.LATE) {
664                                 startYear = century + 66;
665                         }
666                         else {
667                                 throw new IllegalArgumentException("unexpected part");
668                         }
669                 }
670
671                 return startYear;
672         }
673
674         /**
675          * Gets the last year of a given partial century.
676          *
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
686          */
687         public static int getPartialCenturyEndYear(int century, Part part, Era era) {
688                 if (era == null) {
689                         era = Date.DEFAULT_ERA;
690                 }
691
692                 int endYear;
693
694                 if (era == Era.BCE) {
695                         if (part == Part.EARLY) {
696                                 endYear = century + 66;
697                         }
698                         else if (part == Part.MIDDLE) {
699                                 endYear = century + 33;
700                         }
701                         else if (part == Part.LATE) {
702                                 endYear = century;
703                         }
704                         else {
705                                 throw new IllegalArgumentException("unexpected part");
706                         }
707                 }
708                 else {
709                         if (part == Part.EARLY) {
710                                 endYear = century + 33;
711                         }
712                         else if (part == Part.MIDDLE) {
713                                 endYear = century + 66;
714                         }
715                         else if (part == Part.LATE) {
716                                 endYear = century + 99;
717                         }
718                         else {
719                                 throw new IllegalArgumentException("unexpected part");
720                         }
721                 }
722
723                 return endYear;
724         }
725
726         /**
727          * Gets the Date representing the first day of a given half century.
728          *
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
738          */
739         public static Date getHalfCenturyStartDate(int century, int half, Era era) {
740                 if (era == null) {
741                         era = Date.DEFAULT_ERA;
742                 }
743
744                 int startYear = getHalfCenturyStartYear(century, half, era);
745
746                 return new Date(startYear, 1, 1, era);
747         }
748
749         /**
750          * Gets the Date representing the last day of a given half century.
751          *
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
761          */
762         public static Date getHalfCenturyEndDate(int century, int half, Era era) {
763                 if (era == null) {
764                         era = Date.DEFAULT_ERA;
765                 }
766
767                 int endYear = getHalfCenturyEndYear(century, half, era);
768
769                 return new Date(endYear, 12, 31, era);
770         }
771
772         /**
773          * Gets the first year of a given half century.
774          *
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
784          */
785         public static int getHalfCenturyStartYear(int century, int half, Era era) {
786                 if (era == null) {
787                         era = Date.DEFAULT_ERA;
788                 }
789
790                 int startYear;
791
792                 if (era == Era.BCE) {
793                         startYear = (century + 99) - (50 * (half - 1));
794                 }
795                 else {
796                         startYear = century + (50 * (half - 1));
797                 }
798
799                 return startYear;
800         }
801
802         /**
803          * Gets the last year of a given half century.
804          *
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
814          */
815         public static int getHalfCenturyEndYear(int century, int half, Era era) {
816                 if (era == null) {
817                         era = Date.DEFAULT_ERA;
818                 }
819
820                 int endYear;
821
822                 if (era == Era.BCE) {
823                         endYear = (century + 99) - (50 * half) + 1;
824                 }
825                 else {
826                         endYear = century + (50 * half) - 1;
827                 }
828
829                 return endYear;
830         }
831
832         /**
833          * Gets the Date representing the first day of a given quarter century.
834          *
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
844          */
845         public static Date getQuarterCenturyStartDate(int century, int quarter, Era era) {
846                 if (era == null) {
847                         era = Date.DEFAULT_ERA;
848                 }
849
850                 int startYear = getQuarterCenturyStartYear(century, quarter, era);
851
852                 return new Date(startYear, 1, 1, era);
853         }
854
855         /**
856          * Gets the Date representing the last day of a given quarter century.
857          *
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
867          */
868         public static Date getQuarterCenturyEndDate(int century, int quarter, Era era) {
869                 if (era == null) {
870                         era = Date.DEFAULT_ERA;
871                 }
872
873                 int endYear = getQuarterCenturyEndYear(century, quarter, era);
874
875                 return new Date(endYear, 12, 31, era);
876         }
877
878         /**
879          * Gets the first year of a given quarter century.
880          *
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
890          */
891         public static int getQuarterCenturyStartYear(int century, int quarter, Era era) {
892                 if (era == null) {
893                         era = Date.DEFAULT_ERA;
894                 }
895
896                 int startYear;
897
898                 if (era == Era.BCE) {
899                         startYear = (century + 99) - (25 * (quarter - 1));
900                 }
901                 else {
902                         startYear = century + (25 * (quarter - 1));
903                 }
904
905                 return startYear;
906         }
907
908         /**
909          * Gets the last year of a given quarter century.
910          *
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
920          */
921         public static int getQuarterCenturyEndYear(int century, int quarter, Era era) {
922                 if (era == null) {
923                         era = Date.DEFAULT_ERA;
924                 }
925
926                 int endYear;
927
928                 if (era == Era.BCE) {
929                         endYear = (century + 99) - (25 * quarter) + 1;
930                 }
931                 else {
932                         endYear = century + (25 * quarter) - 1;
933                 }
934
935                 return endYear;
936         }
937
938         /**
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.
943          *
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.
947          */
948         public static int nthCenturyToYear(int n) {
949                 int year = (n-1) * 100 + 1;
950
951                 return year;
952         }
953
954         /**
955          * Gets the Date representing the first day of a given millennium.
956          *
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
960          */
961         public static Date getMillenniumStartDate(int n, Era era) {
962                 if (era == null) {
963                         era = Date.DEFAULT_ERA;
964                 }
965
966                 int startYear = getMillenniumStartYear(n, era);
967
968                 return new Date(startYear, 1, 1, era);
969         }
970
971         /**
972          * Gets the Date representing the last day of a given millennium.
973          *
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
977          */
978         public static Date getMillenniumEndDate(int n, Era era) {
979                 if (era == null) {
980                         era = Date.DEFAULT_ERA;
981                 }
982
983                 int endYear = getMillenniumEndYear(n, era);
984
985                 return new Date(endYear, 12, 31, era);
986         }
987
988         /**
989          * Gets the first year of a given millennium.
990          *
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
994          */
995         public static int getMillenniumStartYear(int n, Era era) {
996                 if (era == null) {
997                         era = Date.DEFAULT_ERA;
998                 }
999
1000                 int year;
1001
1002                 if (era == Era.BCE) {
1003                         year = n * 1000;
1004                 }
1005                 else {
1006                         year = (n - 1) * 1000 + 1;
1007                 }
1008
1009                 return year;
1010         }
1011
1012         /**
1013          * Gets the last year of a given millennium.
1014          *
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
1018          */
1019         public static int getMillenniumEndYear(int n, Era era) {
1020                 if (era == null) {
1021                         era = Date.DEFAULT_ERA;
1022                 }
1023
1024                 int year;
1025
1026                 if (era == Era.BCE) {
1027                         year = (n - 1) * 1000 + 1;
1028                 }
1029                 else {
1030                         year = n * 1000;
1031                 }
1032
1033                 return year;
1034         }
1035
1036         /**
1037          * Calculates the earliest date that may be considered to be "before"
1038          * a given date.
1039          *
1040          * @param date The date
1041          * @return     The earliest date "before" the date
1042          */
1043         public static Date getEarliestBeforeDate(Date date) {
1044                 return getEarliestBeforeDate(date, null);
1045         }
1046
1047         /**
1048          * Calculates the latest date that may be considered to be "after"
1049          * a given date.
1050          *
1051          * @param date The date
1052          * @return     The latest date "after" the date
1053          */
1054         public static Date getLatestAfterDate(Date date) {
1055                 return getLatestAfterDate(date, null);
1056         }
1057
1058         /**
1059          * Calculates the earliest date that may be considered to be "before"
1060          * a given date range.
1061          *
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
1065          */
1066         public static Date getEarliestBeforeDate(Date startDate, Date endDate) {
1067                 // TODO
1068                 // Return an empty date to be used in before date cases
1069                 return new Date();
1070
1071                 /*
1072                 // This algorithm is inherited from the XDB fuzzydate parser,
1073                 // which considers "before" to mean "within a lifetime before".
1074
1075                 if (endDate == null) {
1076                         endDate = startDate;
1077                 }
1078
1079                 int difference = getYearsBetween(startDate, endDate);
1080
1081                 Date earliestDate = startDate.copy();
1082                 subtractYears(earliestDate, 1);
1083                 earliestDate.setMonth(1);
1084                 earliestDate.setDay(1);
1085
1086                 if (difference < 100) {
1087                         // The comment from the XDB fuzzydate parser states:
1088                         //
1089                         //    Before/after years are really about birth/death dates
1090                         //    so we use average life-span of 75 years
1091
1092                         subtractYears(earliestDate, 75);
1093                 }
1094                 else {
1095                         // The comment from the XDB fuzzydate parser states:
1096                         //
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
1101                         //    sheesh...
1102
1103                         subtractYears(earliestDate, 175);
1104                 }
1105
1106                 return earliestDate;
1107                 */
1108         }
1109
1110         /**
1111          * Calculates the latest date that may be considered to be "after"
1112          * a given date range.
1113          *
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
1117          */
1118         public static Date getLatestAfterDate(Date startDate, Date endDate) {
1119                 Date currentDate = getCurrentDate();
1120                 if (endDate == null) {
1121                         return currentDate;
1122                 }
1123
1124                 int comparisonResult = compareDates(currentDate, endDate);
1125                 if (comparisonResult == 1 || comparisonResult == 0) {
1126                         return currentDate;
1127                 }
1128                 return null;
1129         }
1130
1131         /**
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
1136          */
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.");
1140                 }
1141
1142                 MutableDateTime startDateTime = convertToDateTime(startDate);
1143                 MutableDateTime endDateTime = convertToDateTime(endDate);
1144
1145                 return startDateTime.compareTo(endDateTime);
1146         }
1147
1148         /**
1149          * Returns a Date object based on the local date.
1150          */
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);
1158         }
1159
1160         public static int getYearsBetween(Date startDate, Date endDate) {
1161                 if (startDate == null || endDate == null) {
1162                         throw new InvalidDateException("date must not be null");
1163                 }
1164
1165                 Integer startYear = startDate.getYear();
1166                 Integer endYear = endDate.getYear();
1167
1168                 if (startYear == null || endYear == null) {
1169                         throw new IllegalArgumentException("year must not be null");
1170                 }
1171
1172                 Era startEra = startDate.getEra();
1173                 Era endEra = endDate.getEra();
1174
1175                 if (startEra == null || endEra == null) {
1176                         throw new IllegalArgumentException("era must not be null");
1177                 }
1178
1179                 MutableDateTime startDateTime = convertToDateTime(startDate);
1180                 MutableDateTime endDateTime = convertToDateTime(endDate);
1181
1182                 int years = Years.yearsBetween(startDateTime, endDateTime).getYears();
1183
1184                 return years;
1185         }
1186
1187         /**
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
1190          * given date.
1191          *
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
1195          */
1196         public static int getCircaIntervalYears(int year, Era era) {
1197                 /*
1198                  * This algorithm is inherited from the fuzzydate parser
1199                  * in XDB. Its comment states:
1200                  *
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."
1205                  *
1206                  */
1207
1208                 if (era == null) {
1209                         era = Date.DEFAULT_ERA;
1210                 }
1211
1212                 if (era == Era.BCE) {
1213                         // Improved precision for BC dates
1214                         int interval = 0;
1215
1216                         if (year % 1000 == 0) {
1217                                 interval = 500;
1218                         } else if (year % 100 == 0) {
1219                                 interval = 50;
1220                         } else if (year % 10 == 0) {
1221                                 interval = 10;
1222                         } else if (year % 10 > 0 && year % 10 < 10) {
1223                                 interval = 5;
1224                         }
1225                         return interval;
1226                 }
1227
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);
1234
1235                 int years = Years.yearsBetween(dateTime, circaBaseDateTime).getYears();
1236
1237
1238                 // return interval;
1239                 return ((int) Math.round(years * 0.05));
1240         }
1241
1242         /**
1243          * Adds a number of days to a date.
1244          *
1245          * @param date The date
1246          * @param days The number of days to add to the date
1247          */
1248         public static void addDays(Date date, int days) {
1249                 MutableDateTime dateTime = convertToDateTime(date);
1250
1251                 dateTime.add(Days.days(days));
1252
1253                 setFromDateTime(date, dateTime);
1254         }
1255
1256         /**
1257          * Adds a number of years to a date's year.
1258          *
1259          * @param date  The date
1260          * @param years The number of years to add to the date
1261          */
1262         public static void addYears(Date date, int years) {
1263                 MutableDateTime dateTime = convertToDateTime(date);
1264
1265                 dateTime.add(Years.years(years));
1266
1267                 setFromDateTime(date, dateTime);
1268         }
1269
1270         /**
1271          * Subtracts a number of years from a date's year.
1272          *
1273          * @param date  The date
1274          * @param years The number of years to subtract from the date
1275          */
1276         public static void subtractYears(Date date, int years) {
1277                 addYears(date, -years);
1278         }
1279
1280         public static String getEarliestTimestamp(Date date) {
1281                 return formatEarliest(date, timestampFormatter);
1282         }
1283
1284         public static String getEarliestScalarValue(Date date) {
1285                 return formatEarliest(date, scalarValueFormatter);
1286         }
1287
1288         public static String formatEarliest(Date date, DateTimeFormatter formatter) {
1289                 Era era = date.getEra();
1290
1291                 if (era == null) {
1292                         era = Date.DEFAULT_ERA;
1293                 }
1294
1295                 MutableDateTime dateTime = null;
1296
1297                 try {
1298                         dateTime = convertToDateTime(date);
1299                 }
1300                 catch(IllegalFieldValueException e) {
1301                         throw new InvalidDateException(e.getMessage());
1302                 }
1303
1304                 String scalarDate = formatter.print(dateTime);
1305
1306                 return scalarDate;
1307         }
1308
1309         public static String getLatestTimestamp(Date date) {
1310                 return formatLatest(date, timestampFormatter);
1311         }
1312
1313         public static String getLatestScalarValue(Date date) {
1314                 return formatLatest(date, scalarValueFormatter);
1315         }
1316
1317         public static String formatLatest(Date date, DateTimeFormatter formatter) {
1318                 Era era = date.getEra();
1319
1320                 if (era == null) {
1321                         era = Date.DEFAULT_ERA;
1322                 }
1323
1324                 MutableDateTime dateTime = null;
1325
1326                 try {
1327                         dateTime = convertToDateTime(date);
1328                 }
1329                 catch(IllegalFieldValueException e) {
1330                         throw new InvalidDateException(e.getMessage());
1331                 }
1332
1333                 dateTime.setTime(23, 59, 59, 999);
1334
1335                 String scalarDate = formatter.print(dateTime);
1336
1337                 return scalarDate;
1338         }
1339
1340         public static boolean isValidDate(int year, int month, int day, Era era) {
1341                 boolean isValid = true;
1342
1343                 try {
1344                         convertToDateTime(new Date(year, month,day, era));
1345                 }
1346                 catch(IllegalFieldValueException e) {
1347                         isValid = false;
1348                 }
1349
1350                 return isValid;
1351         }
1352
1353         /**
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/
1358         */
1359         public static int romanToDecimal(String romanNum) {
1360                 int length = romanNum.length();
1361                 int sum = 0;
1362                 int pre = 0;
1363
1364                 for (int i = length - 1; i >= 0; i--) {
1365                         int cur = getRomanValue(romanNum.charAt(i));
1366
1367                         if (i == length - 1) {
1368                                 sum = sum + cur;
1369                         } else {
1370                            if (cur < pre) {
1371                                    sum = sum - cur;
1372                            } else {
1373                                    sum = sum + cur;
1374                            }
1375                         }
1376                         pre = cur;
1377                 }
1378
1379                 return sum;
1380         }
1381
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;
1386                 return -1;
1387         }
1388
1389         /**
1390          * Converts a Date to a joda-time DateTime.
1391          *
1392          * @param  date The Date
1393          * @return      A MutableDateTime representing the same date
1394          */
1395         private static MutableDateTime convertToDateTime(Date date) {
1396                 Era era = date.getEra();
1397
1398                 if (era == null) {
1399                         era = Date.DEFAULT_ERA;
1400                 }
1401
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);
1408
1409                 return dateTime;
1410         }
1411
1412         /**
1413          * Sets the fields in a Date so that it represents the same date
1414          * as a given DateTime.
1415          *
1416          * @param date     The Date to set
1417          * @param dateTime A MutableDateTime representing the desired date
1418          */
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);
1424         }
1425 }