1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.common;
20
21 import java.util.Arrays;
22 import java.util.Date;
23 import java.util.List;
24 import java.util.Locale;
25
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.logging.log4j.LogManager;
28 import org.apache.logging.log4j.Logger;
29
30 import com.ibm.icu.text.SimpleDateFormat;
31 import com.ibm.icu.util.BuddhistCalendar;
32 import com.ibm.icu.util.Calendar;
33 import com.ibm.icu.util.CopticCalendar;
34 import com.ibm.icu.util.EthiopicCalendar;
35 import com.ibm.icu.util.GregorianCalendar;
36 import com.ibm.icu.util.HebrewCalendar;
37 import com.ibm.icu.util.IslamicCalendar;
38 import com.ibm.icu.util.JapaneseCalendar;
39 import com.ibm.icu.util.ULocale;
40
41
42
43
44
45
46
47
48
49
50 public class MCRCalendar {
51
52
53
54
55 private static final Logger LOGGER = LogManager.getLogger(MCRCalendar.class.getName());
56
57
58
59
60 public static final String TAG_BUDDHIST = "buddhist";
61
62
63
64
65 public static final String TAG_CHINESE = "chinese";
66
67
68
69
70 public static final String TAG_COPTIC = "coptic";
71
72
73
74
75 public static final String TAG_ETHIOPIC = "ethiopic";
76
77
78
79
80 public static final String TAG_GREGORIAN = "gregorian";
81
82
83
84
85 public static final String TAG_HEBREW = "hebrew";
86
87
88
89
90 public static final String TAG_ISLAMIC = "islamic";
91
92
93
94
95 public static final String TAG_JAPANESE = "japanese";
96
97
98
99
100 public static final String TAG_JULIAN = "julian";
101
102
103
104
105 public static final String TAG_PERSIC = "persic";
106
107
108
109
110 public static final String TAG_ARMENIAN = "armenian";
111
112
113
114
115 public static final String TAG_EGYPTIAN = "egyptian";
116
117
118
119
120 public static final int MIN_JULIAN_DAY_NUMBER = 0;
121
122
123
124
125 public static final int MAX_JULIAN_DAY_NUMBER = 3182057;
126
127
128
129
130 public static final List<String> CALENDARS_LIST = List.of(
131 TAG_GREGORIAN, TAG_JULIAN, TAG_ISLAMIC, TAG_BUDDHIST, TAG_COPTIC, TAG_ETHIOPIC, TAG_PERSIC, TAG_JAPANESE,
132 TAG_ARMENIAN, TAG_EGYPTIAN, TAG_HEBREW);
133
134
135
136
137 public static final int FIRST_ARMENIAN_DAY;
138
139
140
141
142 public static final int FIRST_EGYPTIAN_DAY;
143
144 static {
145 final Calendar firstArmenian = GregorianCalendar.getInstance();
146 firstArmenian.set(552, GregorianCalendar.JULY, 13);
147 FIRST_ARMENIAN_DAY = firstArmenian.get(Calendar.JULIAN_DAY);
148
149 final Calendar firstEgypt = GregorianCalendar.getInstance();
150 firstEgypt.set(747, GregorianCalendar.FEBRUARY, 18);
151 firstEgypt.set(Calendar.ERA, GregorianCalendar.BC);
152 FIRST_EGYPTIAN_DAY = firstEgypt.get(Calendar.JULIAN_DAY);
153 }
154
155 private static final String MSG_CALENDAR_UNSUPPORTED = "Calendar %s is not supported!";
156
157
158
159
160 public static Calendar getHistoryDateAsCalendar(String input, boolean last, CalendarType calendarType) {
161 LOGGER.debug("Input of getHistoryDateAsCalendar: {} {} {}", input, calendarType, Boolean.toString(last));
162
163 final String dateString = StringUtils.trim(input);
164
165 if (StringUtils.isBlank(dateString)) {
166 throw new MCRException("The ancient date string is null or empty");
167 }
168
169 final Calendar out;
170 if (dateString.equals("4713-01-01 BC")) {
171 LOGGER.debug("Date string contains MIN_JULIAN_DAY_NUMBER");
172 out = new GregorianCalendar();
173 out.set(Calendar.JULIAN_DAY, MCRCalendar.MIN_JULIAN_DAY_NUMBER);
174 return out;
175 }
176 if (dateString.equals("4000-01-28 AD")) {
177 LOGGER.debug("Date string contains MAX_JULIAN_DAY_NUMBER");
178 out = new GregorianCalendar();
179 out.set(Calendar.JULIAN_DAY, MCRCalendar.MAX_JULIAN_DAY_NUMBER);
180 return out;
181 }
182
183 switch (calendarType) {
184 case Armenian: {
185 out = getCalendarFromArmenianDate(dateString, last);
186 break;
187 }
188 case Buddhist: {
189 out = getCalendarFromBuddhistDate(dateString, last);
190 break;
191 }
192 case Coptic: {
193 out = getCalendarFromCopticDate(dateString, last);
194 break;
195 }
196 case Egyptian: {
197 out = getCalendarFromEgyptianDate(dateString, last);
198 break;
199 }
200 case Ethiopic: {
201 out = getCalendarFromEthiopicDate(dateString, last);
202 break;
203 }
204 case Gregorian: {
205 out = getCalendarFromGregorianDate(dateString, last);
206 break;
207 }
208 case Hebrew: {
209 out = getCalendarFromHebrewDate(dateString, last);
210 break;
211 }
212 case Islamic: {
213 out = getCalendarFromIslamicDate(dateString, last);
214 break;
215 }
216 case Japanese: {
217 out = getCalendarFromJapaneseDate(dateString, last);
218 break;
219 }
220 case Julian: {
221 out = getCalendarFromJulianDate(dateString, last);
222 break;
223 }
224 case Persic: {
225 out = getCalendarFromPersicDate(dateString, last);
226 break;
227 }
228 default: {
229 throw new MCRException("Calendar type " + calendarType + " not supported!");
230 }
231 }
232
233 LOGGER.debug("Output of getHistoryDateAsCalendar: {}", getCalendarDateToFormattedString(out));
234 return out;
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 public static Calendar getHistoryDateAsCalendar(String dateString, boolean last, String calendarString)
252 throws MCRException {
253 return getHistoryDateAsCalendar(dateString, last, CalendarType.of(calendarString));
254 }
255
256
257
258
259
260
261
262
263 private static int[] checkDateStringForJulianCalendar(String dateString, boolean last, CalendarType calendarType) {
264
265 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(dateString, Locale.ROOT));
266
267 final boolean bc = beforeZero(dateTrimmed, calendarType);
268 final String cleanDate = cleanDate(dateTrimmed, calendarType);
269 final int[] fields = parseDateString(cleanDate, last, calendarType);
270 final int year = fields[0];
271 final int mon = fields[1];
272 final int day = fields[2];
273 final int era = bc ? -1 : 1;
274
275
276 if (mon > GregorianCalendar.DECEMBER || mon < GregorianCalendar.JANUARY) {
277 throw new MCRException("The month of the date is inadmissible.");
278 }
279
280
281 if (day > 31) {
282 throw new MCRException("The day of the date is inadmissible.");
283 } else if ((day > 30) && (mon == GregorianCalendar.APRIL || mon == GregorianCalendar.JUNE ||
284 mon == GregorianCalendar.SEPTEMBER || mon == GregorianCalendar.NOVEMBER)) {
285 throw new MCRException("The day of the date is inadmissible.");
286 } else if ((day > 29) && (mon == GregorianCalendar.FEBRUARY)) {
287 throw new MCRException("The day of the date is inadmissible.");
288 } else if ((day > 28) && (mon == GregorianCalendar.FEBRUARY) && !isLeapYear(year, calendarType)) {
289 throw new MCRException("The day of the date is inadmissible.");
290 }
291
292 return new int[] { year, mon, day, era };
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320 protected static GregorianCalendar getCalendarFromGregorianDate(String dateString, boolean last)
321 throws MCRException {
322 try {
323 int[] fields = checkDateStringForJulianCalendar(dateString, last, CalendarType.Gregorian);
324 GregorianCalendar calendar = new GregorianCalendar();
325 calendar.set(fields[0], fields[1], fields[2]);
326 if (fields[3] == -1) {
327 calendar.set(Calendar.ERA, GregorianCalendar.BC);
328 } else {
329 calendar.set(Calendar.ERA, GregorianCalendar.AD);
330 }
331 return calendar;
332 } catch (Exception e) {
333 throw new MCRException("The ancient gregorian date is false.", e);
334 }
335 }
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362 protected static Calendar getCalendarFromJulianDate(String dateString, boolean last) throws MCRException {
363 try {
364 int[] fields = checkDateStringForJulianCalendar(dateString, last, CalendarType.Julian);
365 final Calendar calendar = Calendar.getInstance(CalendarType.Julian.getLocale());
366 ((GregorianCalendar) calendar).setGregorianChange(new Date(Long.MAX_VALUE));
367 calendar.set(fields[0], fields[1], fields[2]);
368 if (fields[3] == -1) {
369 calendar.set(Calendar.ERA, GregorianCalendar.BC);
370 } else {
371 calendar.set(Calendar.ERA, GregorianCalendar.AD);
372 }
373
374 return calendar;
375 } catch (Exception e) {
376 throw new MCRException("The ancient julian date is false.", e);
377 }
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399 protected static IslamicCalendar getCalendarFromIslamicDate(String dateString, boolean last) {
400 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(dateString, Locale.ROOT));
401
402 final boolean before = beforeZero(dateTrimmed, CalendarType.Islamic);
403 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Islamic);
404 final int[] fields = parseDateString(cleanDate, last, CalendarType.Islamic);
405 int year = fields[0];
406 final int mon = fields[1];
407 final int day = fields[2];
408
409 if (before) {
410 year = -year + 1;
411 }
412
413
414 if (mon > 11 || mon < 0) {
415 throw new MCRException("The month of the date is inadmissible.");
416 }
417
418 if (day > 30 || mon % 2 == 1 && mon < 11 && day > 29 || day < 1) {
419 throw new MCRException("The day of the date is inadmissible.");
420 }
421
422 IslamicCalendar calendar = new IslamicCalendar();
423 calendar.setCivil(true);
424 calendar.set(year, mon, day);
425
426 return calendar;
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445 protected static HebrewCalendar getCalendarFromHebrewDate(String datestr, boolean last) {
446 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(datestr, Locale.ROOT));
447
448 final boolean before = beforeZero(dateTrimmed, CalendarType.Hebrew);
449 if (before) {
450 throw new MCRException("Dates before 1 not supported in Hebrew calendar!");
451 }
452
453 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Hebrew);
454 final int[] fields = parseDateString(cleanDate, last, CalendarType.Hebrew);
455 final int year = fields[0];
456 final int mon = fields[1];
457 final int day = fields[2];
458
459 HebrewCalendar hcal = new HebrewCalendar();
460 hcal.set(year, mon, day);
461
462 return hcal;
463 }
464
465
466
467
468
469
470
471
472 private static int[] checkDateStringForCopticCalendar(String dateString, boolean last, CalendarType calendarType) {
473 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(dateString, Locale.ROOT));
474
475 final boolean bm = beforeZero(dateTrimmed, calendarType);
476 final String cleanDate = cleanDate(dateTrimmed, calendarType);
477 final int[] fields = parseDateString(cleanDate, last, calendarType);
478 int year = fields[0];
479 final int mon = fields[1];
480 final int day = fields[2];
481 final int era = bm ? -1 : 1;
482
483 if (bm) {
484 year = -year + 1;
485 }
486
487
488 if (mon > 12 || mon < 0) {
489 throw new MCRException("The month of the date is inadmissible.");
490 }
491
492 if (day > 30 || day < 1 || day > 6 && mon == 12) {
493 throw new MCRException("The day of the date is inadmissible.");
494 }
495
496 return new int[] { year, mon, day, era };
497 }
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517 protected static CopticCalendar getCalendarFromCopticDate(String dateString, boolean last) {
518 try {
519 final int[] fields = checkDateStringForCopticCalendar(dateString, last, CalendarType.Coptic);
520 CopticCalendar calendar = new CopticCalendar();
521 calendar.set(fields[0], fields[1], fields[2]);
522 return calendar;
523 } catch (Exception e) {
524 throw new MCRException("The ancient coptic calendar date is false.", e);
525 }
526 }
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546 protected static EthiopicCalendar getCalendarFromEthiopicDate(String dateString, boolean last) {
547 try {
548 final int[] fields = checkDateStringForCopticCalendar(dateString, last, CalendarType.Ethiopic);
549 EthiopicCalendar calendar = new EthiopicCalendar();
550 calendar.set(fields[0], fields[1], fields[2]);
551 return calendar;
552 } catch (Exception e) {
553 throw new MCRException("The ancient ethiopic calendar date is false.", e);
554 }
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577 protected static JapaneseCalendar getCalendarFromJapaneseDate(String datestr, boolean last) {
578 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(datestr, Locale.ROOT));
579 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Japanese);
580
581
582
583 final String eraToken;
584 final int era;
585 if (StringUtils.contains(cleanDate, "M")) {
586 eraToken = "M";
587 era = JapaneseCalendar.MEIJI;
588 } else if (StringUtils.contains(cleanDate, "T")) {
589 eraToken = "T";
590 era = JapaneseCalendar.TAISHO;
591 } else if (StringUtils.contains(cleanDate, "S")) {
592 eraToken = "S";
593 era = JapaneseCalendar.SHOWA;
594 } else if (StringUtils.contains(cleanDate, "H")) {
595 eraToken = "H";
596 era = JapaneseCalendar.HEISEI;
597 } else if (StringUtils.contains(cleanDate, "R")) {
598 eraToken = "R";
599 era = JapaneseCalendar.REIWA;
600 } else {
601 throw new MCRException("Japanese date " + datestr + " does not contain era statement!");
602 }
603
604 final String firstPart = StringUtils.substringBefore(cleanDate, eraToken);
605 final String secondPart = StringUtils.substringAfter(cleanDate, eraToken);
606
607 final int[] fields = parseDateString(firstPart + secondPart, last, CalendarType.Japanese);
608 final int year = fields[0];
609 final int mon = fields[1];
610 final int day = fields[2];
611
612 JapaneseCalendar jcal = new JapaneseCalendar();
613 jcal.set(year, mon, day);
614 jcal.set(Calendar.ERA, era);
615
616 return jcal;
617 }
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639 protected static BuddhistCalendar getCalendarFromBuddhistDate(String datestr, boolean last) {
640
641 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(datestr, Locale.ROOT));
642
643 final boolean bb = beforeZero(dateTrimmed, CalendarType.Buddhist);
644 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Buddhist);
645 final int[] fields = parseDateString(cleanDate, last, CalendarType.Buddhist);
646 int year = fields[0];
647 int mon = fields[1];
648 int day = fields[2];
649
650 if (bb) {
651 year = -year + 1;
652 }
653
654 if (year == 2125 && mon == 9 && day >= 5 && day < 15) {
655 day = 15;
656 }
657
658 BuddhistCalendar budcal = new BuddhistCalendar();
659 budcal.set(year, mon, day);
660
661 return budcal;
662 }
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683 protected static GregorianCalendar getCalendarFromPersicDate(String datestr, boolean last) {
684 try {
685 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(datestr, Locale.ROOT));
686
687 final boolean bb = beforeZero(dateTrimmed, CalendarType.Persic);
688 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Persic);
689 final int[] fields = parseDateString(cleanDate, last, CalendarType.Persic);
690 final int year = fields[0];
691 final int mon = fields[1];
692 final int day = fields[2];
693
694 final int njahr;
695 if (bb) {
696 njahr = -year + 1 + 621;
697 } else {
698 njahr = year + 621;
699 }
700
701 GregorianCalendar newdate = new GregorianCalendar();
702 newdate.clear();
703 newdate.set(njahr, Calendar.MARCH, 20);
704
705
706 int begday = 0;
707 if (mon == 1) {
708 begday = 31;
709 }
710 if (mon == 2) {
711 begday = 62;
712 }
713 if (mon == 3) {
714 begday = 93;
715 }
716 if (mon == 4) {
717 begday = 124;
718 }
719 if (mon == 5) {
720 begday = 155;
721 }
722 if (mon == 6) {
723 begday = 186;
724 }
725 if (mon == 7) {
726 begday = 216;
727 }
728 if (mon == 8) {
729 begday = 246;
730 }
731 if (mon == 9) {
732 begday = 276;
733 }
734 if (mon == 10) {
735 begday = 306;
736 }
737 if (mon == 11) {
738 begday = 336;
739 }
740 begday += day - 1;
741
742 int jh = njahr / 100;
743 int b = jh % 4;
744 int c = njahr % 100;
745 int d = c / 4;
746
747 final int min = b * 360 + 350 * c - d * 1440 + 720;
748 if (njahr >= 0) {
749 newdate.add(Calendar.MINUTE, min);
750 newdate.add(Calendar.DATE, begday);
751 } else {
752 newdate.add(Calendar.DATE, begday + 2);
753 newdate.add(Calendar.MINUTE, min);
754 }
755
756 return newdate;
757 } catch (Exception e) {
758 throw new MCRException("The ancient persian date is false.", e);
759 }
760 }
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783 protected static GregorianCalendar getCalendarFromArmenianDate(String datestr, boolean last) {
784 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(datestr, Locale.ROOT));
785
786 final boolean before = beforeZero(dateTrimmed, CalendarType.Armenian);
787 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Armenian);
788 final int[] fields = parseDateString(cleanDate, last, CalendarType.Armenian);
789 int year = fields[0];
790 int mon = fields[1];
791 int day = fields[2];
792
793 if (before) {
794 year = -year + 1;
795 }
796
797
798
799 int addedDays = (year - 1) * 365;
800 if (mon == 12) {
801 addedDays += 12 * 30;
802 } else {
803 addedDays += mon * 30;
804 }
805 addedDays += day - 1;
806
807 final GregorianCalendar result = new GregorianCalendar();
808 result.set(Calendar.JULIAN_DAY, (FIRST_ARMENIAN_DAY + addedDays));
809
810 return result;
811 }
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832 protected static GregorianCalendar getCalendarFromEgyptianDate(String datestr, boolean last) {
833 final String dateTrimmed = StringUtils.trim(StringUtils.upperCase(datestr, Locale.ROOT));
834
835 final boolean ba = beforeZero(dateTrimmed, CalendarType.Egyptian);
836 final String cleanDate = cleanDate(dateTrimmed, CalendarType.Egyptian);
837 final int[] fields = parseDateString(cleanDate, last, CalendarType.Egyptian);
838 int year = fields[0];
839 final int mon = fields[1];
840 final int day = fields[2];
841
842 if (ba) {
843 year = -year + 1;
844 }
845
846 int addedDays = (year - 1) * 365;
847 if (mon == 12) {
848 addedDays += 12 * 30;
849 } else {
850 addedDays += mon * 30;
851 }
852 addedDays += day - 1;
853
854 final GregorianCalendar result = new GregorianCalendar();
855 result.set(Calendar.JULIAN_DAY, (FIRST_EGYPTIAN_DAY + addedDays));
856
857 return result;
858 }
859
860
861
862
863
864
865 public static int getJulianDayNumber(Calendar calendar) {
866 return calendar.get(Calendar.JULIAN_DAY);
867 }
868
869
870
871
872
873
874 public static String getJulianDayNumberAsString(Calendar calendar) {
875 return Integer.toString(calendar.get(Calendar.JULIAN_DAY));
876 }
877
878
879
880
881
882
883
884
885 public static GregorianCalendar getGregorianCalendarOfACalendar(Calendar calendar) {
886 int julianDay = getJulianDayNumber(calendar);
887 GregorianCalendar ret = new GregorianCalendar();
888 ret.set(Calendar.JULIAN_DAY, julianDay);
889 return ret;
890 }
891
892
893
894
895
896
897 public static String getCalendarDateToFormattedString(Calendar calendar) {
898 if (calendar instanceof IslamicCalendar) {
899 return getCalendarDateToFormattedString(calendar, "dd.MM.yyyy");
900 } else if (calendar instanceof GregorianCalendar) {
901 return getCalendarDateToFormattedString(calendar, "yyyy-MM-dd G");
902 }
903 return getCalendarDateToFormattedString(calendar, "yyyy-MM-dd G");
904 }
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919 public static String getCalendarDateToFormattedString(Calendar calendar, String format) {
920 if (calendar == null || format == null || format.trim().length() == 0) {
921 return "";
922 }
923 SimpleDateFormat formatter = null;
924 try {
925 if (calendar instanceof IslamicCalendar) {
926 formatter = new SimpleDateFormat(format, new Locale("en"));
927 } else if (calendar instanceof GregorianCalendar) {
928 formatter = new SimpleDateFormat(format, new Locale("en"));
929 } else {
930 formatter = new SimpleDateFormat(format, new Locale("en"));
931 }
932 } catch (Exception e) {
933 formatter = new SimpleDateFormat("dd.MM.yyyy G", new Locale("en"));
934 }
935 try {
936 formatter.setCalendar(calendar);
937 if (calendar instanceof IslamicCalendar) {
938 return formatter.format(calendar.getTime()) + " h.";
939 } else if (calendar instanceof CopticCalendar) {
940 return formatter.format(calendar.getTime()) + " A.M.";
941 } else if (calendar instanceof EthiopicCalendar) {
942 return formatter.format(calendar.getTime()) + " E.E.";
943 } else {
944 return formatter.format(calendar.getTime());
945 }
946 } catch (Exception e) {
947 return "";
948 }
949 }
950
951
952
953
954
955
956
957
958
959
960 public static String getISODateToFormattedString(String date, boolean useLastValue, String calendarName) {
961 String formattedDate = null;
962 try {
963 Calendar calendar = MCRCalendar.getHistoryDateAsCalendar(date, useLastValue, calendarName);
964 GregorianCalendar gregorianCalendar = MCRCalendar.getGregorianCalendarOfACalendar(calendar);
965 formattedDate = MCRCalendar.getCalendarDateToFormattedString(gregorianCalendar, "yyyy-MM-dd")
966 + "T00:00:00.000Z";
967 if (gregorianCalendar.get(Calendar.ERA) == GregorianCalendar.BC) {
968 formattedDate = "-" + formattedDate;
969 }
970 } catch (Exception e) {
971 String errorMsg = "Error while converting date string : " + date + " - " + useLastValue +
972 " - " + calendarName;
973 if (LOGGER.isDebugEnabled()) {
974 LOGGER.debug(errorMsg, e);
975 }
976 LOGGER.warn(errorMsg);
977 return "";
978 }
979 return formattedDate;
980 }
981
982
983
984
985
986
987
988 public static String getCalendarTypeString(Calendar calendar) {
989 if (calendar == null) {
990 return "";
991 }
992 if (calendar instanceof IslamicCalendar) {
993 return TAG_ISLAMIC;
994 } else if (calendar instanceof BuddhistCalendar) {
995 return TAG_BUDDHIST;
996 } else if (calendar instanceof CopticCalendar) {
997 return TAG_COPTIC;
998 } else if (calendar instanceof EthiopicCalendar) {
999 return TAG_ETHIOPIC;
1000 } else if (calendar instanceof HebrewCalendar) {
1001 return TAG_HEBREW;
1002 } else if (calendar instanceof JapaneseCalendar) {
1003 return TAG_JAPANESE;
1004 } else if (calendar instanceof GregorianCalendar) {
1005 return TAG_GREGORIAN;
1006 } else {
1007 return TAG_JULIAN;
1008 }
1009 }
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021 public static int[] parseDateString(String dateString, boolean last, CalendarType calendarType) {
1022
1023 final boolean iso = isoFormat(dateString);
1024 final String delimiter = delimiter(dateString);
1025
1026
1027 final int firstdot = StringUtils.indexOf(dateString, delimiter, 1);
1028 final int secdot = StringUtils.indexOf(dateString, delimiter, firstdot + 1);
1029
1030 final int day;
1031 final int mon;
1032 final int year;
1033 if (secdot != -1) {
1034
1035 final int firstPart = Integer.parseInt(StringUtils.substring(dateString, 0, firstdot));
1036 final int secondPart = Integer.parseInt(StringUtils.substring(dateString, firstdot + 1, secdot));
1037 final int thirdPart = Integer.parseInt(StringUtils.substring(dateString, secdot + 1));
1038 if (iso) {
1039 year = firstPart;
1040 mon = secondPart - 1;
1041 day = thirdPart;
1042 } else {
1043 day = firstPart;
1044 mon = secondPart - 1;
1045 year = thirdPart;
1046 }
1047 } else {
1048 if (firstdot != -1) {
1049
1050 final int firstPart = Integer.parseInt(StringUtils.substring(dateString, 0, firstdot));
1051 final int secondPart = Integer.parseInt(StringUtils.substring(dateString, firstdot + 1));
1052 if (iso) {
1053 year = firstPart;
1054 mon = secondPart - 1;
1055 } else {
1056 mon = firstPart - 1;
1057 year = secondPart;
1058 }
1059
1060 if (last) {
1061 day = getLastDayOfMonth(mon, year, calendarType);
1062 } else {
1063 day = 1;
1064 }
1065 } else {
1066
1067 year = Integer.parseInt(dateString);
1068 if (last) {
1069 mon = getLastMonth(year, calendarType);
1070 day = getLastDayOfMonth(mon, year, calendarType);
1071 } else {
1072 mon = getFirstMonth(calendarType);
1073 day = 1;
1074 }
1075 }
1076 }
1077
1078 return new int[] { year, mon, day };
1079 }
1080
1081
1082
1083
1084
1085
1086
1087 public static boolean isoFormat(String input) {
1088 return -1 != StringUtils.indexOf(input, "-", 1);
1089 }
1090
1091
1092
1093
1094
1095
1096
1097
1098 public static String cleanDate(String input, CalendarType calendarType) {
1099 final String date = StringUtils.trim(StringUtils.upperCase(input, Locale.ROOT));
1100 final int start;
1101 final int end;
1102 final int length = StringUtils.length(date);
1103
1104 if (StringUtils.startsWith(date, "-")) {
1105 start = 1;
1106 end = length;
1107 } else {
1108 final int[] borders;
1109 switch (calendarType) {
1110 case Armenian: {
1111 borders = calculateArmenianDateBorders(date);
1112 break;
1113 }
1114 case Buddhist: {
1115 borders = calculateBuddhistDateBorders(date);
1116 break;
1117 }
1118 case Coptic:
1119 case Ethiopic: {
1120 borders = calculateCopticDateBorders(date);
1121 break;
1122 }
1123 case Egyptian: {
1124 borders = calculateEgyptianDateBorders(date);
1125 break;
1126 }
1127 case Gregorian:
1128 case Julian: {
1129 borders = calculateGregorianDateBorders(date);
1130 break;
1131 }
1132 case Hebrew: {
1133 borders = calculateHebrewDateBorders(date);
1134 break;
1135 }
1136 case Islamic: {
1137 borders = calculateIslamicDateBorders(date);
1138 break;
1139 }
1140 case Japanese: {
1141 borders = calculateJapaneseDateBorders(date);
1142 break;
1143 }
1144 case Persic: {
1145 borders = calculatePersianDateBorders(date);
1146 break;
1147 }
1148 default: {
1149 throw new MCRException(String.format(Locale.ROOT, MSG_CALENDAR_UNSUPPORTED, calendarType));
1150 }
1151 }
1152
1153 start = borders[0];
1154 end = borders[1];
1155 }
1156
1157 return StringUtils.trim(StringUtils.substring(date, start, end));
1158 }
1159
1160
1161
1162
1163
1164
1165
1166 public static int[] calculateEgyptianDateBorders(String datestr) {
1167 final int start;
1168 final int ende;
1169 final int length = StringUtils.length(datestr);
1170
1171 if (StringUtils.startsWith(datestr, "-")) {
1172 start = 1;
1173 } else {
1174 start = 0;
1175 }
1176
1177 if (StringUtils.contains(datestr, "A.N.")) {
1178 ende = StringUtils.indexOf(datestr, "A.N.");
1179 } else {
1180 ende = length;
1181 }
1182
1183 return new int[] { start, ende };
1184 }
1185
1186
1187
1188
1189
1190
1191
1192 public static int[] calculateArmenianDateBorders(String input) {
1193 final int start;
1194 if (StringUtils.startsWith(input, "-")) {
1195 start = 1;
1196 } else {
1197 start = 0;
1198 }
1199
1200 return new int[] { start, StringUtils.length(input) };
1201 }
1202
1203
1204
1205
1206
1207
1208
1209 public static int[] calculateJapaneseDateBorders(String input) {
1210 final int start;
1211 if (StringUtils.startsWith(input, "-")) {
1212 start = 1;
1213 } else {
1214 start = 0;
1215 }
1216
1217 return new int[] { start, StringUtils.length(input) };
1218 }
1219
1220
1221
1222
1223
1224
1225
1226 public static int[] calculatePersianDateBorders(String dateStr) {
1227 final int start;
1228 if (StringUtils.startsWith(dateStr, "-")) {
1229 start = 1;
1230 } else {
1231 start = 0;
1232 }
1233
1234 return new int[] { start, StringUtils.length(dateStr) };
1235 }
1236
1237
1238
1239
1240
1241
1242
1243 public static int[] calculateCopticDateBorders(String input) {
1244 final int start;
1245 final int end;
1246 final int length = StringUtils.length(input);
1247
1248 if (StringUtils.startsWith(input, "-")) {
1249 start = 1;
1250 end = length;
1251 } else {
1252 start = 0;
1253
1254 if (StringUtils.contains(input, "A.M")) {
1255 end = StringUtils.indexOf(input, "A.M.");
1256 } else if (StringUtils.contains(input, "E.E.")) {
1257 end = StringUtils.indexOf(input, "E.E.");
1258 } else {
1259 end = length;
1260 }
1261 }
1262
1263 return new int[] { start, end };
1264 }
1265
1266
1267
1268
1269
1270
1271
1272 public static int[] calculateHebrewDateBorders(String input) {
1273 return new int[] { 0, StringUtils.length(input) };
1274 }
1275
1276
1277
1278
1279
1280
1281
1282 public static int[] calculateIslamicDateBorders(String dateString) {
1283 int start = 0;
1284 int ende = dateString.length();
1285 int i = dateString.indexOf("H.");
1286 if (i != -1) {
1287 ende = i;
1288 }
1289 if (dateString.length() > 10) {
1290 i = dateString.indexOf(".\u0647.\u0642");
1291 if (i != -1) {
1292 start = 3;
1293 } else {
1294 i = dateString.indexOf(".\u0647");
1295 if (i != -1) {
1296 start = 2;
1297 }
1298 }
1299 }
1300
1301 return new int[] { start, ende };
1302 }
1303
1304
1305
1306
1307
1308
1309
1310 public static int[] calculateGregorianDateBorders(String dateString) {
1311 final int start;
1312 final int end;
1313 final int length = StringUtils.length(dateString);
1314
1315 if (StringUtils.contains(dateString, "N. CHR") || StringUtils.contains(dateString, "V. CHR")) {
1316 final int positionNChr = StringUtils.indexOf(dateString, "N. CHR");
1317 final int positionVChr = StringUtils.indexOf(dateString, "V. CHR");
1318 if (-1 != positionNChr) {
1319 if (0 == positionNChr) {
1320 start = 7;
1321 end = length;
1322 } else {
1323 start = 0;
1324 end = positionNChr - 1;
1325 }
1326 } else if (-1 != positionVChr) {
1327 if (0 == positionVChr) {
1328 start = 7;
1329 end = length;
1330 } else {
1331 start = 0;
1332 end = positionVChr - 1;
1333 }
1334 } else {
1335 start = 0;
1336 end = length;
1337 }
1338 } else if (StringUtils.contains(dateString, "AD") || StringUtils.contains(dateString, "BC")) {
1339 final int positionAD = StringUtils.indexOf(dateString, "AD");
1340 final int positionBC = StringUtils.indexOf(dateString, "BC");
1341 if (-1 != positionAD) {
1342 if (0 == positionAD) {
1343 start = 2;
1344 end = length;
1345 } else {
1346 start = 0;
1347 end = positionAD - 1;
1348 }
1349 } else if (-1 != positionBC) {
1350 if (0 == positionBC) {
1351 start = 2;
1352 end = length;
1353 } else {
1354 start = 0;
1355 end = positionBC - 1;
1356 }
1357 } else {
1358 start = 0;
1359 end = length;
1360 }
1361 } else {
1362 start = 0;
1363 end = length;
1364 }
1365
1366 return new int[] { start, end };
1367 }
1368
1369
1370
1371
1372
1373
1374
1375 public static int[] calculateBuddhistDateBorders(String datestr) {
1376 final int start;
1377 final int end;
1378 final int length = StringUtils.length(datestr);
1379
1380 if (StringUtils.startsWith(datestr, "-")) {
1381 start = 1;
1382 end = length;
1383 } else {
1384 start = 0;
1385
1386 if (StringUtils.contains(datestr, "B.E.")) {
1387 end = StringUtils.indexOf(datestr, "B.E.");
1388 } else {
1389 end = length;
1390 }
1391 }
1392
1393 return new int[] { start, end };
1394 }
1395
1396
1397
1398
1399
1400
1401
1402 public static String delimiter(String input) {
1403 if (-1 != StringUtils.indexOf(input, "-", 1)) {
1404 return "-";
1405 } else if (-1 != StringUtils.indexOf(input, "/", 1)) {
1406 return "/";
1407 } else {
1408 return ".";
1409 }
1410 }
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427 public static boolean beforeZero(String input, CalendarType calendarType) {
1428 if (StringUtils.startsWith(input, "-")) {
1429 return true;
1430 }
1431
1432 switch (calendarType) {
1433 case Buddhist: {
1434 return StringUtils.contains(input, "B.E.");
1435 }
1436 case Gregorian:
1437 case Julian: {
1438 return StringUtils.contains(input, "BC") || StringUtils.contains(input, "V. CHR");
1439 }
1440 case Coptic:
1441 case Hebrew:
1442 case Ethiopic:
1443 case Persic:
1444 case Chinese:
1445 case Islamic:
1446 case Armenian:
1447 case Egyptian:
1448 case Japanese: {
1449
1450 return false;
1451 }
1452 default: {
1453 throw new MCRException(String.format(Locale.ROOT, MSG_CALENDAR_UNSUPPORTED, calendarType));
1454 }
1455 }
1456 }
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467 public static int getLastDayOfMonth(int month, int year, CalendarType calendarType) {
1468 final Calendar cal = Calendar.getInstance(calendarType.getLocale());
1469
1470 if (calendarType == CalendarType.Julian) {
1471 ((GregorianCalendar) cal).setGregorianChange(new Date(Long.MAX_VALUE));
1472 }
1473
1474 cal.set(Calendar.MONTH, month);
1475 cal.set(Calendar.YEAR, year);
1476
1477 return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
1478 }
1479
1480
1481
1482
1483
1484
1485
1486 public static int getFirstMonth(CalendarType calendarType) {
1487 switch (calendarType) {
1488 case Buddhist:
1489 case Gregorian:
1490 case Julian: {
1491 return GregorianCalendar.JANUARY;
1492 }
1493 case Coptic:
1494 case Egyptian: {
1495 return CopticCalendar.TOUT;
1496 }
1497 case Ethiopic: {
1498 return EthiopicCalendar.MESKEREM;
1499 }
1500 case Hebrew: {
1501 return HebrewCalendar.TISHRI;
1502 }
1503 case Islamic: {
1504 return IslamicCalendar.MUHARRAM;
1505 }
1506 case Armenian:
1507 case Persic: {
1508 return 0;
1509 }
1510 default: {
1511 throw new MCRException(String.format(Locale.ROOT, MSG_CALENDAR_UNSUPPORTED, calendarType));
1512 }
1513 }
1514 }
1515
1516
1517
1518
1519
1520
1521
1522
1523 public static int getLastMonth(int year, CalendarType calendarType) {
1524 final Calendar cal = Calendar.getInstance(calendarType.getLocale());
1525 cal.set(Calendar.YEAR, year);
1526
1527 return cal.getActualMaximum(Calendar.MONTH);
1528 }
1529
1530
1531
1532
1533
1534
1535
1536
1537 public static boolean isLeapYear(int year, CalendarType calendarType) {
1538 switch (calendarType) {
1539 case Gregorian: {
1540 final GregorianCalendar cal = new GregorianCalendar();
1541 return cal.isLeapYear(year);
1542 }
1543 case Julian: {
1544 final GregorianCalendar cal = new GregorianCalendar();
1545 cal.setGregorianChange(new Date(Long.MAX_VALUE));
1546 return cal.isLeapYear(year);
1547 }
1548 default: {
1549 throw new MCRException(String.format(Locale.ROOT, MSG_CALENDAR_UNSUPPORTED, calendarType));
1550 }
1551 }
1552 }
1553
1554 public enum CalendarType {
1555 Buddhist(TAG_BUDDHIST, new ULocale("@calendar=buddhist")),
1556 Chinese(TAG_CHINESE, new ULocale("@calendar=chinese")),
1557 Coptic(TAG_COPTIC, new ULocale("@calendar=coptic")),
1558 Ethiopic(TAG_ETHIOPIC, new ULocale("@calendar=ethiopic")),
1559 Gregorian(TAG_GREGORIAN, new ULocale("@calendar=gregorian")),
1560 Hebrew(TAG_HEBREW, new ULocale("@calendar=hebrew")),
1561 Islamic(TAG_ISLAMIC, new ULocale("@calendar=islamic-civil")),
1562 Japanese(TAG_JAPANESE, new ULocale("@calendar=japanese")),
1563 Julian(TAG_JULIAN, new ULocale("@calendar=gregorian")),
1564 Persic(TAG_PERSIC, new ULocale("@calendar=persian")),
1565
1566 Armenian(TAG_ARMENIAN, new ULocale("@calendar=coptic")),
1567
1568 Egyptian(TAG_EGYPTIAN, new ULocale("@calendar=coptic"));
1569
1570 private final String type;
1571
1572 private final ULocale locale;
1573
1574 CalendarType(String type, ULocale locale) {
1575 this.type = type;
1576 this.locale = locale;
1577 }
1578
1579 public ULocale getLocale() {
1580 return locale;
1581 }
1582
1583 public String getType() {
1584 return type;
1585 }
1586
1587 public static CalendarType of(String type) {
1588 return Arrays.stream(CalendarType.values())
1589 .filter(current -> StringUtils.equals(current.getType(), type))
1590 .findFirst().orElseThrow();
1591 }
1592 }
1593 }