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 package org.exolab.castor.types;
37
38 import java.text.ParseException;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class Duration implements java.io.Serializable {
59
60 private static final long serialVersionUID = -6475091654291323029L;
61
62 private static final Log LOG = LogFactory.getLog(Duration.class);
63
64
65 private static final boolean DEBUG = false;
66
67 private static final int TIME_FLAG = 8;
68
69
70 private short _year = 0;
71
72 private short _month = 0;
73
74 private short _day = 0;
75
76 private short _hour = 0;
77
78 private short _minute = 0;
79
80 private short _second = 0;
81
82 private long _millisecond = 0;
83
84 private boolean _isNegative = false;
85
86
87
88
89 public Duration() {
90
91 }
92
93
94
95
96
97
98
99 public Duration(String duration) throws ParseException {
100 parseDurationInternal(duration, this);
101 }
102
103
104
105
106
107
108
109
110 public Duration(long l) {
111 long refSecond = 1000;
112 long refMinute = 60 * refSecond;
113 long refHour = 60 * refMinute;
114 long refDay = 24 * refHour;
115 long refMonth = (long) (30.42 * refDay);
116 long refYear = 12 * refMonth;
117
118 if (DEBUG) {
119 System.out.println("In time duration Constructor");
120 System.out.println("long : " + l);
121 }
122
123 if (l < 0) {
124 this.setNegative();
125 l = -l;
126 }
127
128 short year = (short) (l / refYear);
129 l = l % refYear;
130 if (DEBUG) {
131 System.out.println("nb years:" + year);
132 System.out.println("New long : " + l);
133 }
134
135 short month = (short) (l / refMonth);
136 l = l % refMonth;
137 if (DEBUG) {
138 System.out.println("nb months:" + month);
139 System.out.println("New long : " + l);
140 System.out.println(refDay);
141 }
142
143 short day = (short) (l / refDay);
144 l = l % refDay;
145 if (DEBUG) {
146 System.out.println("nb days:" + day);
147 System.out.println("New long : " + l);
148 }
149
150 short hour = (short) (l / refHour);
151 l = l % refHour;
152 if (DEBUG) {
153 System.out.println("nb hours:" + hour);
154 System.out.println("New long : " + l);
155 }
156
157 short minute = (short) (l / refMinute);
158 l = l % refMinute;
159 if (DEBUG) {
160 System.out.println("nb minutes:" + minute);
161 System.out.println("New long : " + l);
162 }
163
164 short seconds = (short) (l / refSecond);
165 l = l % refSecond;
166 if (DEBUG) {
167 System.out.println("nb seconds:" + seconds);
168 }
169
170 long milliseconds = l;
171 if (DEBUG) {
172 System.out.println("nb milliseconds:" + milliseconds);
173 }
174
175 this.setValue(year, month, day, hour, minute, seconds, milliseconds);
176 }
177
178
179
180 public void setYear(short year) {
181 if (year < 0) {
182 String err = "In a duration all fields have to be positive.";
183 throw new IllegalArgumentException(err);
184 }
185 _year = year;
186 }
187
188 public void setMonth(short month) {
189 if (month < 0) {
190 String err = "In a duration all fields have to be positive.";
191 throw new IllegalArgumentException(err);
192 }
193 _month = month;
194 }
195
196 public void setDay(short day) {
197 if (day < 0) {
198 String err = "In a duration all fields have to be positive.";
199 throw new IllegalArgumentException(err);
200 }
201 _day = day;
202 }
203
204 public void setHour(short hour) {
205 if (hour < 0) {
206 String err = "In a duration all fields have to be positive.";
207 throw new IllegalArgumentException(err);
208 }
209 _hour = hour;
210 }
211
212 public void setMinute(short minute) {
213 if (minute < 0) {
214 String err = "In a duration all fields have to be positive.";
215 throw new IllegalArgumentException(err);
216 }
217 _minute = minute;
218 }
219
220 public void setSeconds(short second) {
221 if (second < 0) {
222 String err = "In a duration all fields have to be positive.";
223 throw new IllegalArgumentException(err);
224 }
225 _second = second;
226 }
227
228 public void setMilli(long milli) {
229 if (milli < 0) {
230 String err = "In a duration all fields have to be positive.";
231 throw new IllegalArgumentException(err);
232 }
233 _millisecond = milli;
234 }
235
236 public void setNegative() {
237 _isNegative = true;
238 }
239
240
241
242
243
244
245
246
247
248
249
250
251 public void setValue(short year, short month, short day, short hour, short minute, short second,
252 long millisecond) {
253 this.setYear(year);
254 this.setMonth(month);
255 this.setDay(day);
256 this.setHour(hour);
257 this.setMinute(minute);
258 this.setSeconds(second);
259 this.setMilli(millisecond);
260 }
261
262
263
264 public short getYear() {
265 return _year;
266 }
267
268 public short getMonth() {
269 return _month;
270 }
271
272 public short getDay() {
273 return _day;
274 }
275
276 public short getHour() {
277 return _hour;
278 }
279
280 public short getMinute() {
281 return _minute;
282 }
283
284 public short getSeconds() {
285 return _second;
286 }
287
288 public long getMilli() {
289 return _millisecond;
290 }
291
292 public boolean isNegative() {
293 return _isNegative;
294 }
295
296
297
298
299
300
301
302 public long toLong() {
303 long result = 0;
304
305
306 result =
307 (long) (((((((_year * 12L) + _month) * 30.42 + _day) * 24L + _hour) * 60L + _minute) * 60L
308 + _second) * 1000L + _millisecond);
309
310 result = isNegative() ? -result : result;
311 return result;
312 }
313
314
315
316
317
318
319
320 public String toString() {
321
322 if (this.toLong() == 0) {
323 return "PT0S";
324 }
325
326 StringBuilder result = new StringBuilder();
327 if (_isNegative) {
328 result.append('-');
329 }
330 result.append('P');
331
332 if (_year != 0) {
333 result.append(_year).append('Y');
334 }
335
336 if (_month != 0) {
337 result.append(_month).append('M');
338 }
339
340 if (_day != 0) {
341 result.append(_day).append('D');
342 }
343
344 boolean isThereTime = _hour != 0 || _minute != 0 || _second != 0 || _millisecond != 0;
345 if (isThereTime) {
346 result.append('T');
347
348 if (_hour != 0) {
349 result.append(_hour).append('H');
350 }
351
352 if (_minute != 0) {
353 result.append(_minute).append('M');
354 }
355
356 if (_second != 0 || _millisecond != 0) {
357 result.append(_second);
358 if (_millisecond != 0) {
359 result.append('.');
360 if (_millisecond < 100) {
361 result.append('0');
362 if (_millisecond < 10)
363 result.append('0');
364 }
365 result.append(_millisecond);
366 }
367 result.append('S');
368 }
369 }
370
371 return result.toString();
372 }
373
374
375
376
377
378
379
380
381
382 public static Object parse(String str) throws ParseException {
383 return parseDuration(str);
384 }
385
386
387
388
389
390
391
392
393
394 public static Duration parseDuration(String str) throws ParseException {
395 Duration result = new Duration();
396 return parseDurationInternal(str, result);
397 }
398
399 private static Duration parseDurationInternal(String str, Duration result) throws ParseException {
400 boolean isMilli = false;
401 if (str == null) {
402 throw new IllegalArgumentException("the string to be parsed must not be null");
403 }
404
405
406 if (str.length() == 0) {
407 return null;
408 }
409
410 if (result == null) {
411 result = new Duration();
412 }
413
414 char[] chars = str.toCharArray();
415 int idx = 0;
416
417 if (chars[idx] == '-') {
418 ++idx;
419 result.setNegative();
420 if (idx >= chars.length) {
421 throw new ParseException("'-' is wrongly placed", 0);
422 }
423 }
424
425
426 if (chars[idx] != 'P') {
427 throw new ParseException("Missing 'P' delimiter", idx);
428 }
429 ++idx;
430
431 if (idx == chars.length) {
432 throw new ParseException("Bad format for a duration:" + str, idx);
433 }
434 int number = 0;
435 boolean hasNumber = false;
436
437
438
439
440 int flags = 0;
441
442 while (idx < chars.length) {
443
444 char ch = chars[idx++];
445
446 switch (ch) {
447
448
449 case 'Y':
450
451 if (flags > 0) {
452 String err = str + ":Syntax error, 'Y' must proceed all other delimiters.";
453 throw new ParseException(err, idx);
454 }
455
456 flags = 64;
457 if (hasNumber) {
458 result.setYear((short) number);
459 hasNumber = false;
460 } else {
461 String err = str + ":missing number of years before 'Y'";
462 throw new ParseException(err, idx);
463 }
464
465 break;
466
467 case 'M':
468
469
470 if ((flags & TIME_FLAG) == 8) {
471
472
473
474 if ((flags & 3) > 0) {
475 throw new ParseException(str + ": Syntax Error...", idx);
476 }
477 flags = flags | 2;
478 if (hasNumber) {
479 result.setMinute((short) number);
480 hasNumber = false;
481 } else {
482 String err = str + ": missing number of minutes before 'M'";
483 throw new ParseException(err, idx);
484 }
485 }
486
487 else {
488
489
490 if ((flags & 63) > 0) {
491 throw new ParseException(str + ":Syntax Error...", idx);
492 }
493 flags = flags | 32;
494 if (hasNumber) {
495 result.setMonth((short) number);
496 hasNumber = false;
497 } else {
498 String err = str + ":missing number of months before 'M'";
499 throw new ParseException(err, idx);
500 }
501 }
502 break;
503
504 case 'D':
505
506 if ((flags & 31) > 0) {
507 throw new ParseException(str + ":Syntax Error...", idx);
508 }
509 flags = flags | 16;
510 if (hasNumber) {
511 result.setDay((short) number);
512 hasNumber = false;
513 } else {
514 String err = str + ":missing number of days before 'D'";
515 throw new ParseException(err, idx);
516 }
517 break;
518
519 case 'T':
520
521 if ((flags & TIME_FLAG) == 8) {
522 String err = str + ":Syntax error, 'T' may not " + "exist more than once.";
523 throw new ParseException(err, idx);
524 }
525 flags = flags | 8;
526 break;
527
528 case 'H':
529
530
531 if ((flags & 15) != 8) {
532 String err = null;
533 if ((flags & 8) != 8)
534 err = str + ": Missing 'T' before 'H'";
535 else
536 err = str + ": Syntax Error, 'H' must appear for 'M' or 'S'";
537 throw new ParseException(err, idx);
538 }
539 flags = flags | 4;
540 if (hasNumber) {
541 result.setHour((short) number);
542 hasNumber = false;
543 } else {
544 String err = str + ":missing number of hours before 'H'";
545 throw new ParseException(err, idx);
546 }
547 break;
548 case 'S':
549 if (flags != 0) {
550
551 if ((flags & 8) != 8) {
552 String err = str + ": Missing 'T' before 'S'";
553 throw new ParseException(err, idx);
554 }
555 if ((flags & 1) == 1) {
556 String err = str + ": Syntax error 'S' may not exist more than once.";
557 throw new ParseException(err, idx);
558 }
559
560 flags = flags | 1;
561 if (hasNumber) {
562 result.setSeconds((short) number);
563 hasNumber = false;
564 } else {
565 String err = str + ": missing number of seconds before 'S'";
566 throw new ParseException(err, idx);
567 }
568 } else {
569 if (hasNumber) {
570
571 String numb = Integer.toString(number).replaceFirst("1", "");
572
573 number = Integer.parseInt(numb);
574 if (numb.length() < 3) {
575 if (numb.length() < 2) {
576 number = number * 10;
577 }
578 number = number * 10;
579 }
580 result.setMilli((long) number);
581 hasNumber = false;
582 } else {
583 String err = str + ": missing number of milliseconds before 'S'";
584 throw new ParseException(err, idx);
585 }
586 }
587
588 break;
589
590 case '.':
591
592
593 if ((flags & 8) != 8) {
594 String err = str + ": Missing 'T' before 'S'";
595 throw new ParseException(err, idx);
596 }
597
598 if ((flags | 1) == 1) {
599 String err = str + ": Syntax error '.' may not exist more than once.";
600 throw new ParseException(err, idx);
601 }
602
603 flags = 0;
604
605 if (hasNumber) {
606 result.setSeconds((short) number);
607 hasNumber = false;
608 } else {
609 String err = str + ": missing number of seconds before 'S'";
610 throw new ParseException(err, idx);
611 }
612 isMilli = true;
613 break;
614
615 default:
616
617 if ('0' <= ch && ch <= '9') {
618 if (hasNumber) {
619 number = (number * 10) + (ch - 48);
620 } else {
621 hasNumber = true;
622 if (isMilli) {
623
624 number = Integer.parseInt("1" + (ch - 48));
625 } else {
626 number = ch - 48;
627 }
628 }
629 } else {
630 throw new ParseException(str + ":Invalid character: " + ch, idx);
631 }
632 break;
633 }
634 }
635
636
637 if ((flags & 15) == 8) {
638 LOG.warn("Warning: " + str + ": T shall be omitted");
639 }
640
641 if (hasNumber) {
642 throw new ParseException(str + ": expecting ending delimiter", idx);
643 }
644
645 return result;
646 }
647
648
649
650
651
652 public int hashCode() {
653 return 37 * (_year ^ _month ^ _day ^ _hour ^ _minute ^ _second);
654 }
655
656
657
658
659
660
661 public boolean equals(Object object) {
662 if (object instanceof Duration) {
663 return equal((Duration) object);
664 }
665 return false;
666 }
667
668
669
670
671
672
673
674 public boolean equal(Duration duration) {
675 boolean result = false;
676 if (duration == null) {
677 return result;
678 }
679 result = (_year == duration.getYear());
680 result = result && (_month == duration.getMonth());
681 result = result && (_day == duration.getDay());
682 result = result && (_hour == duration.getHour());
683 result = result && (_minute == duration.getMinute());
684 result = result && (_second == duration.getSeconds());
685 result = result && (_millisecond == duration.getMilli());
686 result = result && (this.isNegative() == duration.isNegative());
687 return result;
688 }
689
690
691
692
693
694
695
696
697
698
699
700 public boolean isGreater(Duration duration) {
701 boolean result = false;
702
703 result = this.toLong() > duration.toLong();
704 return result;
705 }
706
707 }