1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package org.exolab.castor.types;
38
39 import java.text.DateFormat;
40 import java.text.ParseException;
41 import java.util.Calendar;
42 import java.util.GregorianCalendar;
43 import java.util.Date;
44 import java.util.SimpleTimeZone;
45 import java.util.TimeZone;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public abstract class DateTimeBase implements java.io.Serializable, Cloneable {
62
63 public static final int INDETERMINATE = -1;
64
65
66 public static final int LESS_THAN = 0;
67
68
69 public static final int EQUALS = 1;
70
71
72 public static final int GREATER_THAN = 2;
73
74
75
76
77
78 protected static final int MAX_TIME_ZONE_COMPARISON_OFFSET = 14;
79
80
81 protected static final String WRONGLY_PLACED = " is wrongly placed.";
82
83
84 private boolean _isNegative = false;
85
86
87 private short _century = 0;
88
89
90 private short _year = 0;
91
92
93 private short _month = 0;
94
95
96 private short _day = 0;
97
98
99 private short _hour = 0;
100
101
102 private short _minute = 0;
103
104
105 private short _second = 0;
106
107
108 private short _millsecond = 0;
109
110
111 private boolean _zoneNegative = false;
112
113
114 private boolean _utc = false;
115
116
117 private short _zoneHour = 0;
118
119
120 private short _zoneMinute = 0;
121
122
123
124
125
126
127
128
129 public abstract Date toDate();
130
131
132
133
134
135
136 public abstract void setValues(short[] values);
137
138
139
140
141
142
143 public abstract short[] getValues();
144
145
146
147
148
149
150
151
152
153
154 public final boolean isLeap(int year) {
155 return ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0;
156 }
157
158
159
160
161
162
163
164
165
166 private final boolean isLeap(short century, short year) {
167 return isLeap(century * 100 + year);
168 }
169
170
171
172
173
174
175
176
177
178 public void setNegative() throws UnsupportedOperationException {
179 _isNegative = true;
180 }
181
182
183
184
185
186
187
188
189 public void setCentury(short century) throws UnsupportedOperationException {
190 String err = "";
191 if (century < 0) {
192 err = "century " + century + " must not be negative.";
193 throw new IllegalArgumentException(err);
194 } else if (_year == 0 && century == 0 && _century != 0) {
195 err = "century: 0000 is not an allowed year.";
196 throw new IllegalArgumentException(err);
197 }
198
199 _century = century;
200 }
201
202
203
204
205
206
207
208
209 public void setYear(short year) throws UnsupportedOperationException {
210 String err = "";
211 if (year < 0) {
212 err = "year " + year + " must not be negative.";
213 throw new IllegalArgumentException(err);
214 } else if (year == -1) {
215 if (_century != -1) {
216 err = "year can not be omitted unless century is also omitted.";
217 throw new IllegalArgumentException(err);
218 }
219 } else if (year == 0 && _century == 0) {
220 err = "year: 0000 is not an allowed year";
221 throw new IllegalArgumentException(err);
222 } else if (year > 99) {
223 err = "year " + year + " is out of range: 0 <= year <= 99.";
224 throw new IllegalArgumentException(err);
225 }
226
227 _year = year;
228 }
229
230
231
232
233
234
235
236
237 public void setMonth(short month) throws UnsupportedOperationException {
238 String err = "";
239 if (month == -1) {
240 if (_century != -1) {
241 err = "month cannot be omitted unless the previous component is also omitted.\n"
242 + "only higher level components can be omitted.";
243 throw new IllegalArgumentException(err);
244 }
245 } else if (month < 1 || month > 12) {
246 err = "month " + month + " is out of range: 1 <= month <= 12";
247 throw new IllegalArgumentException(err);
248 }
249
250 _month = month;
251 }
252
253
254
255
256
257
258
259
260 public void setDay(short day) throws UnsupportedOperationException {
261 String err = "";
262 if (day == -1) {
263 if (_month != -1) {
264 err = "day cannot be omitted unless the previous component is also omitted.\n"
265 + "only higher level components can be omitted.";
266 throw new IllegalArgumentException(err);
267 }
268 } else if (day < 1) {
269 err = "day " + day + " cannot be negative.";
270 throw new IllegalArgumentException(err);
271 }
272
273 short maxDay = maxDayInMonthFor(_century, _year, _month);
274 if (day > maxDay) {
275 if (_month != 2) {
276 err =
277 "day " + day + " is out of range for month " + _month + ": " + "1 <= day <= " + maxDay;
278 throw new IllegalArgumentException(err);
279 } else if (isLeap(_century, _year)) {
280 err = "day " + day + " is out of range for February in a leap year: " + "1 <= day <= 29";
281 throw new IllegalArgumentException(err);
282 } else {
283 err =
284 "day " + day + " is out of range for February in a non-leap year: " + "1 <= day <= 28";
285 throw new IllegalArgumentException(err);
286 }
287 }
288
289 _day = day;
290 }
291
292
293
294
295
296
297
298
299 public void setHour(short hour) throws UnsupportedOperationException {
300 if (hour > 23) {
301 String err = "hour " + hour + " must be strictly less than 24";
302 throw new IllegalArgumentException(err);
303 } else if (hour < 0) {
304 String err = "hour " + hour + " cannot be negative.";
305 throw new IllegalArgumentException(err);
306 }
307
308 _hour = hour;
309 }
310
311
312
313
314
315
316
317
318 public void setMinute(short minute) throws UnsupportedOperationException {
319 if (minute > 59) {
320 String err = "minute " + minute + " must be strictly less than 60.";
321 throw new IllegalArgumentException(err);
322 } else if (minute < 0) {
323 String err = "minute " + minute + " cannot be negative.";
324 throw new IllegalArgumentException(err);
325 }
326
327 _minute = minute;
328 }
329
330
331
332
333
334
335
336
337
338
339
340 public void setSecond(short second, short millsecond) throws UnsupportedOperationException {
341 setSecond(second);
342 setMilliSecond(millsecond);
343 }
344
345
346
347
348
349
350
351
352
353 public void setSecond(short second) throws UnsupportedOperationException {
354 if (second > 59) {
355 String err = "seconds " + second + " must be less than 60";
356 throw new IllegalArgumentException(err);
357 } else if (second < 0) {
358 String err = "seconds " + second + " cannot be negative.";
359 throw new IllegalArgumentException(err);
360 }
361
362 _second = second;
363 }
364
365
366
367
368
369
370
371
372 public void setMilliSecond(short millisecond) throws UnsupportedOperationException {
373 if (millisecond < 0) {
374 String err = "milliseconds " + millisecond + " cannot be negative.";
375 throw new IllegalArgumentException(err);
376 } else if (millisecond > 999) {
377 String err = "milliseconds " + millisecond + " is out of bounds: 0 <= milliseconds <= 999.";
378 throw new IllegalArgumentException(err);
379 }
380
381 _millsecond = millisecond;
382 }
383
384
385
386
387 public void setUTC() {
388 _utc = true;
389 }
390
391
392
393
394
395
396
397
398 public void setZoneNegative(boolean zoneNegative) {
399 _zoneNegative = zoneNegative;
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414 public void setZone(short hour, short minute) {
415 setZoneHour(hour);
416 setZoneMinute(minute);
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 public void setZoneHour(short hour) {
431 if (hour > 23) {
432 String err = "time zone hour " + hour + " must be strictly less than 24";
433 throw new IllegalArgumentException(err);
434 } else if (hour < 0) {
435 String err = "time zone hour " + hour + " cannot be negative.";
436 throw new IllegalArgumentException(err);
437 }
438
439 _zoneHour = hour;
440
441
442 setUTC();
443 }
444
445
446
447
448
449
450
451
452
453 public void setZoneMinute(short minute) {
454 if (minute > 59) {
455 String err = "time zone minute " + minute + " must be strictly lower than 60";
456 throw new IllegalArgumentException(err);
457 } else if (minute < 0) {
458 String err = "time zone minute " + minute + " cannot be negative.";
459 throw new IllegalArgumentException(err);
460 }
461
462 _zoneMinute = minute;
463
464
465 setUTC();
466 }
467
468
469
470 public boolean isNegative() throws UnsupportedOperationException {
471 return _isNegative;
472 }
473
474 public short getCentury() throws UnsupportedOperationException {
475 return _century;
476 }
477
478 public short getYear() throws UnsupportedOperationException {
479 return _year;
480 }
481
482 public short getMonth() throws UnsupportedOperationException {
483 return _month;
484 }
485
486 public short getDay() throws UnsupportedOperationException {
487 return _day;
488 }
489
490 public short getHour() throws UnsupportedOperationException {
491 return _hour;
492 }
493
494 public short getMinute() throws UnsupportedOperationException {
495 return _minute;
496 }
497
498 public short getSeconds() throws UnsupportedOperationException {
499 return _second;
500 }
501
502 public short getMilli() throws UnsupportedOperationException {
503 return _millsecond;
504 }
505
506
507
508
509
510
511
512
513 public boolean isUTC() {
514 return _utc;
515 }
516
517 public boolean isZoneNegative() {
518 return _zoneNegative;
519 }
520
521 public short getZoneHour() {
522 return _zoneHour;
523 }
524
525 public short getZoneMinute() {
526 return _zoneMinute;
527 }
528
529
530
531 public boolean hasIsNegative() {
532 return true;
533 }
534
535 public boolean hasCentury() {
536 return true;
537 }
538
539 public boolean hasYear() {
540 return true;
541 }
542
543 public boolean hasMonth() {
544 return true;
545 }
546
547 public boolean hasDay() {
548 return true;
549 }
550
551 public boolean hasHour() {
552 return true;
553 }
554
555 public boolean hasMinute() {
556 return true;
557 }
558
559 public boolean hasSeconds() {
560 return true;
561 }
562
563 public boolean hasMilli() {
564 return true;
565 }
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582 public void addDuration(Duration duration) {
583 int temp = 0;
584 int carry = 0;
585 int sign = (duration.isNegative()) ? -1 : 1;
586
587
588
589
590 try {
591 temp = _month + sign * duration.getMonth();
592 carry = fQuotient(temp - 1, 12);
593 this.setMonth((short) (modulo(temp - 1, 12) + 1));
594 } catch (UnsupportedOperationException e) {
595
596 }
597
598
599 try {
600 temp = _century * 100 + _year + sign * duration.getYear() + carry;
601 this.setCentury((short) (temp / 100));
602 this.setYear((short) (temp % 100));
603 } catch (UnsupportedOperationException e) {
604
605 }
606
607
608
609 int tempDay = _day;
610 if (tempDay < 1) {
611 tempDay = 1;
612 } else {
613 int maxDay = maxDayInMonthFor(_century, _year, _month);
614 if (_day > maxDay) {
615 tempDay = maxDay;
616 }
617 }
618
619
620
621
622 try {
623 temp = _millsecond + sign * (int) duration.getMilli();
624 carry = fQuotient(temp, 1000);
625 this.setMilliSecond((short) modulo(temp, 1000));
626
627 temp = _second + sign * duration.getSeconds() + carry;
628 carry = fQuotient(temp, 60);
629 this.setSecond((short) modulo(temp, 60));
630 } catch (UnsupportedOperationException e) {
631
632 }
633
634
635 try {
636 temp = _minute + sign * duration.getMinute() + carry;
637 carry = fQuotient(temp, 60);
638 this.setMinute((short) modulo(temp, 60));
639 } catch (UnsupportedOperationException e) {
640
641 }
642
643
644 try {
645 temp = _hour + sign * duration.getHour() + carry;
646 carry = fQuotient(temp, 24);
647 this.setHour((short) modulo(temp, 24));
648 } catch (UnsupportedOperationException e) {
649
650 }
651
652
653
654
655 try {
656 tempDay += sign * duration.getDay() + carry;
657
658
659 while (true) {
660 short maxDay = maxDayInMonthFor(_century, _year, _month);
661 if (tempDay < 1) {
662 tempDay = (short) (tempDay + maxDayInMonthFor(_century, _year, _month - 1));
663 carry = -1;
664 } else if (tempDay > maxDay) {
665 tempDay = (short) (tempDay - maxDay);
666 carry = 1;
667 } else {
668 break;
669 }
670
671 try {
672 temp = _month + carry;
673 this.setMonth((short) (modulo(temp - 1, 12) + 1));
674 temp = this.getCentury() * 100 + this.getYear() + fQuotient(temp - 1, 12);
675 this.setCentury((short) (temp / 100));
676 this.setYear((short) (temp % 100));
677 } catch (UnsupportedOperationException e) {
678
679 }
680 }
681
682 this.setDay((short) tempDay);
683 } catch (UnsupportedOperationException e) {
684
685 }
686 }
687
688
689
690
691
692
693
694
695
696 private int fQuotient(int a, int b) {
697 return (int) Math.floor((float) a / (float) b);
698 }
699
700
701
702
703
704
705
706 private int modulo(int a, int b) {
707 return a - fQuotient(a, b) * b;
708 }
709
710
711
712
713
714
715
716
717 private final short maxDayInMonthFor(short century, short year, int month) {
718 if (month == 4 || month == 6 || month == 9 || month == 11) {
719 return 30;
720 } else if (month == 2) {
721 return (short) ((isLeap(century, year)) ? 29 : 28);
722 } else {
723 return 31;
724 }
725 }
726
727
728
729
730
731
732
733
734
735
736
737
738 public void normalize() {
739 if (!isUTC() || (_zoneHour == 0 && _zoneMinute == 0)) {
740 return;
741 }
742
743 Duration temp = new Duration();
744 temp.setHour(_zoneHour);
745 temp.setMinute(_zoneMinute);
746 if (!isZoneNegative()) {
747 temp.setNegative();
748 }
749
750 this.addDuration(temp);
751
752
753 this.setZone((short) 0, (short) 0);
754 this.setZoneNegative(false);
755 }
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776 public int compareTo(DateTimeBase dateTime) {
777 if (dateTime == null) {
778 throw new IllegalArgumentException(
779 "a Date/Time datatype cannot be compared with a null value");
780 }
781
782
783 DateTimeBase tempDate1;
784 DateTimeBase tempDate2;
785
786 try {
787 tempDate1 = clone(this);
788 if (tempDate1.isUTC()) {
789 tempDate1.normalize();
790 }
791
792 tempDate2 = clone(dateTime);
793 if (tempDate2.isUTC()) {
794 tempDate2.normalize();
795 }
796
797
798
799
800
801
802 } catch (CloneNotSupportedException e) {
803
804 throw new RuntimeException("Unexpected 'clone not supported' Exception");
805 }
806
807
808 if (tempDate1.isUTC() == tempDate2.isUTC()) {
809 return compareFields(tempDate1, tempDate2);
810 }
811
812
813 if (tempDate1.isUTC()) {
814 tempDate2.setZone((short) MAX_TIME_ZONE_COMPARISON_OFFSET, (short) 0);
815 tempDate2.normalize();
816 int result = compareFields(tempDate1, tempDate2);
817 if (result == LESS_THAN) {
818 return result;
819 }
820
821
822 tempDate2.setZone((short) MAX_TIME_ZONE_COMPARISON_OFFSET, (short) 0);
823 tempDate2.setZoneNegative(true);
824 tempDate2.normalize();
825
826 tempDate2.setZone((short) MAX_TIME_ZONE_COMPARISON_OFFSET, (short) 0);
827 tempDate2.setZoneNegative(true);
828 tempDate2.normalize();
829 result = compareFields(tempDate1, tempDate2);
830 if (result == GREATER_THAN) {
831 return result;
832 }
833 return INDETERMINATE;
834 }
835
836
837 if (tempDate2.isUTC()) {
838 tempDate1.setZone((short) MAX_TIME_ZONE_COMPARISON_OFFSET, (short) 0);
839 tempDate1.normalize();
840 int result = compareFields(tempDate1, tempDate2);
841 if (result == GREATER_THAN) {
842 return result;
843 }
844
845
846 tempDate1.setZone((short) MAX_TIME_ZONE_COMPARISON_OFFSET, (short) 0);
847 tempDate1.setZoneNegative(true);
848 tempDate1.normalize();
849
850 tempDate1.setZone((short) MAX_TIME_ZONE_COMPARISON_OFFSET, (short) 0);
851 tempDate1.setZoneNegative(true);
852 tempDate1.normalize();
853 result = compareFields(tempDate1, tempDate2);
854 if (result == LESS_THAN) {
855 return result;
856 }
857 return INDETERMINATE;
858 }
859
860 return INDETERMINATE;
861 }
862
863
864
865
866
867
868 private DateTimeBase copyDateTimeInstance(DateTimeBase dateTime) {
869 DateTimeBase newDateTime = new DateTime();
870 newDateTime._isNegative = dateTime._isNegative;
871 newDateTime._century = dateTime._century;
872 newDateTime._year = dateTime._year;
873 newDateTime._month = dateTime._month;
874 newDateTime._day = dateTime._day;
875 newDateTime._hour = dateTime._hour;
876 newDateTime._minute = dateTime._minute;
877 newDateTime._second = dateTime._second;
878 newDateTime._millsecond = dateTime._millsecond;
879 newDateTime._zoneNegative = dateTime._zoneNegative;
880 newDateTime._utc = dateTime._utc;
881 newDateTime._zoneHour = dateTime._zoneHour;
882 newDateTime._zoneMinute = dateTime._zoneMinute;
883 return newDateTime;
884 }
885
886 public DateTimeBase clone(DateTimeBase dateTime) throws CloneNotSupportedException {
887 DateTimeBase newDateTime = (DateTimeBase) super.clone();
888
889 newDateTime.setValues(dateTime.getValues());
890 if (dateTime.hasIsNegative() && dateTime.isNegative()) {
891 newDateTime.setNegative();
892 }
893 if (dateTime.isUTC()) {
894 newDateTime.setUTC();
895 newDateTime.setZone(dateTime.getZoneHour(), dateTime.getZoneMinute());
896 newDateTime.setZoneNegative(dateTime.isZoneNegative());
897 }
898 return newDateTime;
899 }
900
901 private static int compareFields(DateTimeBase date1, DateTimeBase date2) {
902 short field1;
903 short field2;
904
905 if (date1.hasCentury() != date2.hasCentury()) {
906 return INDETERMINATE;
907 }
908
909 if (date1.hasCentury() && date2.hasCentury()) {
910 field1 = date1.getCentury();
911 field2 = date2.getCentury();
912 if (field1 < field2) {
913 return LESS_THAN;
914 } else if (field1 > field2) {
915 return GREATER_THAN;
916 }
917 }
918
919 if (date1.hasYear() != date2.hasYear()) {
920 return INDETERMINATE;
921 }
922
923 if (date1.hasYear() && date2.hasYear()) {
924 field1 = date1.getYear();
925 field2 = date2.getYear();
926 if (field1 < field2) {
927 return LESS_THAN;
928 } else if (field1 > field2) {
929 return GREATER_THAN;
930 }
931 }
932
933 if (date1.hasMonth() != date2.hasMonth()) {
934 return INDETERMINATE;
935 }
936
937 if (date1.hasMonth() && date2.hasMonth()) {
938 field1 = date1.getMonth();
939 field2 = date2.getMonth();
940 if (field1 < field2) {
941 return LESS_THAN;
942 } else if (field1 > field2) {
943 return GREATER_THAN;
944 }
945 }
946
947 if (date1.hasDay() != date2.hasDay()) {
948 return INDETERMINATE;
949 }
950
951 if (date1.hasDay() && date2.hasDay()) {
952 field1 = date1.getDay();
953 field2 = date2.getDay();
954 if (field1 < field2) {
955 return LESS_THAN;
956 } else if (field1 > field2) {
957 return GREATER_THAN;
958 }
959 }
960
961 if (date1.hasHour() != date2.hasHour()) {
962 return INDETERMINATE;
963 }
964
965 if (date1.hasHour() && date2.hasHour()) {
966 field1 = date1.getHour();
967 field2 = date2.getHour();
968 if (field1 < field2) {
969 return LESS_THAN;
970 } else if (field1 > field2) {
971 return GREATER_THAN;
972 }
973 }
974
975 if (date1.hasMinute() != date2.hasMinute()) {
976 return INDETERMINATE;
977 }
978
979 if (date1.hasMinute() && date2.hasMinute()) {
980 field1 = date1.getMinute();
981 field2 = date2.getMinute();
982 if (field1 < field2) {
983 return LESS_THAN;
984 } else if (field1 > field2) {
985 return GREATER_THAN;
986 }
987 }
988
989 if (date1.hasSeconds() != date2.hasSeconds()) {
990 return INDETERMINATE;
991 }
992
993 if (date1.hasSeconds() && date2.hasSeconds()) {
994 field1 = date1.getSeconds();
995 field2 = date2.getSeconds();
996 if (field1 < field2) {
997 return LESS_THAN;
998 } else if (field1 > field2) {
999 return GREATER_THAN;
1000 }
1001 }
1002
1003 if (date1.hasMilli() != date2.hasMilli()) {
1004 return INDETERMINATE;
1005 }
1006
1007 if (date1.hasMilli() && date2.hasMilli()) {
1008 field1 = date1.getMilli();
1009 field2 = date2.getMilli();
1010 if (field1 < field2) {
1011 return LESS_THAN;
1012 } else if (field1 > field2) {
1013 return GREATER_THAN;
1014 }
1015 }
1016
1017 return EQUALS;
1018 }
1019
1020
1021
1022
1023 public int hashCode() {
1024 return _year ^ _month ^ _day ^ _hour ^ _minute ^ _second ^ _millsecond ^ _zoneHour
1025 ^ _zoneMinute;
1026 }
1027
1028
1029
1030
1031
1032
1033 public boolean equals(Object object) {
1034
1035
1036 if (object instanceof DateTimeBase) {
1037 return equal((DateTimeBase) object);
1038 }
1039 return false;
1040 }
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050 protected boolean equal(DateTimeBase dateTime) {
1051 return EQUALS == this.compareTo(dateTime);
1052 }
1053
1054
1055
1056
1057
1058
1059 public Calendar toCalendar() {
1060 Calendar result = new GregorianCalendar();
1061 result.setTime(toDate());
1062 return result;
1063 }
1064
1065
1066
1067 protected static int parseYear(final String str, final DateTimeBase result, final char[] chars,
1068 final int index, final String complaint) throws ParseException {
1069 int idx = index;
1070
1071 if (chars[idx] == '-') {
1072 idx++;
1073 result.setNegative();
1074 }
1075
1076 if (str.length() < idx + 4 || !Character.isDigit(chars[idx])
1077 || !Character.isDigit(chars[idx + 1]) || !Character.isDigit(chars[idx + 2])
1078 || !Character.isDigit(chars[idx + 3])) {
1079 throw new ParseException(complaint + str + "\nThe Year must be 4 digits long", idx);
1080 }
1081
1082 short value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1083 short value2 = (short) ((chars[idx + 2] - '0') * 10 + (chars[idx + 3] - '0'));
1084
1085 if (value1 == 0 && value2 == 0) {
1086 throw new ParseException(complaint + str + "\n'0000' is not allowed as a year.", idx);
1087 }
1088
1089 result.setCentury(value1);
1090 result.setYear(value2);
1091
1092 idx += 4;
1093
1094 return idx;
1095 }
1096
1097 protected static int parseMonth(final String str, final DateTimeBase result, final char[] chars,
1098 final int index, final String complaint) throws ParseException {
1099 int idx = index;
1100
1101 if (chars[idx] != '-') {
1102 throw new ParseException(complaint + str + "\n '-' " + DateTimeBase.WRONGLY_PLACED, idx);
1103 }
1104
1105 idx++;
1106
1107 if (str.length() < idx + 2 || !Character.isDigit(chars[idx])
1108 || !Character.isDigit(chars[idx + 1])) {
1109 throw new ParseException(complaint + str + "\nThe Month must be 2 digits long", idx);
1110 }
1111
1112 short value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1113 result.setMonth(value1);
1114
1115 idx += 2;
1116 return idx;
1117 }
1118
1119 protected static int parseDay(final String str, final DateTimeBase result, final char[] chars,
1120 final int index, final String complaint) throws ParseException {
1121 int idx = index;
1122
1123 if (chars[idx] != '-') {
1124 throw new ParseException(complaint + str + "\n '-' " + DateTimeBase.WRONGLY_PLACED, idx);
1125 }
1126
1127 idx++;
1128
1129 if (str.length() < idx + 2 || !Character.isDigit(chars[idx])
1130 || !Character.isDigit(chars[idx + 1])) {
1131 throw new ParseException(complaint + str + "\nThe Day must be 2 digits long", idx);
1132 }
1133
1134 short value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1135 result.setDay(value1);
1136
1137 idx += 2;
1138 return idx;
1139 }
1140
1141 protected static int parseTime(final String str, final DateTimeBase result, final char[] chars,
1142 final int index, final String complaint) throws ParseException {
1143 int idx = index;
1144
1145 if (str.length() < idx + 8) {
1146 throw new ParseException(
1147 complaint + str + "\nA Time field must be at least 8 characters long", idx);
1148 }
1149
1150 if (!Character.isDigit(chars[idx]) || !Character.isDigit(chars[idx + 1])) {
1151 throw new ParseException(complaint + str + "\nThe Hour must be 2 digits long", idx);
1152 }
1153
1154 short hour;
1155
1156 hour = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1157
1158 boolean isHourOfValue24 = false;
1159 if (hour == 24) {
1160 result.setHour((short) 0);
1161 Duration oneDay = new Duration();
1162 oneDay.setDay((short) 1);
1163 result.addDuration(oneDay);
1164 isHourOfValue24 = true;
1165 } else {
1166 result.setHour(hour);
1167 }
1168
1169 idx += 2;
1170
1171
1172 if (chars[idx] != ':') {
1173 throw new ParseException(complaint + str + "\n ':#1' " + DateTimeBase.WRONGLY_PLACED, idx);
1174 }
1175
1176 idx++;
1177
1178 if (!Character.isDigit(chars[idx]) || !Character.isDigit(chars[idx + 1])) {
1179 throw new ParseException(complaint + str + "\nThe Minute must be 2 digits long", idx);
1180 }
1181
1182 short minutes = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1183
1184 if (isHourOfValue24 && minutes != 0) {
1185 throw new ParseException(
1186 complaint + str + "\nWhen an hour of 24 is used, minutes must be strictly of value 00.",
1187 idx);
1188 }
1189 result.setMinute(minutes);
1190
1191 idx += 2;
1192
1193
1194 if (chars[idx] != ':') {
1195 throw new ParseException(complaint + str + "\n ':#2' " + DateTimeBase.WRONGLY_PLACED, idx);
1196 }
1197
1198 idx++;
1199
1200 if (!Character.isDigit(chars[idx]) || !Character.isDigit(chars[idx + 1])) {
1201 throw new ParseException(complaint + str + "\nThe Second must be 2 digits long", idx);
1202 }
1203
1204 short seconds = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1205 if (isHourOfValue24 && seconds != 0) {
1206 throw new ParseException(
1207 complaint + str + "\nWhen an hour of 24 is used, seconds must be strictly of value 00.",
1208 idx);
1209 }
1210 result.setSecond(seconds);
1211
1212 idx += 2;
1213
1214 if (idx < chars.length && chars[idx] == '.') {
1215 idx++;
1216
1217 long decimalValue = 0;
1218 long powerOfTen = 1;
1219 while (idx < chars.length && Character.isDigit(chars[idx])) {
1220 decimalValue = decimalValue * 10 + (chars[idx] - '0');
1221 powerOfTen *= 10;
1222 idx++;
1223 }
1224
1225
1226 if (powerOfTen > 1000) {
1227 decimalValue /= (powerOfTen / 1000);
1228 powerOfTen = 1000;
1229 } else if (powerOfTen < 1000) {
1230 decimalValue *= (1000 / powerOfTen);
1231 powerOfTen = 1000;
1232 }
1233 result.setMilliSecond((short) decimalValue);
1234 }
1235
1236 return idx;
1237 }
1238
1239 protected static int parseTimeZone(final String str, final DateTimeBase result,
1240 final char[] chars, final int index, final String complaint) throws ParseException {
1241
1242 if (index >= chars.length) {
1243 return index;
1244 }
1245
1246 int idx = index;
1247
1248 if (chars[idx] == 'Z') {
1249 result.setUTC();
1250 return ++idx;
1251 }
1252
1253 if (chars[idx] == '+' || chars[idx] == '-') {
1254 if (chars[idx] == '-') {
1255 result.setZoneNegative(true);
1256 }
1257 idx++;
1258 if (idx + 5 > chars.length || chars[idx + 2] != ':' || !Character.isDigit(chars[idx])
1259 || !Character.isDigit(chars[idx + 1]) || !Character.isDigit(chars[idx + 3])
1260 || !Character.isDigit(chars[idx + 4])) {
1261 throw new ParseException(complaint + str + "\nTimeZone must have the format (+/-)hh:mm",
1262 idx);
1263 }
1264 short value1 = (short) ((chars[idx] - '0') * 10 + (chars[idx + 1] - '0'));
1265 short value2 = (short) ((chars[idx + 3] - '0') * 10 + (chars[idx + 4] - '0'));
1266 result.setZone(value1, value2);
1267 idx += 5;
1268 }
1269
1270 return idx;
1271 }
1272
1273
1274
1275
1276
1277
1278 protected void setDateFormatTimeZone(DateFormat df) {
1279
1280 if (!isUTC()) {
1281 return;
1282 }
1283
1284 int offset = (this.getZoneMinute() + this.getZoneHour() * 60) * 60 * 1000;
1285 offset = isZoneNegative() ? -offset : offset;
1286
1287 SimpleTimeZone timeZone = new SimpleTimeZone(0, "UTC");
1288 timeZone.setRawOffset(offset);
1289 timeZone.setID(TimeZone.getAvailableIDs(offset)[0]);
1290 df.setTimeZone(timeZone);
1291 }
1292
1293
1294
1295
1296
1297
1298 protected void setDateFormatTimeZone(Calendar calendar) {
1299
1300 if (!isUTC()) {
1301 return;
1302 }
1303
1304 int offset = (this.getZoneMinute() + this.getZoneHour() * 60) * 60 * 1000;
1305 offset = isZoneNegative() ? -offset : offset;
1306
1307 SimpleTimeZone timeZone = new SimpleTimeZone(0, "UTC");
1308 timeZone.setRawOffset(offset);
1309 String[] availableIDs = TimeZone.getAvailableIDs(offset);
1310 if (availableIDs != null && availableIDs.length > 0) {
1311 timeZone.setID(availableIDs[0]);
1312 }
1313 calendar.setTimeZone(timeZone);
1314 }
1315
1316 protected void appendDateString(StringBuffer result) {
1317 if (isNegative()) {
1318 result.append('-');
1319 }
1320
1321 if ((this.getCentury() / 10) == 0) {
1322 result.append(0);
1323 }
1324 result.append(this.getCentury());
1325
1326 if ((this.getYear() / 10) == 0) {
1327 result.append(0);
1328 }
1329 result.append(this.getYear());
1330
1331 result.append('-');
1332 if ((this.getMonth() / 10) == 0) {
1333 result.append(0);
1334 }
1335 result.append(this.getMonth());
1336
1337 result.append('-');
1338 if ((this.getDay() / 10) == 0) {
1339 result.append(0);
1340 }
1341 result.append(this.getDay());
1342 }
1343
1344 protected void appendTimeString(StringBuffer result) {
1345 if ((this.getHour() / 10) == 0) {
1346 result.append(0);
1347 }
1348 result.append(this.getHour());
1349
1350 result.append(':');
1351
1352 if ((this.getMinute() / 10) == 0) {
1353 result.append(0);
1354 }
1355 result.append(this.getMinute());
1356
1357 result.append(':');
1358
1359 if ((this.getSeconds() / 10) == 0) {
1360 result.append(0);
1361 }
1362 result.append(this.getSeconds());
1363
1364 if (this.getMilli() != 0) {
1365 result.append('.');
1366 if (this.getMilli() < 100) {
1367 result.append('0');
1368 if (this.getMilli() < 10) {
1369 result.append('0');
1370 }
1371 }
1372 result.append(this.getMilli());
1373 }
1374 }
1375
1376 protected void appendTimeZoneString(StringBuffer result) {
1377 if (!isUTC()) {
1378 return;
1379 }
1380
1381
1382 if (this.getZoneHour() == 0 && this.getZoneMinute() == 0) {
1383 result.append('Z');
1384 return;
1385 }
1386
1387 if (isZoneNegative()) {
1388 result.append('-');
1389 } else {
1390 result.append('+');
1391 }
1392
1393 if ((this.getZoneHour() / 10) == 0) {
1394 result.append(0);
1395 }
1396 result.append(this.getZoneHour());
1397
1398 result.append(':');
1399 if ((this.getZoneMinute() / 10) == 0) {
1400 result.append(0);
1401 }
1402 result.append(this.getZoneMinute());
1403 }
1404
1405 }