]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
4daeb359ff781ef229130b654e6008f99a7aa783
[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.MutableDateTime;
9 import org.joda.time.Years;
10 import org.joda.time.chrono.GJChronology;
11 import org.joda.time.format.DateTimeFormat;
12 import org.joda.time.format.DateTimeFormatter;
13
14 public class DateUtils {
15         private static final DateTimeFormatter monthFormatter = DateTimeFormat.forPattern("MMMM");
16         private static final DateTimeFormatter scalarDateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
17
18         // The chronology to use for date calculations, which are done using the joda-time library.
19         // See http://www.joda.org/joda-time/apidocs/org/joda/time/Chronology.html for descriptions of
20         // the chronologies supported by joda-time.
21         
22         // GJChronology (http://www.joda.org/joda-time/apidocs/org/joda/time/chrono/GJChronology.html)
23         // seems best for representing a mix of modern and historical dates, as might be seen by an
24         // anthropology museum.
25         
26         private static final Chronology chronology = GJChronology.getInstance();
27         
28         // Define the DateTime that serves as the basis for circa calculations, using the algorithm
29         // ported from the XDB date parser. Its comment states:
30         //
31         //    We define circa year/century specifications offsets
32         //    as +/- 5% of the difference between that year/century
33         //    and the present (2100), so that the farther we go back
34         //    in time, the wider the range of meaning of "circa."
35         
36         private static final DateTime circaBaseDateTime = new DateTime(2100, 12, 31, 0, 0, 0, 0, chronology);
37         
38         /**
39          * Gets the number (1-12) of a month for a given name.
40          * 
41          * @param monthName The name of the month
42          * @return          The number of the month, between 1 and 12
43          */
44         public static int getMonthByName(String monthName) {
45                 // Normalize "sept" to "sep", since DateTimeFormat doesn't
46                 // understand the former.
47                 
48                 if (monthName.equals("sept")) {
49                         monthName = "sep";
50                 }
51                 
52                 return monthFormatter.parseDateTime(monthName).getMonthOfYear();
53         }
54         
55         /**
56          * Gets the number of days in a given month.
57          * 
58          * @param month The month number, between 1 and 12
59          * @param year  The year (in order to account for leap years)
60          * @return      The number of days in the month
61          */
62         public static int getDaysInMonth(int month, int year, Era era) {
63                 if (era == null) {
64                         era = Date.DEFAULT_ERA;
65                 }
66                 
67                 DateTime dateTime = new DateTime(chronology)
68                                 .withEra((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD)
69                                 .withYearOfEra(year)
70                                 .withMonthOfYear(month);
71
72                 return dateTime.dayOfMonth().getMaximumValue();
73         }
74         
75         /**
76          * Gets the Date representing the first day of a given quarter year.
77          * 
78          * @param year    The year
79          * @param quarter The quarter, between 1 and 4
80          * @return        The first day of the quarter year
81          */
82         public static Date getQuarterYearStartDate(int quarter, int year) {
83                 int startMonth = getQuarterYearStartMonth(quarter);
84                 
85                 return new Date(year, startMonth, 1);
86         }
87         
88         /**
89          * Gets the Date representing the last day of a given quarter year.
90          * 
91          * @param year    The year
92          * @param quarter The quarter, between 1 and 4
93          * @return        The last day of the quarter year
94          */
95         public static Date getQuarterYearEndDate(int quarter, int year, Era era) {
96                 int endMonth = getQuarterYearEndMonth(quarter);
97                 
98                 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
99         }
100         
101         /**
102          * Gets the first month of a given quarter in a year.
103          * 
104          * @param quarter The quarter, between 1 and 4
105          * @return        The number of the first month in the quarter
106          */
107         public static int getQuarterYearStartMonth(int quarter) {
108                 return ((3 * (quarter-1)) + 1);
109         }
110         
111         /**
112          * Gets the last month of a given quarter in a year.
113          * 
114          * @param quarter The quarter, between 1 and 4
115          * @return        The number of the last month in the quarter
116          */
117         public static int getQuarterYearEndMonth(int quarter) {
118                 return (getQuarterYearStartMonth(quarter) + 2);
119         }
120
121         /**
122          * Gets the Date representing the first day of a given half year.
123          * 
124          * @param year The year
125          * @param half The half, between 1 and 2
126          * @return     The first day of the half year
127          */
128         public static Date getHalfYearStartDate(int half, int year) {
129                 int startMonth = getHalfYearStartMonth(half);
130                 
131                 return new Date(year, startMonth, 1);
132         }
133
134
135         /**
136          * Gets the Date representing the last day of a given half year.
137          * 
138          * @param year The year
139          * @param half The half, between 1 and 2
140          * @return     The last day of the half year
141          */
142         public static Date getHalfYearEndDate(int half, int year, Era era) {
143                 int endMonth = getHalfYearEndMonth(half);
144                 
145                 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
146         }
147
148         /**
149          * Gets the first month of a given half in a year.
150          * 
151          * @param half The half, between 1 and 2
152          * @return     The number of the first month in the half
153          */
154         public static int getHalfYearStartMonth(int half) {
155                 return ((6 * (half-1)) + 1);
156         }
157
158         /**
159          * Gets the last month of a given half in a year.
160          * 
161          * @param half The half, between 1 and 2
162          * @return     The number of the last month in the half
163          */
164         public static int getHalfYearEndMonth(int half) {
165                 return (getHalfYearStartMonth(half) + 5);
166         }
167         
168         /**
169          * Gets the Date representing the first day of a given partial year.
170          * 
171          * @param year The year
172          * @param part The part
173          * @return     The first day of the partial year
174          */
175         public static Date getPartialYearStartDate(Part part, int year) {
176                 int startMonth = getPartialYearStartMonth(part);
177                 
178                 return new Date(year, startMonth, 1);
179         }
180
181         /**
182          * Gets the Date representing the last day of a given partial year.
183          * 
184          * @param year The year
185          * @param part The part
186          * @return     The last day of the partial year
187          */
188         public static Date getPartialYearEndDate(Part part, int year, Era era) {
189                 int endMonth = getPartialYearEndMonth(part);
190                 
191                 return new Date(year, endMonth, DateUtils.getDaysInMonth(endMonth, year, era));
192         }
193         
194         /**
195          * Gets the first month of a given part of a year.
196          * 
197          * @param part The part
198          * @return     The number of the first month in the part
199          */
200         public static int getPartialYearStartMonth(Part part) {
201                 int month;
202                 
203                 if (part == Part.EARLY) {
204                         month = 1;
205                 }
206                 else if (part == Part.MIDDLE) {
207                         month = 5;
208                 }
209                 else if (part == Part.LATE) {
210                         month = 9;
211                 }
212                 else {
213                         throw new IllegalArgumentException("unexpected part");
214                 }
215                 
216                 return month;
217         }
218         
219         /**
220          * Gets the last month of a given part of a year.
221          * 
222          * @param part The part
223          * @return     The number of the last month in the part
224          */
225         public static int getPartialYearEndMonth(Part part) {
226                 int month;
227                 
228                 if (part == Part.EARLY) {
229                         month = 4;
230                 }
231                 else if (part == Part.MIDDLE) {
232                         month = 8;
233                 }
234                 else if (part == Part.LATE) {
235                         month = 12;
236                 }
237                 else {
238                         throw new IllegalArgumentException("unexpected part");
239                 }
240                 
241                 return month;
242         }
243         
244         /**
245          * Gets the Date representing the first day of a given partial decade.
246          * 
247          * @param decade The decade, specified as a number ending in 0.
248          *               For decades A.D., this is the first year of the decade. For 
249          *               decades B.C., this is the last year of the decade.
250          * @param part   The part
251          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
252          * @return       The first day of the partial decade
253          */
254         public static Date getPartialDecadeStartDate(int decade, Part part, Era era) {
255                 if (era == null) {
256                         era = Date.DEFAULT_ERA;
257                 }
258
259                 int startYear = getPartialDecadeStartYear(decade, part, era);
260                 
261                 return new Date(startYear, 1, 1, era);
262         }
263
264         /**
265          * Gets the Date representing the last day of a given partial decade.
266          * 
267          * @param decade The decade, specified as a number ending in 0.
268          *               For decades A.D., this is the first year of the decade. For 
269          *               decades B.C., this is the last year of the decade.
270          * @param part   The part
271          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
272          * @return       The last day of the partial decade
273          */
274         public static Date getPartialDecadeEndDate(int decade, Part part, Era era) {
275                 if (era == null) {
276                         era = Date.DEFAULT_ERA;
277                 }
278
279                 int endYear = getPartialDecadeEndYear(decade, part, era);
280                 
281                 return new Date(endYear, 12, 31, era);
282         }
283         
284         /**
285          * Gets the first year of a given part of a decade.
286          * 
287          * @param decade The decade, specified as a number ending in 0.
288          *               For decades A.D., this is the first year of the decade. For 
289          *               decades B.C., this is the last year of the decade.
290          * @param part   The part
291          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
292          * @return       The first year in the part
293          */
294         public static int getPartialDecadeStartYear(int decade, Part part, Era era) {
295                 if (era == null) {
296                         era = Date.DEFAULT_ERA;
297                 }
298                 
299                 int startYear;
300                 
301                 if (era == Era.BCE) {
302                         if (part == Part.EARLY) {
303                                 startYear = decade + 9;
304                         }
305                         else if (part == Part.MIDDLE) {
306                                 startYear = decade + 6;
307                         }
308                         else if (part == Part.LATE) {
309                                 startYear = decade + 3;
310                         }
311                         else {
312                                 throw new IllegalArgumentException("unexpected part");
313                         }
314                 }
315                 else {
316                         if (part == Part.EARLY) {
317                                 startYear = decade;
318                         }
319                         else if (part == Part.MIDDLE) {
320                                 startYear = decade + 4;
321                         }
322                         else if (part == Part.LATE) {
323                                 startYear = decade + 7;
324                         }
325                         else {
326                                 throw new IllegalArgumentException("unexpected part");
327                         }
328                 }
329                 
330                 return startYear;
331         }
332         
333         /**
334          * Gets the last year of a given part of a decade.
335          * 
336          * @param decade The decade, specified as a number ending in 0.
337          *               For decades A.D., this is the first year of the decade. For 
338          *               decades B.C., this is the last year of the decade.
339          * @param part   The part
340          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
341          * @return       The last year in the part
342          */
343         public static int getPartialDecadeEndYear(int decade, Part part, Era era) {
344                 if (era == null) {
345                         era = Date.DEFAULT_ERA;
346                 }
347                 
348                 int endYear;
349                 
350                 if (era == Era.BCE) {
351                         if (part == Part.EARLY) {
352                                 endYear = decade + 7;
353                         }
354                         else if (part == Part.MIDDLE) {
355                                 endYear = decade + 4;
356                         }
357                         else if (part == Part.LATE) {
358                                 endYear = decade;
359                         }
360                         else {
361                                 throw new IllegalArgumentException("unexpected part");
362                         }
363                 }
364                 else {
365                         if (part == Part.EARLY) {
366                                 endYear = decade + 3;
367                         }
368                         else if (part == Part.MIDDLE) {
369                                 endYear = decade + 6;
370                         }
371                         else if (part == Part.LATE) {
372                                 endYear = decade + 9;
373                         }
374                         else {
375                                 throw new IllegalArgumentException("unexpected part");
376                         }
377                 }
378                 
379                 return endYear;
380         }
381
382         /**
383          * Gets the Date representing the first day of a given decade.
384          * 
385          * @param decade The decade, specified as a number ending in 0.
386          *               For decades A.D., this is the first year of the decade. For 
387          *               decades B.C., this is the last year of the decade.
388          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
389          * @return       The first day of the decade
390          */
391         public static Date getDecadeStartDate(int decade, Era era) {
392                 if (era == null) {
393                         era = Date.DEFAULT_ERA;
394                 }
395
396                 int startYear = getDecadeStartYear(decade, era);
397                 
398                 return new Date(startYear, 1, 1, era);
399         }
400         
401         /**
402          * Gets the Date representing the last day of a given decade.
403          * 
404          * @param decade The decade, specified as a number ending in 0.
405          *               For decades A.D., this is the first year of the decade. For 
406          *               decades B.C., this is the last year of the decade.
407          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
408          * @return       The last day of the decade
409          */
410         public static Date getDecadeEndDate(int decade, Era era) {
411                 if (era == null) {
412                         era = Date.DEFAULT_ERA;
413                 }
414
415                 int endYear = getDecadeEndYear(decade, era);
416                 
417                 return new Date(endYear, 12, 31, era);
418         }
419         
420         /**
421          * Gets the first year of a given decade.
422          * 
423          * @param decade The decade, specified as a number ending in 0.
424          *               For decades A.D., this is the first year of the decade. For 
425          *               decades B.C., this is the last year of the decade.
426          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
427          * @return       The first year of the decade
428          */
429         public static int getDecadeStartYear(int decade, Era era) {
430                 if (era == null) {
431                         era = Date.DEFAULT_ERA;
432                 }
433                 
434                 int startYear;
435                 
436                 if (era == Era.BCE) {
437                         startYear = decade + 9;
438                 }
439                 else {
440                         startYear = decade;
441                 }
442                 
443                 return startYear;
444         }
445
446         /**
447          * Gets the last year of a given decade.
448          * 
449          * @param decade The decade, specified as a number ending in 0.
450          *               For decades A.D., this is the first year of the decade. For 
451          *               decades B.C., this is the last year of the decade.
452          * @param era    The era of the decade. If null, Date.DEFAULT_ERA is assumed.
453          * @return       The last year of the decade
454          */
455         public static int getDecadeEndYear(int decade, Era era) {
456                 if (era == null) {
457                         era = Date.DEFAULT_ERA;
458                 }
459                 
460                 int endYear;
461                 
462                 if (era == Era.BCE) {
463                         endYear = decade;
464                 }
465                 else {
466                         endYear = decade + 9;
467                 }
468                 
469                 return endYear;
470         }
471                 
472         /**
473          * Gets the Date representing the first day of a given century.
474          * 
475          * @param century The century, specified as a number ending in 00 or 01.
476          *                For centuries A.D., this is the first year of the century. For 
477          *                centuries B.C., this is the last year of the century. For example,
478          *                the "21st century" would be specified as 2001, whereas the "2000's"
479          *                would be specified as 2000. The "2nd century B.C." would be specified
480          *                as 101. The "100's B.C." would be specified as 100.
481          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
482          * @return        The first day of the century
483          */
484         public static Date getCenturyStartDate(int century, Era era) {
485                 if (era == null) {
486                         era = Date.DEFAULT_ERA;
487                 }
488
489                 int startYear = getCenturyStartYear(century, era);
490                 
491                 return new Date(startYear, 1, 1, era);
492         }
493
494         /**
495          * Gets the Date representing the last day of a given century.
496          * 
497          * @param century The century, specified as a number ending in 00 or 01.
498          *                For centuries A.D., this is the first year of the century. For 
499          *                centuries B.C., this is the last year of the century. For example,
500          *                the "21st century" would be specified as 2001, whereas the "2000's"
501          *                would be specified as 2000. The "2nd century B.C." would be specified
502          *                as 101. The "100's B.C." would be specified as 100.
503          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
504          * @return        The last day of the century
505          */
506         public static Date getCenturyEndDate(int century, Era era) {
507                 if (era == null) {
508                         era = Date.DEFAULT_ERA;
509                 }
510
511                 int endYear = getCenturyEndYear(century, era);
512                 
513                 return new Date(endYear, 12, 31, era);
514         }
515         
516         /**
517          * Gets the first year of a given century.
518          * 
519          * @param century The century, specified as a number ending in 00 or 01.
520          *                For centuries A.D., this is the first year of the century. For 
521          *                centuries B.C., this is the last year of the century. For example,
522          *                the "21st century" would be specified as 2001, whereas the "2000's"
523          *                would be specified as 2000. The "2nd century B.C." would be specified
524          *                as 101. The "100's B.C." would be specified as 100.
525          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
526          * @return        The first year of the century
527          */
528         public static int getCenturyStartYear(int century, Era era) {
529                 if (era == null) {
530                         era = Date.DEFAULT_ERA;
531                 }
532                 
533                 int startYear;
534                 
535                 if (era == Era.BCE) {
536                         startYear = century + 99;
537                 }
538                 else {
539                         startYear = century;
540                 }
541                 
542                 return startYear;
543         }
544         
545         /**
546          * Gets the last year of a given century.
547          * 
548          * @param century The century, specified as a number ending in 00 or 01.
549          *                For centuries A.D., this is the first year of the century. For 
550          *                centuries B.C., this is the last year of the century. For example,
551          *                the "21st century" would be specified as 2001, whereas the "2000's"
552          *                would be specified as 2000. The "2nd century B.C." would be specified
553          *                as 101. The "100's B.C." would be specified as 100.
554          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
555          * @return        The last year of the century
556          */
557         public static int getCenturyEndYear(int century, Era era) {
558                 if (era == null) {
559                         era = Date.DEFAULT_ERA;
560                 }
561                 
562                 int endYear;
563                 
564                 if (era == Era.BCE) {
565                         endYear = century;
566                 }
567                 else {
568                         endYear = century + 99;
569                 }
570                 
571                 return endYear;
572         }
573         
574         /**
575          * Gets the Date representing the first day of a given partial century.
576          * 
577          * @param century The century, specified as a number ending in 00 or 01.
578          *                For centuries A.D., this is the first year of the century. For 
579          *                centuries B.C., this is the last year of the century. For example,
580          *                the "21st century" would be specified as 2001, whereas the "2000's"
581          *                would be specified as 2000. The "2nd century B.C." would be specified
582          *                as 101. The "100's B.C." would be specified as 100.
583          * @param part    The part
584          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
585          * @return        The first day of the partial century
586          */
587         public static Date getPartialCenturyStartDate(int century, Part part, Era era) {
588                 if (era == null) {
589                         era = Date.DEFAULT_ERA;
590                 }
591
592                 int startYear = getPartialCenturyStartYear(century, part, era);
593                 
594                 return new Date(startYear, 1, 1, era);
595         }
596         
597         /**
598          * Gets the Date representing the last day of a given partial century.
599          * 
600          * @param century The century, specified as a number ending in 00 or 01.
601          *                For centuries A.D., this is the first year of the century. For 
602          *                centuries B.C., this is the last year of the century. For example,
603          *                the "21st century" would be specified as 2001, whereas the "2000's"
604          *                would be specified as 2000. The "2nd century B.C." would be specified
605          *                as 101. The "100's B.C." would be specified as 100.
606          * @param part    The part
607          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
608          * @return        The last day of the partial century
609          */
610         public static Date getPartialCenturyEndDate(int century, Part part, Era era) {
611                 if (era == null) {
612                         era = Date.DEFAULT_ERA;
613                 }
614
615                 int endYear = getPartialCenturyEndYear(century, part, era);
616                 
617                 return new Date(endYear, 12, 31, era);
618         }
619         
620         /**
621          * Gets the first year of a given partial century.
622          * 
623          * @param century The century, specified as a number ending in 00 or 01.
624          *                For centuries A.D., this is the first year of the century. For 
625          *                centuries B.C., this is the last year of the century. For example,
626          *                the "21st century" would be specified as 2001, whereas the "2000's"
627          *                would be specified as 2000. The "2nd century B.C." would be specified
628          *                as 101. The "100's B.C." would be specified as 100.
629          * @param part    The part
630          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
631          * @return        The first year of the partial century
632          */
633         public static int getPartialCenturyStartYear(int century, Part part, Era era) {
634                 if (era == null) {
635                         era = Date.DEFAULT_ERA;
636                 }
637                 
638                 int startYear;
639                 
640                 if (era == Era.BCE) {
641                         if (part == Part.EARLY) {
642                                 startYear = century + 99;
643                         }
644                         else if (part == Part.MIDDLE) {
645                                 startYear = century + 66;
646                         }
647                         else if (part == Part.LATE) {
648                                 startYear = century + 33;
649                         }
650                         else {
651                                 throw new IllegalArgumentException("unexpected part");
652                         }
653                 }
654                 else {
655                         if (part == Part.EARLY) {
656                                 startYear = century;
657                         }
658                         else if (part == Part.MIDDLE) {
659                                 startYear = century + 33;
660                         }
661                         else if (part == Part.LATE) {
662                                 startYear = century + 66;
663                         }
664                         else {
665                                 throw new IllegalArgumentException("unexpected part");
666                         }
667                 }
668                 
669                 return startYear;
670         }
671         
672         /**
673          * Gets the last year of a given partial century.
674          * 
675          * @param century The century, specified as a number ending in 00 or 01.
676          *                For centuries A.D., this is the first year of the century. For 
677          *                centuries B.C., this is the last year of the century. For example,
678          *                the "21st century" would be specified as 2001, whereas the "2000's"
679          *                would be specified as 2000. The "2nd century B.C." would be specified
680          *                as 101. The "100's B.C." would be specified as 100.
681          * @param part    The part
682          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
683          * @return        The last year of the partial century
684          */
685         public static int getPartialCenturyEndYear(int century, Part part, Era era) {
686                 if (era == null) {
687                         era = Date.DEFAULT_ERA;
688                 }
689                 
690                 int endYear;
691                 
692                 if (era == Era.BCE) {
693                         if (part == Part.EARLY) {
694                                 endYear = century + 66;
695                         }
696                         else if (part == Part.MIDDLE) {
697                                 endYear = century + 33;
698                         }
699                         else if (part == Part.LATE) {
700                                 endYear = century;
701                         }
702                         else {
703                                 throw new IllegalArgumentException("unexpected part");
704                         }
705                 }
706                 else {
707                         if (part == Part.EARLY) {
708                                 endYear = century + 33;
709                         }
710                         else if (part == Part.MIDDLE) {
711                                 endYear = century + 66;
712                         }
713                         else if (part == Part.LATE) {
714                                 endYear = century + 99;
715                         }
716                         else {
717                                 throw new IllegalArgumentException("unexpected part");
718                         }
719                 }
720                 
721                 return endYear;
722         }
723         
724         /**
725          * Gets the Date representing the first day of a given half century.
726          * 
727          * @param century The century, specified as a number ending in 00 or 01.
728          *                For centuries A.D., this is the first year of the century. For 
729          *                centuries B.C., this is the last year of the century. For example,
730          *                the "21st century" would be specified as 2001, whereas the "2000's"
731          *                would be specified as 2000. The "2nd century B.C." would be specified
732          *                as 101. The "100's B.C." would be specified as 100.
733          * @param half    The half
734          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
735          * @return        The first day of the half century
736          */
737         public static Date getHalfCenturyStartDate(int century, int half, Era era) {
738                 if (era == null) {
739                         era = Date.DEFAULT_ERA;
740                 }
741
742                 int startYear = getHalfCenturyStartYear(century, half, era);
743                 
744                 return new Date(startYear, 1, 1, era);
745         }
746
747         /**
748          * Gets the Date representing the last day of a given half century.
749          * 
750          * @param century The century, specified as a number ending in 00 or 01.
751          *                For centuries A.D., this is the first year of the century. For 
752          *                centuries B.C., this is the last year of the century. For example,
753          *                the "21st century" would be specified as 2001, whereas the "2000's"
754          *                would be specified as 2000. The "2nd century B.C." would be specified
755          *                as 101. The "100's B.C." would be specified as 100.
756          * @param half    The half
757          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
758          * @return        The last day of the half century
759          */
760         public static Date getHalfCenturyEndDate(int century, int half, Era era) {
761                 if (era == null) {
762                         era = Date.DEFAULT_ERA;
763                 }
764
765                 int endYear = getHalfCenturyEndYear(century, half, era);
766                 
767                 return new Date(endYear, 12, 31, era);
768         }
769         
770         /**
771          * Gets the first year of a given half century.
772          * 
773          * @param century The century, specified as a number ending in 00 or 01.
774          *                For centuries A.D., this is the first year of the century. For 
775          *                centuries B.C., this is the last year of the century. For example,
776          *                the "21st century" would be specified as 2001, whereas the "2000's"
777          *                would be specified as 2000. The "2nd century B.C." would be specified
778          *                as 101. The "100's B.C." would be specified as 100.
779          * @param half    The half
780          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
781          * @return        The first year of the half century
782          */
783         public static int getHalfCenturyStartYear(int century, int half, Era era) {
784                 if (era == null) {
785                         era = Date.DEFAULT_ERA;
786                 }
787                 
788                 int startYear;
789                 
790                 if (era == Era.BCE) {
791                         startYear = (century + 99) - (50 * (half - 1));
792                 }
793                 else {
794                         startYear = century + (50 * (half - 1));
795                 }
796                 
797                 return startYear;
798         }
799         
800         /**
801          * Gets the last year of a given half century.
802          * 
803          * @param century The century, specified as a number ending in 00 or 01.
804          *                For centuries A.D., this is the first year of the century. For 
805          *                centuries B.C., this is the last year of the century. For example,
806          *                the "21st century" would be specified as 2001, whereas the "2000's"
807          *                would be specified as 2000. The "2nd century B.C." would be specified
808          *                as 101. The "100's B.C." would be specified as 100.
809          * @param half    The half
810          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
811          * @return        The last year of the half century
812          */
813         public static int getHalfCenturyEndYear(int century, int half, Era era) {
814                 if (era == null) {
815                         era = Date.DEFAULT_ERA;
816                 }
817                 
818                 int endYear;
819                 
820                 if (era == Era.BCE) {
821                         endYear = (century + 99) - (50 * half) + 1;
822                 }
823                 else {
824                         endYear = century + (50 * half) - 1;
825                 }
826                 
827                 return endYear;
828         }
829         
830         /**
831          * Gets the Date representing the first day of a given quarter century.
832          * 
833          * @param century The century, specified as a number ending in 00 or 01.
834          *                For centuries A.D., this is the first year of the century. For 
835          *                centuries B.C., this is the last year of the century. For example,
836          *                the "21st century" would be specified as 2001, whereas the "2000's"
837          *                would be specified as 2000. The "2nd century B.C." would be specified
838          *                as 101. The "100's B.C." would be specified as 100.
839          * @param quarter The quarter
840          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
841          * @return        The first day of the quarter century
842          */
843         public static Date getQuarterCenturyStartDate(int century, int quarter, Era era) {
844                 if (era == null) {
845                         era = Date.DEFAULT_ERA;
846                 }
847                 
848                 int startYear = getQuarterCenturyStartYear(century, quarter, era);
849                 
850                 return new Date(startYear, 1, 1, era);
851         }
852
853         /**
854          * Gets the Date representing the last day of a given quarter century.
855          * 
856          * @param century The century, specified as a number ending in 00 or 01.
857          *                For centuries A.D., this is the first year of the century. For 
858          *                centuries B.C., this is the last year of the century. For example,
859          *                the "21st century" would be specified as 2001, whereas the "2000's"
860          *                would be specified as 2000. The "2nd century B.C." would be specified
861          *                as 101. The "100's B.C." would be specified as 100.
862          * @param quarter The quarter
863          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
864          * @return        The last day of the quarter century
865          */
866         public static Date getQuarterCenturyEndDate(int century, int quarter, Era era) {
867                 if (era == null) {
868                         era = Date.DEFAULT_ERA;
869                 }
870                 
871                 int endYear = getQuarterCenturyEndYear(century, quarter, era);
872                 
873                 return new Date(endYear, 12, 31, era);
874         }
875         
876         /**
877          * Gets the first year of a given quarter century.
878          * 
879          * @param century The century, specified as a number ending in 00 or 01.
880          *                For centuries A.D., this is the first year of the century. For 
881          *                centuries B.C., this is the last year of the century. For example,
882          *                the "21st century" would be specified as 2001, whereas the "2000's"
883          *                would be specified as 2000. The "2nd century B.C." would be specified
884          *                as 101. The "100's B.C." would be specified as 100.
885          * @param quarter The quarter
886          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
887          * @return        The first year of the quarter century
888          */
889         public static int getQuarterCenturyStartYear(int century, int quarter, Era era) {
890                 if (era == null) {
891                         era = Date.DEFAULT_ERA;
892                 }
893                 
894                 int startYear;
895                 
896                 if (era == Era.BCE) {
897                         startYear = (century + 99) - (25 * (quarter - 1));
898                 }
899                 else {
900                         startYear = century + (25 * (quarter - 1));
901                 }
902                 
903                 return startYear;
904         }
905         
906         /**
907          * Gets the last year of a given quarter century.
908          * 
909          * @param century The century, specified as a number ending in 00 or 01.
910          *                For centuries A.D., this is the first year of the century. For 
911          *                centuries B.C., this is the last year of the century. For example,
912          *                the "21st century" would be specified as 2001, whereas the "2000's"
913          *                would be specified as 2000. The "2nd century B.C." would be specified
914          *                as 101. The "100's B.C." would be specified as 100.
915          * @param quarter The quarter
916          * @param era     The era of the century. If null, Date.DEFAULT_ERA is assumed.
917          * @return        The last year of the quarter century
918          */
919         public static int getQuarterCenturyEndYear(int century, int quarter, Era era) {
920                 if (era == null) {
921                         era = Date.DEFAULT_ERA;
922                 }
923                 
924                 int endYear;
925                 
926                 if (era == Era.BCE) {
927                         endYear = (century + 99) - (25 * quarter) + 1;
928                 }
929                 else {
930                         endYear = century + (25 * quarter) - 1;
931                 }
932                 
933                 return endYear;
934         }
935         
936         /**
937          * Converts an nth century number to a year. For example, to convert "21st century"
938          * to a year, call nthCenturyToYear(21), which returns 2001. For centuries A.D., the
939          * year returned is the first year of the nth century. For centuries B.C., the
940          * year returned is the last year of the nth century. 
941          * 
942          * @param n The nth century number
943          * @return  The first year in the nth century, for centuries A.D.
944          *          The last year of the nth century, for centuries B.C.
945          */
946         public static int nthCenturyToYear(int n) {
947                 int year = (n-1) * 100 + 1;
948                 
949                 return year;
950         }
951         
952         /**
953          * Gets the Date representing the first day of a given millennium.
954          * 
955          * @param n   The nth millennium number
956          * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
957          * @return    The first day of the millennium
958          */
959         public static Date getMillenniumStartDate(int n, Era era) {
960                 if (era == null) {
961                         era = Date.DEFAULT_ERA;
962                 }
963
964                 int startYear = getMillenniumStartYear(n, era);
965                 
966                 return new Date(startYear, 1, 1, era);
967         }
968
969         /**
970          * Gets the Date representing the last day of a given millennium.
971          * 
972          * @param n   The nth millennium number
973          * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
974          * @return    The last day of the millennium
975          */
976         public static Date getMillenniumEndDate(int n, Era era) {
977                 if (era == null) {
978                         era = Date.DEFAULT_ERA;
979                 }
980
981                 int endYear = getMillenniumEndYear(n, era);
982                 
983                 return new Date(endYear, 12, 31, era);
984         }
985         
986         /**
987          * Gets the first year of a given millennium.
988          * 
989          * @param n   The nth millennium number
990          * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
991          * @return    The first year of the millennium
992          */
993         public static int getMillenniumStartYear(int n, Era era) {
994                 if (era == null) {
995                         era = Date.DEFAULT_ERA;
996                 }
997                 
998                 int year;
999
1000                 if (era == Era.BCE) {
1001                         year = n * 1000;
1002                 }
1003                 else {
1004                         year = (n - 1) * 1000 + 1;
1005                 }
1006                 
1007                 return year;
1008         }
1009
1010         /**
1011          * Gets the last year of a given millennium.
1012          * 
1013          * @param n   The nth millennium number
1014          * @param era The era of the millennium. If null, Date.DEFAULT_ERA is assumed.
1015          * @return    The last year of the millennium
1016          */
1017         public static int getMillenniumEndYear(int n, Era era) {
1018                 if (era == null) {
1019                         era = Date.DEFAULT_ERA;
1020                 }
1021                 
1022                 int year;
1023
1024                 if (era == Era.BCE) {
1025                         year = (n - 1) * 1000 + 1;
1026                 }
1027                 else {
1028                         year = n * 1000;
1029                 }
1030                 
1031                 return year;
1032         }
1033         
1034         /**
1035          * Calculates the earliest date that may be considered to be "before"
1036          * a given date.
1037          * 
1038          * @param date The date
1039          * @return     The earliest date "before" the date
1040          */
1041         public static Date getEarliestBeforeDate(Date date) {
1042                 return getEarliestBeforeDate(date, null);
1043         }
1044         
1045         /**
1046          * Calculates the latest date that may be considered to be "after"
1047          * a given date.
1048          * 
1049          * @param date The date
1050          * @return     The latest date "after" the date
1051          */
1052         public static Date getLatestAfterDate(Date date) {
1053                 return getLatestAfterDate(date, null);
1054         }
1055         
1056         /**
1057          * Calculates the earliest date that may be considered to be "before"
1058          * a given date range.
1059          * 
1060          * @param startDate The first date in the range
1061          * @param endDate   The last date in the range
1062          * @return          The earliest date "before" the range
1063          */
1064         public static Date getEarliestBeforeDate(Date startDate, Date endDate) {
1065                 // TODO
1066                 return null;
1067                 
1068                 /*
1069                 // This algorithm is inherited from the XDB fuzzydate parser,
1070                 // which considers "before" to mean "within a lifetime before".
1071
1072                 if (endDate == null) {
1073                         endDate = startDate;
1074                 }
1075                 
1076                 int difference = getYearsBetween(startDate, endDate);
1077                 
1078                 Date earliestDate = startDate.copy();
1079                 subtractYears(earliestDate, 1);
1080                 earliestDate.setMonth(1);
1081                 earliestDate.setDay(1);
1082                 
1083                 if (difference < 100) {
1084                         // The comment from the XDB fuzzydate parser states:
1085                         //
1086                         //    Before/after years are really about birth/death dates
1087                         //    so we use average life-span of 75 years
1088
1089                         subtractYears(earliestDate, 75);
1090                 }
1091                 else {
1092                         // The comment from the XDB fuzzydate parser states:
1093                         //
1094                         //    Before/after years are really about birth/death dates
1095                         //    so we use average life-span of 75 years
1096                         //    but since  the spec was a century, e.g. pre 20th century
1097                         //    we'll make the range a bit bigger
1098                         //    sheesh...
1099
1100                         subtractYears(earliestDate, 175);
1101                 }
1102                 
1103                 return earliestDate;
1104                 */
1105         }
1106         
1107         /**
1108          * Calculates the latest date that may be considered to be "after"
1109          * a given date range.
1110          * 
1111          * @param startDate The first date in the range
1112          * @param endDate   The last date in the range
1113          * @return          The latest date "after" the range
1114          */
1115         public static Date getLatestAfterDate(Date startDate, Date endDate) {
1116                 // TODO
1117                 return null;
1118         }
1119
1120         public static int getYearsBetween(Date startDate, Date endDate) {
1121                 if (startDate == null || endDate == null) {
1122                         throw new InvalidDateException("date must not be null");
1123                 }
1124                 
1125                 Integer startYear = startDate.getYear();
1126                 Integer endYear = endDate.getYear();
1127                 
1128                 if (startYear == null || endYear == null) {
1129                         throw new IllegalArgumentException("year must not be null");
1130                 }
1131                 
1132                 Era startEra = startDate.getEra();
1133                 Era endEra = endDate.getEra();
1134                 
1135                 if (startEra == null || endEra == null) {
1136                         throw new IllegalArgumentException("era must not be null");
1137                 }
1138                 
1139                 MutableDateTime startDateTime = convertToDateTime(startDate);
1140                 MutableDateTime endDateTime = convertToDateTime(endDate);
1141                 
1142                 int years = Years.yearsBetween(startDateTime, endDateTime).getYears();
1143                 
1144                 return years;
1145         }
1146         
1147         /**
1148          * Calculates the interval, in years, that should be padded around a date so
1149          * that any date within that interval may be considered to be "circa" the
1150          * given date. 
1151          * 
1152          * @param  year The year of the date
1153          * @param  era  The era of the date. If null, Date.DEFAULT_ERA is assumed.
1154          * @return      The number of "circa" years before and after the date
1155          */
1156         public static int getCircaIntervalYears(int year, Era era) {
1157                 /*
1158                  * This algorithm is inherited from the fuzzydate parser
1159                  * in XDB. Its comment states:
1160                  * 
1161                  *   We define circa year/century specifications offsets
1162                  *   as +/- 5% of the difference between that year/century
1163                  *   and the present (2100), so that the farther we go back
1164                  *   in time, the wider the range of meaning of "circa."
1165                  *    
1166                  */
1167                 
1168                 if (era == null) {
1169                         era = Date.DEFAULT_ERA;
1170                 }
1171                 
1172                 MutableDateTime dateTime = new MutableDateTime(chronology);
1173                 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1174                 dateTime.yearOfEra().set(year);
1175                 dateTime.monthOfYear().set(1);
1176                 dateTime.dayOfMonth().set(1);
1177                 dateTime.setTime(0, 0, 0, 0);
1178                 
1179                 int years = Years.yearsBetween(dateTime, circaBaseDateTime).getYears();
1180
1181                 return ((int) Math.round(years * 0.05));
1182         }
1183         
1184         /**
1185          * Adds a number of days to a date.
1186          * 
1187          * @param date The date 
1188          * @param days The number of days to add to the date
1189          */
1190         public static void addDays(Date date, int days) {
1191                 MutableDateTime dateTime = convertToDateTime(date);
1192                 
1193                 dateTime.add(Days.days(days));
1194                 
1195                 setFromDateTime(date, dateTime);
1196         }
1197         
1198         /**
1199          * Adds a number of years to a date's year.
1200          * 
1201          * @param date  The date        
1202          * @param years The number of years to add to the date
1203          */
1204         public static void addYears(Date date, int years) {
1205                 MutableDateTime dateTime = convertToDateTime(date);
1206
1207                 dateTime.add(Years.years(years));
1208                 
1209                 setFromDateTime(date, dateTime);
1210         }
1211         
1212         /**
1213          * Subtracts a number of years from a date's year.
1214          * 
1215          * @param date  The date        
1216          * @param years The number of years to subtract from the date
1217          */
1218         public static void subtractYears(Date date, int years) {
1219                 addYears(date, -years);
1220         }
1221         
1222         public static String getEarliestTimestamp(Date date) {
1223                 Era era = date.getEra();
1224                 
1225                 if (era == null) {
1226                         era = Date.DEFAULT_ERA;
1227                 }
1228                 
1229                 MutableDateTime dateTime = null;
1230                 
1231                 try {
1232                         dateTime = convertToDateTime(date);
1233                 }
1234                 catch(IllegalFieldValueException e) {
1235                         throw new InvalidDateException(e.getMessage());
1236                 }
1237                 
1238                 String scalarDate = scalarDateFormatter.print(dateTime);
1239                 
1240                 return scalarDate;
1241         }
1242         
1243         public static String getLatestTimestamp(Date date) {
1244                 Era era = date.getEra();
1245                 
1246                 if (era == null) {
1247                         era = Date.DEFAULT_ERA;
1248                 }
1249                 
1250                 MutableDateTime dateTime = null;
1251                 
1252                 try {
1253                         dateTime = convertToDateTime(date);
1254                 }
1255                 catch(IllegalFieldValueException e) {
1256                         throw new InvalidDateException(e.getMessage());
1257                 }
1258                 
1259                 dateTime.setTime(23, 59, 59, 999);
1260                 
1261                 String scalarDate = scalarDateFormatter.print(dateTime);
1262                 
1263                 return scalarDate;
1264         }
1265         
1266         public static boolean isValidDate(int year, int month, int day, Era era) {
1267                 boolean isValid = true;
1268                 
1269                 try {
1270                         convertToDateTime(new Date(year, month,day, era));
1271                 }
1272                 catch(IllegalFieldValueException e) {
1273                         isValid = false;
1274                 }
1275                 
1276                 return isValid;
1277         }
1278         
1279         /**
1280          * Converts a Date to a joda-time DateTime.
1281          * 
1282          * @param  date The Date
1283          * @return      A MutableDateTime representing the same date
1284          */
1285         private static MutableDateTime convertToDateTime(Date date) {
1286                 Era era = date.getEra();
1287                 
1288                 if (era == null) {
1289                         era = Date.DEFAULT_ERA;
1290                 }
1291
1292                 MutableDateTime dateTime = new MutableDateTime(chronology);
1293                 dateTime.era().set((era == Era.BCE) ? DateTimeConstants.BC : DateTimeConstants.AD);
1294                 dateTime.yearOfEra().set(date.getYear());
1295                 dateTime.monthOfYear().set(date.getMonth());
1296                 dateTime.dayOfMonth().set(date.getDay());
1297                 dateTime.setTime(0, 0, 0, 0);
1298
1299                 return dateTime;
1300         }
1301         
1302         /**
1303          * Sets the fields in a Date so that it represents the same date
1304          * as a given DateTime.
1305          * 
1306          * @param date     The Date to set
1307          * @param dateTime A MutableDateTime representing the desired date
1308          */
1309         private static void setFromDateTime(Date date, MutableDateTime dateTime) {
1310                 date.setYear(dateTime.getYearOfEra());
1311                 date.setMonth(dateTime.getMonthOfYear());
1312                 date.setDay(dateTime.getDayOfMonth());
1313                 date.setEra((dateTime.getEra() == DateTimeConstants.BC) ? Era.BCE : Era.CE);
1314         }
1315 }