1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.units;
17
18 import java.text.FieldPosition;
19 import java.text.MessageFormat;
20 import java.text.NumberFormat;
21 import java.text.ParseException;
22 import java.util.Locale;
23 import java.util.regex.Pattern;
24
25
26
27
28 @SuppressWarnings("DuplicatedCode")
29 public class TimeFormatter extends MeasureFormatter<Time, TimeUnit> {
30
31
32
33
34
35 public static final int FORMAT_NANOSECONDS = 1;
36
37
38
39
40
41 public static final int FORMAT_MICROSECONDS = 1 << 1;
42
43
44
45
46
47 public static final int FORMAT_MILLISECONDS = 1 << 2;
48
49
50
51
52
53 public static final int FORMAT_SECONDS = 1 << 3;
54
55
56
57
58
59 public static final int FORMAT_MINUTES = 1 << 4;
60
61
62
63
64
65 public static final int FORMAT_HOURS = 1 << 5;
66
67
68
69
70
71 public static final int FORMAT_DAYS = 1 << 6;
72
73
74
75
76
77 public static final int FORMAT_WEEKS = 1 << 7;
78
79
80
81
82
83 public static final int FORMAT_MONTHS = 1 << 8;
84
85
86
87
88
89 public static final int FORMAT_YEARS = 1 << 9;
90
91
92
93
94
95 public static final int FORMAT_CENTURIES = 1 << 10;
96
97
98
99
100
101 public static final int FORMAT_ALL = FORMAT_NANOSECONDS | FORMAT_MICROSECONDS | FORMAT_MILLISECONDS
102 | FORMAT_SECONDS | FORMAT_MINUTES | FORMAT_HOURS | FORMAT_DAYS | FORMAT_WEEKS | FORMAT_MONTHS
103 | FORMAT_YEARS | FORMAT_CENTURIES;
104
105
106
107
108
109 public static final int FORMAT_TIME_ALL = FORMAT_NANOSECONDS | FORMAT_MICROSECONDS | FORMAT_MILLISECONDS
110 | FORMAT_SECONDS | FORMAT_MINUTES | FORMAT_HOURS;
111
112
113
114
115
116 public static final int FORMAT_TIME_STANDARD = FORMAT_SECONDS | FORMAT_MINUTES | FORMAT_HOURS;
117
118
119
120
121
122 public static final int FORMAT_DATE_ALL = FORMAT_DAYS | FORMAT_WEEKS | FORMAT_MONTHS | FORMAT_YEARS
123 | FORMAT_CENTURIES;
124
125
126
127
128
129 public static final int FORMAT_DATE_STANDARD = FORMAT_DAYS | FORMAT_MONTHS | FORMAT_YEARS;
130
131
132
133
134 public static final String NANOSECOND_SYMBOL = "ns";
135
136
137
138
139 public static final String MICROSECOND_SYMBOL = "µs";
140
141
142
143
144 public static final String MILLISECOND_SYMBOL = "ms";
145
146
147
148
149 public static final String SECOND_SYMBOL = "s";
150
151
152
153
154 public static final String MINUTE_SYMBOL = "min";
155
156
157
158
159 public static final String HOUR_SYMBOL = "h";
160
161
162
163
164 public static final String DAY_SYMBOL = "d";
165
166
167
168
169 public static final String WEEK_SYMBOL = "wk";
170
171
172
173
174 public static final String MONTH_SYMBOL = "mon";
175
176
177
178
179 public static final String YEAR_SYMBOL = "yr";
180
181
182
183
184 public static final String CENTURY_SYMBOL = "th c.";
185
186
187
188
189 private static final String FIRST_CENTURY_SYMBOL = "st c.";
190
191
192
193
194 private static final String SECOND_CENTURY_SYMBOL = "nd c.";
195
196
197
198
199 private static final String THIRD_CENTURY_SIMBOL = "rd c.";
200
201
202
203
204
205 private static final String CENTURY_FORMAT_PATTERN = "{0}{1}";
206
207
208
209
210 private static final int INTEGER_DIGITS = 2;
211
212
213
214
215 private static final String HOUR_MINUTE_PATTERN = "^(\\d+):(\\d{2,})$";
216
217
218
219
220
221 private static final String HOUR_MINUTE_SECOND_PATTERN = "^(\\d+):(\\d{2}):(\\d{2,})$";
222
223
224
225
226 private static final String FIRST_CENTURY_PATTERN = "(.*)(\\s+)(\\d+)(\\s?st c.)(\\s+)(.*)";
227
228
229
230
231 private static final String SECOND_CENTURY_PATTERN = "(.*)(\\s+)(\\d+)(\\s?nd c.)(\\s+)(.*)";
232
233
234
235
236 private static final String THIRD_CENTURY_PATTERN = "(.*)(\\s+)(\\d+)(\\s?rd c.)(\\s+)(.*)";
237
238
239
240
241 private static final String CENTURY_PATTERN = "(.*)(\\s+)(\\d+)(\\s?th c.)(\\s+)(.*)";
242
243
244
245
246 private static final String YEAR_PATTERN = "(.*)(\\s+)(\\d+)(\\s?yr)(\\s+)(.*)";
247
248
249
250
251 private static final String MONTH_PATTERN = "(.*)(\\s+)(\\d+)(\\s?mon)(\\s+)(.*)";
252
253
254
255
256 private static final String WEEK_PATTERN = "(.*)(\\s+)(\\d+)(\\s?wk)(\\s+)(.*)";
257
258
259
260
261 private static final String DAY_PATTERN = "(.*)(\\s+)(\\d+)(\\s?d)(\\s+)(.*)";
262
263
264
265
266 private static final String HOUR_PATTERN = "(.*)(\\s+)(\\d+)(\\s?h)(\\s+)(.*)";
267
268
269
270
271 private static final String MINUTE_PATTERN = "(.*)(\\s+)(\\d+)(\\s?min)(\\s+)(.*)";
272
273
274
275
276 private static final String SECOND_PATTERN = "(.*)(\\s+)(\\d+)(\\s?s)(\\s+)(.*)";
277
278
279
280
281 private static final String MILLISECOND_PATTERN = "(.*)(\\s+)(\\d+)(\\s?ms)(\\s+)(.*)";
282
283
284
285
286 private static final String MICROSECOND_PATTERN = "(.*)(\\s+)(\\d+)(\\s?µs)(\\s+)(.*)";
287
288
289
290
291 private static final String NANOSECOND_PATTERN = "(.*)(\\s+)(\\d+)(\\s?ns)(\\s+)(.*)";
292
293
294
295
296 private static final String SPACE = " ";
297
298
299
300
301 private Pattern hourMinutePattern;
302
303
304
305
306 private Pattern hourMinuteSecondPattern;
307
308
309
310
311 private Pattern firstCenturyPattern;
312
313
314
315
316 private Pattern secondCenturyPattern;
317
318
319
320
321 private Pattern thirdCenturyPattern;
322
323
324
325
326 private Pattern centuryPattern;
327
328
329
330
331 private Pattern yearPattern;
332
333
334
335
336 private Pattern monthPattern;
337
338
339
340
341 private Pattern weekPattern;
342
343
344
345
346 private Pattern dayPattern;
347
348
349
350
351 private Pattern hourPattern;
352
353
354
355
356 private Pattern minutePattern;
357
358
359
360
361 private Pattern secondPattern;
362
363
364
365
366 private Pattern millisecondPattern;
367
368
369
370
371 private Pattern microsecondPattern;
372
373
374
375
376 private Pattern nanosecondPattern;
377
378
379
380
381 @SuppressWarnings("WeakerAccess")
382 public TimeFormatter() {
383 super();
384 }
385
386
387
388
389
390
391
392 @SuppressWarnings("WeakerAccess")
393 public TimeFormatter(final Locale locale) {
394 super(locale);
395 }
396
397
398
399
400
401
402
403 public TimeFormatter(final TimeFormatter formatter) {
404 this(formatter.getLocale());
405 hourMinutePattern = formatter.hourMinutePattern;
406 hourMinuteSecondPattern = formatter.hourMinuteSecondPattern;
407 firstCenturyPattern = formatter.firstCenturyPattern;
408 secondCenturyPattern = formatter.secondCenturyPattern;
409 thirdCenturyPattern = formatter.thirdCenturyPattern;
410 centuryPattern = formatter.centuryPattern;
411 yearPattern = formatter.yearPattern;
412 monthPattern = formatter.monthPattern;
413 weekPattern = formatter.weekPattern;
414 dayPattern = formatter.dayPattern;
415 hourPattern = formatter.hourPattern;
416 minutePattern = formatter.minutePattern;
417 secondPattern = formatter.secondPattern;
418 millisecondPattern = formatter.millisecondPattern;
419 microsecondPattern = formatter.microsecondPattern;
420 nanosecondPattern = formatter.nanosecondPattern;
421 }
422
423
424
425
426
427
428
429 @Override
430 public boolean equals(final Object obj) {
431 final var equals = super.equals(obj);
432 return (obj instanceof TimeFormatter) && equals;
433 }
434
435
436
437
438
439
440
441
442 @Override
443 public int hashCode() {
444 return super.hashCode();
445 }
446
447
448
449
450
451
452
453
454
455 @Override
456 public UnitSystem getUnitSystem(final String source) {
457 final var unit = findUnit(source);
458 try {
459 return unit != null ? TimeUnit.getUnitSystem(unit) : null;
460 } catch (final IllegalArgumentException e) {
461 return null;
462 }
463 }
464
465
466
467
468
469
470
471
472 @Override
473 public String format(final Number value, final TimeUnit unit) {
474 if (unit == TimeUnit.CENTURY) {
475 final var v = value.doubleValue();
476
477 final String symbol;
478 if (Math.abs(v) <= 1.0) {
479 symbol = FIRST_CENTURY_SYMBOL;
480 } else if (Math.abs(v) <= 2.0) {
481 symbol = SECOND_CENTURY_SYMBOL;
482 } else if (Math.abs(v) <= 3.0) {
483 symbol = THIRD_CENTURY_SIMBOL;
484 } else {
485 symbol = CENTURY_SYMBOL;
486 }
487 return MessageFormat.format(CENTURY_FORMAT_PATTERN, numberFormat.format(value), symbol);
488 } else {
489 return super.format(value, unit);
490 }
491 }
492
493
494
495
496
497
498
499
500
501
502
503 @Override
504 public StringBuffer format(
505 final Number value, final TimeUnit unit, final StringBuffer toAppendTo, final FieldPosition pos) {
506 if (unit == TimeUnit.CENTURY) {
507 final var v = value.doubleValue();
508
509 final String symbol;
510 if (Math.abs(v) <= 1.0) {
511 symbol = FIRST_CENTURY_SYMBOL;
512 } else if (Math.abs(v) <= 2.0) {
513 symbol = SECOND_CENTURY_SYMBOL;
514 } else if (Math.abs(v) <= 3.0) {
515 symbol = THIRD_CENTURY_SIMBOL;
516 } else {
517 symbol = CENTURY_SYMBOL;
518 }
519
520 final var format = new MessageFormat(CENTURY_FORMAT_PATTERN);
521 return format.format(new Object[]{numberFormat.format(value), symbol}, toAppendTo, pos);
522 } else {
523 return super.format(value, unit, toAppendTo, pos);
524 }
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539 @Override
540 public String formatAndConvert(
541 final Number value, final TimeUnit unit, final UnitSystem system) {
542 final var v = value.doubleValue();
543
544 final var nanoseconds = TimeConverter.convert(v, unit, TimeUnit.NANOSECOND);
545 if (Math.abs(nanoseconds) < (TimeConverter.SECONDS_PER_MICROSECOND / TimeConverter.SECONDS_PER_NANOSECOND)) {
546 return format(nanoseconds, TimeUnit.NANOSECOND);
547 }
548
549 final var microseconds = TimeConverter.convert(v, unit, TimeUnit.MICROSECOND);
550 if (Math.abs(microseconds) < (TimeConverter.SECONDS_PER_MILLISECOND / TimeConverter.SECONDS_PER_MICROSECOND)) {
551 return format(microseconds, TimeUnit.MICROSECOND);
552 }
553
554 final var milliseconds = TimeConverter.convert(v, unit, TimeUnit.MILLISECOND);
555 if (Math.abs(milliseconds) < (1.0 / TimeConverter.SECONDS_PER_MILLISECOND)) {
556 return format(milliseconds, TimeUnit.MILLISECOND);
557 }
558
559 final var seconds = TimeConverter.convert(v, unit, TimeUnit.SECOND);
560 if (Math.abs(seconds) < TimeConverter.SECONDS_PER_MINUTE) {
561 return format(seconds, TimeUnit.SECOND);
562 }
563
564 final var minutes = TimeConverter.convert(v, unit, TimeUnit.MINUTE);
565 if (Math.abs(minutes) < (TimeConverter.SECONDS_PER_HOUR / TimeConverter.SECONDS_PER_MINUTE)) {
566 return format(minutes, TimeUnit.MINUTE);
567 }
568
569 final var hours = TimeConverter.convert(v, unit, TimeUnit.HOUR);
570 if (Math.abs(hours) < (TimeConverter.SECONDS_PER_DAY / TimeConverter.SECONDS_PER_HOUR)) {
571 return format(hours, TimeUnit.HOUR);
572 }
573
574 final var days = TimeConverter.convert(v, unit, TimeUnit.DAY);
575 if (Math.abs(days) < (TimeConverter.SECONDS_PER_WEEK / TimeConverter.SECONDS_PER_DAY)) {
576 return format(days, TimeUnit.DAY);
577 }
578
579 final var weeks = TimeConverter.convert(v, unit, TimeUnit.WEEK);
580 if (Math.abs(weeks) < (TimeConverter.SECONDS_PER_MONTH / TimeConverter.SECONDS_PER_WEEK)) {
581 return format(weeks, TimeUnit.WEEK);
582 }
583
584 final var months = TimeConverter.convert(v, unit, TimeUnit.MONTH);
585 if (Math.abs(months) < (TimeConverter.SECONDS_PER_YEAR / TimeConverter.SECONDS_PER_MONTH)) {
586 return format(months, TimeUnit.MONTH);
587 }
588
589 final var years = TimeConverter.convert(v, unit, TimeUnit.YEAR);
590 if (Math.abs(years) < (TimeConverter.SECONDS_PER_CENTURY / TimeConverter.SECONDS_PER_YEAR)) {
591 return format(years, TimeUnit.YEAR);
592 }
593
594 final var centuries = TimeConverter.convert(v, unit, TimeUnit.CENTURY);
595 return format(centuries, TimeUnit.CENTURY);
596 }
597
598
599
600
601
602
603 @Override
604 public UnitSystem getUnitSystem() {
605 return UnitSystem.METRIC;
606 }
607
608
609
610
611
612
613
614
615
616
617 @Override
618 public Time parse(final String source) throws ParseException, UnknownUnitException {
619 return internalParse(source, new Time());
620 }
621
622
623
624
625
626
627
628
629 @Override
630 public TimeUnit findUnit(final String source) {
631 if (source.contains(NANOSECOND_SYMBOL + " ") || source.endsWith(NANOSECOND_SYMBOL)) {
632 return TimeUnit.NANOSECOND;
633 }
634 if (source.contains(MICROSECOND_SYMBOL + " ") || source.endsWith(MICROSECOND_SYMBOL)) {
635 return TimeUnit.MICROSECOND;
636 }
637 if (source.contains(MILLISECOND_SYMBOL + " ") || source.endsWith(MILLISECOND_SYMBOL)) {
638 return TimeUnit.MILLISECOND;
639 }
640 if (source.contains(SECOND_SYMBOL + " ") || source.endsWith(SECOND_SYMBOL)) {
641 return TimeUnit.SECOND;
642 }
643 if (source.contains(MINUTE_SYMBOL + " ") || source.endsWith(MINUTE_SYMBOL)) {
644 return TimeUnit.MINUTE;
645 }
646 if (source.contains(WEEK_SYMBOL + " ") || source.endsWith(WEEK_SYMBOL)) {
647 return TimeUnit.WEEK;
648 }
649 if (source.contains(MONTH_SYMBOL + " ") || source.endsWith(MONTH_SYMBOL)) {
650 return TimeUnit.MONTH;
651 }
652 if (source.contains(YEAR_SYMBOL + " ") || source.endsWith(YEAR_SYMBOL)) {
653 return TimeUnit.YEAR;
654 }
655 if (source.contains(CENTURY_SYMBOL) || source.contains(FIRST_CENTURY_SYMBOL)
656 || source.contains(SECOND_CENTURY_SYMBOL) || source.contains(THIRD_CENTURY_SIMBOL)) {
657 return TimeUnit.CENTURY;
658 }
659
660 if (source.contains(DAY_SYMBOL + " ") || source.endsWith(DAY_SYMBOL)) {
661 return TimeUnit.DAY;
662 }
663 if (source.contains(HOUR_SYMBOL + " ") || source.endsWith(HOUR_SYMBOL)) {
664 return TimeUnit.HOUR;
665 }
666
667 return null;
668 }
669
670
671
672
673
674
675
676 @Override
677 public String getUnitSymbol(final TimeUnit unit) {
678 return switch (unit) {
679 case NANOSECOND -> NANOSECOND_SYMBOL;
680 case MICROSECOND -> MICROSECOND_SYMBOL;
681 case MILLISECOND -> MILLISECOND_SYMBOL;
682 case MINUTE -> MINUTE_SYMBOL;
683 case HOUR -> HOUR_SYMBOL;
684 case DAY -> DAY_SYMBOL;
685 case WEEK -> WEEK_SYMBOL;
686 case MONTH -> MONTH_SYMBOL;
687 case YEAR -> YEAR_SYMBOL;
688 case CENTURY -> CENTURY_SYMBOL;
689 default -> SECOND_SYMBOL;
690 };
691 }
692
693
694
695
696
697
698
699
700 public String formatHourMinute(final Time time) {
701 final var exactHours = TimeConverter.convert(time.getValue().doubleValue(), time.getUnit(), TimeUnit.HOUR);
702 final var hours = Math.floor(exactHours);
703 final var diffHours = exactHours - hours;
704
705 final var minutes = TimeConverter.convert(diffHours, TimeUnit.HOUR, TimeUnit.MINUTE);
706
707 final var hourFormat = NumberFormat.getInstance(getLocale());
708 hourFormat.setMinimumIntegerDigits(INTEGER_DIGITS);
709 hourFormat.setMinimumFractionDigits(0);
710 hourFormat.setMaximumFractionDigits(0);
711
712 final var minuteFormat = NumberFormat.getInstance(getLocale());
713 minuteFormat.setMinimumIntegerDigits(INTEGER_DIGITS);
714 minuteFormat.setMaximumIntegerDigits(INTEGER_DIGITS);
715
716 if (numberFormat != null) {
717 hourFormat.setMaximumIntegerDigits(numberFormat.getMaximumIntegerDigits());
718 hourFormat.setRoundingMode(numberFormat.getRoundingMode());
719 hourFormat.setGroupingUsed(numberFormat.isGroupingUsed());
720
721 minuteFormat.setMinimumFractionDigits(numberFormat.getMinimumFractionDigits());
722 minuteFormat.setMaximumFractionDigits(numberFormat.getMaximumFractionDigits());
723 minuteFormat.setRoundingMode(numberFormat.getRoundingMode());
724 minuteFormat.setGroupingUsed(numberFormat.isGroupingUsed());
725 }
726 return hourFormat.format(hours) + ":" + minuteFormat.format(minutes);
727 }
728
729
730
731
732
733
734
735
736
737
738 public Time parseHourMinute(final CharSequence source)
739 throws ParseException, UnknownUnitException {
740 if (hourMinutePattern == null) {
741 hourMinutePattern = Pattern.compile(HOUR_MINUTE_PATTERN);
742 }
743
744 final var matcher = hourMinutePattern.matcher(source);
745 if (!matcher.matches()) {
746 throw new UnknownUnitException();
747 }
748
749 final var hourString = matcher.group(1);
750 final var minuteString = matcher.group(2);
751
752 final var hour = numberFormat.parse(hourString);
753 final var minute = numberFormat.parse(minuteString);
754
755 final var result = new Time(hour, TimeUnit.HOUR);
756 TimeConverter.convert(result, TimeUnit.MINUTE);
757 result.add(new Time(minute, TimeUnit.MINUTE));
758 return result;
759 }
760
761
762
763
764
765
766
767
768 public String formatHourMinuteSecond(final Time time) {
769 final var exactHours = TimeConverter.convert(time.getValue().doubleValue(), time.getUnit(), TimeUnit.HOUR);
770 final var hours = Math.floor(exactHours);
771 final var diffHours = exactHours - hours;
772
773 final var exactMinutes = TimeConverter.convert(diffHours, TimeUnit.HOUR, TimeUnit.MINUTE);
774 final var minutes = Math.floor(exactMinutes);
775 final var diffMinutes = exactMinutes - minutes;
776
777 final var seconds = TimeConverter.convert(diffMinutes, TimeUnit.MINUTE, TimeUnit.SECOND);
778
779 final var hourFormat = NumberFormat.getInstance(getLocale());
780 hourFormat.setMinimumIntegerDigits(INTEGER_DIGITS);
781 hourFormat.setMinimumFractionDigits(0);
782 hourFormat.setMaximumFractionDigits(0);
783
784 final var minuteFormat = NumberFormat.getInstance(getLocale());
785 minuteFormat.setMinimumIntegerDigits(INTEGER_DIGITS);
786 minuteFormat.setMinimumFractionDigits(0);
787 minuteFormat.setMaximumFractionDigits(0);
788
789 final var secondFormat = NumberFormat.getInstance(getLocale());
790 secondFormat.setMinimumIntegerDigits(INTEGER_DIGITS);
791 secondFormat.setMaximumIntegerDigits(INTEGER_DIGITS);
792
793 if (numberFormat != null) {
794 hourFormat.setMaximumIntegerDigits(numberFormat.getMaximumIntegerDigits());
795 hourFormat.setRoundingMode(numberFormat.getRoundingMode());
796 hourFormat.setGroupingUsed(numberFormat.isGroupingUsed());
797
798 minuteFormat.setMaximumIntegerDigits(numberFormat.getMaximumIntegerDigits());
799 minuteFormat.setRoundingMode(numberFormat.getRoundingMode());
800 minuteFormat.setGroupingUsed(numberFormat.isGroupingUsed());
801
802 secondFormat.setMinimumFractionDigits(numberFormat.getMinimumFractionDigits());
803 secondFormat.setMaximumFractionDigits(numberFormat.getMaximumFractionDigits());
804 secondFormat.setRoundingMode(numberFormat.getRoundingMode());
805 secondFormat.setGroupingUsed(numberFormat.isGroupingUsed());
806 }
807 return hourFormat.format(hours) + ":" + minuteFormat.format(minutes) + ":" + secondFormat.format(seconds);
808 }
809
810
811
812
813
814
815
816
817
818
819 public Time parseHourMinuteSecond(final CharSequence source) throws ParseException, UnknownUnitException {
820 if (hourMinuteSecondPattern == null) {
821 hourMinuteSecondPattern = Pattern.compile(HOUR_MINUTE_SECOND_PATTERN);
822 }
823
824 final var matcher = hourMinuteSecondPattern.matcher(source);
825 if (!matcher.matches()) {
826 throw new UnknownUnitException();
827 }
828
829 final var hourString = matcher.group(1);
830 final var minuteString = matcher.group(2);
831 final var secondString = matcher.group(3);
832
833 final var hour = numberFormat.parse(hourString);
834 final var minute = numberFormat.parse(minuteString);
835 final var second = numberFormat.parse(secondString);
836
837 final var result = new Time(hour, TimeUnit.HOUR);
838 TimeConverter.convert(result, TimeUnit.MINUTE);
839 result.add(new Time(minute, TimeUnit.MINUTE));
840 TimeConverter.convert(result, TimeUnit.SECOND);
841 result.add(new Time(second, TimeUnit.SECOND));
842 return result;
843 }
844
845
846
847
848
849
850
851
852
853
854 public String formatMultiple(final Time time, final int flags) {
855 return formatMultiple(time, flags, true);
856 }
857
858
859
860
861
862
863
864
865
866
867
868 public String formatMultiple(final Time time, final int flags, final boolean onlyNonZero) {
869
870
871 final var exactCenturies = TimeConverter.convert(time.getValue().doubleValue(), time.getUnit(),
872 TimeUnit.CENTURY);
873 var centuries = 0.0;
874 double diffCenturies;
875 if ((flags & FORMAT_CENTURIES) != 0) {
876 if ((flags & (FORMAT_YEARS | FORMAT_MONTHS | FORMAT_WEEKS | FORMAT_DAYS | FORMAT_HOURS | FORMAT_MINUTES
877 | FORMAT_SECONDS | FORMAT_MILLISECONDS | FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
878
879 centuries = Math.floor(exactCenturies);
880 } else {
881 centuries = exactCenturies;
882 }
883 diffCenturies = exactCenturies - centuries;
884 } else {
885 diffCenturies = exactCenturies;
886 }
887
888
889 final var exactYears = TimeConverter.convert(diffCenturies, TimeUnit.CENTURY, TimeUnit.YEAR);
890 var years = 0.0;
891 double diffYears;
892 if ((flags & FORMAT_YEARS) != 0) {
893 if ((flags & (FORMAT_MONTHS | FORMAT_WEEKS | FORMAT_DAYS | FORMAT_HOURS | FORMAT_MINUTES | FORMAT_SECONDS
894 | FORMAT_MILLISECONDS | FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
895
896 years = Math.floor(exactYears);
897 } else {
898 years = exactYears;
899 }
900 diffYears = exactYears - years;
901 } else {
902 diffYears = exactYears;
903 }
904
905
906 final var exactMonths = TimeConverter.convert(diffYears, TimeUnit.YEAR, TimeUnit.MONTH);
907 var months = 0.0;
908 double diffMonths;
909 if ((flags & FORMAT_MONTHS) != 0) {
910 if ((flags & (FORMAT_WEEKS | FORMAT_DAYS | FORMAT_HOURS | FORMAT_MINUTES | FORMAT_SECONDS
911 | FORMAT_MILLISECONDS | FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
912
913 months = Math.floor(exactMonths);
914 } else {
915 months = exactMonths;
916 }
917 diffMonths = exactMonths - months;
918 } else {
919 diffMonths = exactMonths;
920 }
921
922
923 final var exactWeeks = TimeConverter.convert(diffMonths, TimeUnit.MONTH, TimeUnit.WEEK);
924 var weeks = 0.0;
925 double diffWeeks;
926 if ((flags & FORMAT_WEEKS) != 0) {
927 if ((flags & (FORMAT_DAYS | FORMAT_HOURS | FORMAT_MINUTES | FORMAT_SECONDS | FORMAT_MILLISECONDS
928 | FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
929
930 weeks = Math.floor(exactWeeks);
931 } else {
932 weeks = exactWeeks;
933 }
934 diffWeeks = exactWeeks - weeks;
935 } else {
936 diffWeeks = exactWeeks;
937 }
938
939
940 final var exactDays = TimeConverter.convert(diffWeeks, TimeUnit.WEEK, TimeUnit.DAY);
941 var days = 0.0;
942 double diffDays;
943 if ((flags & FORMAT_DAYS) != 0) {
944 if ((flags & (FORMAT_HOURS | FORMAT_MINUTES | FORMAT_SECONDS | FORMAT_MILLISECONDS | FORMAT_MICROSECONDS
945 | FORMAT_NANOSECONDS)) != 0) {
946
947 days = Math.floor(exactDays);
948 } else {
949 days = exactDays;
950 }
951 diffDays = exactDays - days;
952 } else {
953 diffDays = exactDays;
954 }
955
956
957 final var exactHours = TimeConverter.convert(diffDays, TimeUnit.DAY, TimeUnit.HOUR);
958 var hours = 0.0;
959 double diffHours;
960 if ((flags & FORMAT_HOURS) != 0) {
961 if ((flags & (FORMAT_MINUTES | FORMAT_SECONDS | FORMAT_MILLISECONDS | FORMAT_MICROSECONDS
962 | FORMAT_NANOSECONDS)) != 0) {
963
964 hours = Math.floor(exactHours);
965 } else {
966 hours = exactHours;
967 }
968 diffHours = exactHours - hours;
969 } else {
970 diffHours = exactHours;
971 }
972
973
974 final var exactMinutes = TimeConverter.convert(diffHours, TimeUnit.HOUR, TimeUnit.MINUTE);
975 var minutes = 0.0;
976 double diffMinutes;
977 if ((flags & FORMAT_MINUTES) != 0) {
978 if ((flags & (FORMAT_SECONDS | FORMAT_MILLISECONDS | FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
979
980 minutes = Math.floor(exactMinutes);
981 } else {
982 minutes = exactMinutes;
983 }
984 diffMinutes = exactMinutes - minutes;
985 } else {
986 diffMinutes = exactMinutes;
987 }
988
989
990 final var exactSeconds = TimeConverter.convert(diffMinutes, TimeUnit.MINUTE, TimeUnit.SECOND);
991 var seconds = 0.0;
992 double diffSeconds;
993 if ((flags & FORMAT_SECONDS) != 0) {
994 if ((flags & (FORMAT_MILLISECONDS | FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
995
996 seconds = Math.floor(exactSeconds);
997 } else {
998 seconds = exactSeconds;
999 }
1000 diffSeconds = exactSeconds - seconds;
1001 } else {
1002 diffSeconds = exactSeconds;
1003 }
1004
1005
1006 final var exactMilliseconds = TimeConverter.convert(diffSeconds, TimeUnit.SECOND, TimeUnit.MILLISECOND);
1007 var milliseconds = 0.0;
1008 double diffMilliseconds;
1009 if ((flags & FORMAT_MILLISECONDS) != 0) {
1010 if ((flags & (FORMAT_MICROSECONDS | FORMAT_NANOSECONDS)) != 0) {
1011
1012 milliseconds = Math.floor(exactMilliseconds);
1013 } else {
1014 milliseconds = exactMilliseconds;
1015 }
1016 diffMilliseconds = exactMilliseconds - milliseconds;
1017 } else {
1018 diffMilliseconds = exactMilliseconds;
1019 }
1020
1021
1022 final var exactMicroseconds = TimeConverter.convert(diffMilliseconds, TimeUnit.MILLISECOND,
1023 TimeUnit.MICROSECOND);
1024 var microseconds = 0.0;
1025 double diffMicroseconds;
1026 if ((flags & FORMAT_MICROSECONDS) != 0) {
1027 if ((flags & (FORMAT_NANOSECONDS)) != 0) {
1028 microseconds = Math.floor(exactMicroseconds);
1029 } else {
1030 microseconds = exactMicroseconds;
1031 }
1032 diffMicroseconds = exactMicroseconds - microseconds;
1033 } else {
1034 diffMicroseconds = exactMicroseconds;
1035 }
1036
1037
1038 var nanoseconds = 0.0;
1039 if ((flags & FORMAT_NANOSECONDS) != 0) {
1040 nanoseconds = TimeConverter.convert(diffMicroseconds, TimeUnit.MICROSECOND, TimeUnit.NANOSECOND);
1041 }
1042
1043
1044 final var builder = new StringBuilder();
1045 if (((flags & FORMAT_CENTURIES) != 0) && (!onlyNonZero || centuries != 0.0)) {
1046 builder.append(format(centuries, TimeUnit.CENTURY));
1047 }
1048 if (((flags & FORMAT_YEARS) != 0) && (!onlyNonZero || years != 0.0)) {
1049 appendSpaceIfNeeded(builder).append(format(years, TimeUnit.YEAR));
1050 }
1051 if (((flags & FORMAT_MONTHS) != 0) && (!onlyNonZero || months != 0.0)) {
1052 appendSpaceIfNeeded(builder).append(format(months, TimeUnit.MONTH));
1053 }
1054 if (((flags & FORMAT_WEEKS) != 0) && (!onlyNonZero || weeks != 0.0)) {
1055 appendSpaceIfNeeded(builder).append(format(weeks, TimeUnit.WEEK));
1056 }
1057 if (((flags & FORMAT_DAYS) != 0) && (!onlyNonZero || days != 0.0)) {
1058 appendSpaceIfNeeded(builder).append(
1059 format(days, TimeUnit.DAY));
1060 }
1061 if (((flags & FORMAT_HOURS) != 0) && (!onlyNonZero || hours != 0.0)) {
1062 appendSpaceIfNeeded(builder).append(format(hours, TimeUnit.HOUR));
1063 }
1064 if (((flags & FORMAT_MINUTES) != 0) && (!onlyNonZero || minutes != 0.0)) {
1065 appendSpaceIfNeeded(builder).append(format(minutes, TimeUnit.MINUTE));
1066 }
1067 if (((flags & FORMAT_SECONDS) != 0) && (!onlyNonZero || seconds != 0.0)) {
1068 appendSpaceIfNeeded(builder).append(format(seconds, TimeUnit.SECOND));
1069 }
1070 if (((flags & FORMAT_MILLISECONDS) != 0) && (!onlyNonZero || milliseconds != 0.0)) {
1071 appendSpaceIfNeeded(builder).append(format(milliseconds, TimeUnit.MILLISECOND));
1072 }
1073 if (((flags & FORMAT_MICROSECONDS) != 0) && (!onlyNonZero || microseconds != 0.0)) {
1074 appendSpaceIfNeeded(builder).append(format(microseconds, TimeUnit.MICROSECOND));
1075 }
1076 if (((flags & FORMAT_NANOSECONDS) != 0) && (!onlyNonZero || nanoseconds != 0.0)) {
1077 appendSpaceIfNeeded(builder).append(format(nanoseconds, TimeUnit.NANOSECOND));
1078 }
1079 return builder.toString();
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093 public Time parseMultiple(final CharSequence source) throws ParseException, UnknownUnitException {
1094 final var wrapped = " " + source + " ";
1095 Time result = null;
1096
1097 final var firstCentury = parse1stCentury(wrapped);
1098 final var secondCentury = parse2ndCentury(wrapped);
1099 final var thirdCentury = parse3rdCentury(wrapped);
1100 final var century = parseCentury(wrapped);
1101 final var year = parseYear(wrapped);
1102 final var month = parseMonth(wrapped);
1103 final var week = parseWeek(wrapped);
1104 final var day = parseDay(wrapped);
1105 final var hour = parseHour(wrapped);
1106 final var minute = parseMinute(wrapped);
1107 final var second = parseSecond(wrapped);
1108 final var millisecond = parseMillisecond(wrapped);
1109 final var microsecond = parseMicrosecond(wrapped);
1110 final var nanosecond = parseNanosecond(wrapped);
1111
1112
1113 if (firstCentury != null) {
1114 result = firstCentury;
1115 }
1116 if (secondCentury != null) {
1117 result = secondCentury;
1118 }
1119 if (thirdCentury != null) {
1120 result = thirdCentury;
1121 }
1122 if (century != null) {
1123 result = century;
1124 }
1125
1126
1127 if (year != null) {
1128 if (result == null) {
1129 result = year;
1130 } else {
1131 result.add(year);
1132 }
1133 }
1134
1135
1136 if (month != null) {
1137 if (result == null) {
1138 result = month;
1139 } else {
1140 result.add(month);
1141 }
1142 }
1143
1144
1145 if (week != null) {
1146 if (result == null) {
1147 result = week;
1148 } else {
1149 result.add(week);
1150 }
1151 }
1152
1153
1154 if (day != null) {
1155 if (result == null) {
1156 result = day;
1157 } else {
1158 result.add(day);
1159 }
1160 }
1161
1162
1163 if (hour != null) {
1164 if (result == null) {
1165 result = hour;
1166 } else {
1167 result.add(hour);
1168 }
1169 }
1170
1171
1172 if (minute != null) {
1173 if (result == null) {
1174 result = minute;
1175 } else {
1176 result.add(minute);
1177 }
1178 }
1179
1180
1181 if (second != null) {
1182 if (result == null) {
1183 result = second;
1184 } else {
1185 result.add(second);
1186 }
1187 }
1188
1189
1190 if (millisecond != null) {
1191 if (result == null) {
1192 result = millisecond;
1193 } else {
1194 result.add(millisecond);
1195 }
1196 }
1197
1198
1199 if (microsecond != null) {
1200 if (result == null) {
1201 result = microsecond;
1202 } else {
1203 result.add(microsecond);
1204 }
1205 }
1206
1207
1208 if (nanosecond != null) {
1209 if (result == null) {
1210 result = nanosecond;
1211 } else {
1212 result.add(nanosecond);
1213 }
1214 }
1215
1216 if (result == null) {
1217
1218 throw new UnknownUnitException();
1219 }
1220
1221 return result;
1222 }
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233 private Time parse1stCentury(final CharSequence source) throws ParseException {
1234 if (firstCenturyPattern == null) {
1235 firstCenturyPattern = Pattern.compile(FIRST_CENTURY_PATTERN);
1236 }
1237
1238 final var matcher = firstCenturyPattern.matcher(source);
1239 if (!matcher.matches()) {
1240 return null;
1241 }
1242
1243 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.CENTURY);
1244 }
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255 private Time parse2ndCentury(final CharSequence source) throws ParseException {
1256 if (secondCenturyPattern == null) {
1257 secondCenturyPattern = Pattern.compile(SECOND_CENTURY_PATTERN);
1258 }
1259
1260 final var matcher = secondCenturyPattern.matcher(source);
1261 if (!matcher.matches()) {
1262 return null;
1263 }
1264
1265 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.CENTURY);
1266 }
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277 private Time parse3rdCentury(final CharSequence source) throws ParseException {
1278 if (thirdCenturyPattern == null) {
1279 thirdCenturyPattern = Pattern.compile(THIRD_CENTURY_PATTERN);
1280 }
1281
1282 final var matcher = thirdCenturyPattern.matcher(source);
1283 if (!matcher.matches()) {
1284 return null;
1285 }
1286
1287 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.CENTURY);
1288 }
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299 private Time parseCentury(final CharSequence source) throws ParseException {
1300 if (centuryPattern == null) {
1301 centuryPattern = Pattern.compile(CENTURY_PATTERN);
1302 }
1303
1304 final var matcher = centuryPattern.matcher(source);
1305 if (!matcher.matches()) {
1306 return null;
1307 }
1308
1309 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.CENTURY);
1310 }
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321 private Time parseYear(final CharSequence source) throws ParseException {
1322 if (yearPattern == null) {
1323 yearPattern = Pattern.compile(YEAR_PATTERN);
1324 }
1325
1326 final var matcher = yearPattern.matcher(source);
1327 if (!matcher.matches()) {
1328 return null;
1329 }
1330
1331 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.YEAR);
1332 }
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 private Time parseMonth(final CharSequence source) throws ParseException {
1344 if (monthPattern == null) {
1345 monthPattern = Pattern.compile(MONTH_PATTERN);
1346 }
1347
1348 final var matcher = monthPattern.matcher(source);
1349 if (!matcher.matches()) {
1350 return null;
1351 }
1352
1353 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.MONTH);
1354 }
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365 private Time parseWeek(final CharSequence source) throws ParseException {
1366 if (weekPattern == null) {
1367 weekPattern = Pattern.compile(WEEK_PATTERN);
1368 }
1369
1370 final var matcher = weekPattern.matcher(source);
1371 if (!matcher.matches()) {
1372 return null;
1373 }
1374
1375 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.WEEK);
1376 }
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387 private Time parseDay(final CharSequence source) throws ParseException {
1388 if (dayPattern == null) {
1389 dayPattern = Pattern.compile(DAY_PATTERN);
1390 }
1391
1392 final var matcher = dayPattern.matcher(source);
1393 if (!matcher.matches()) {
1394 return null;
1395 }
1396
1397 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.DAY);
1398 }
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409 private Time parseHour(final CharSequence source) throws ParseException {
1410 if (hourPattern == null) {
1411 hourPattern = Pattern.compile(HOUR_PATTERN);
1412 }
1413
1414 final var matcher = hourPattern.matcher(source);
1415 if (!matcher.matches()) {
1416 return null;
1417 }
1418
1419 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.HOUR);
1420 }
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431 private Time parseMinute(final CharSequence source) throws ParseException {
1432 if (minutePattern == null) {
1433 minutePattern = Pattern.compile(MINUTE_PATTERN);
1434 }
1435
1436 final var matcher = minutePattern.matcher(source);
1437 if (!matcher.matches()) {
1438 return null;
1439 }
1440
1441 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.MINUTE);
1442 }
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453 private Time parseSecond(final CharSequence source) throws ParseException {
1454 if (secondPattern == null) {
1455 secondPattern = Pattern.compile(SECOND_PATTERN);
1456 }
1457
1458 final var matcher = secondPattern.matcher(source);
1459 if (!matcher.matches()) {
1460 return null;
1461 }
1462
1463 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.SECOND);
1464 }
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475 private Time parseMillisecond(final CharSequence source) throws ParseException {
1476 if (millisecondPattern == null) {
1477 millisecondPattern = Pattern.compile(MILLISECOND_PATTERN);
1478 }
1479
1480 final var matcher = millisecondPattern.matcher(source);
1481 if (!matcher.matches()) {
1482 return null;
1483 }
1484
1485 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.MILLISECOND);
1486 }
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497 private Time parseMicrosecond(final CharSequence source) throws ParseException {
1498 if (microsecondPattern == null) {
1499 microsecondPattern = Pattern.compile(MICROSECOND_PATTERN);
1500 }
1501
1502 final var matcher = microsecondPattern.matcher(source);
1503 if (!matcher.matches()) {
1504 return null;
1505 }
1506
1507 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.MICROSECOND);
1508 }
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519 private Time parseNanosecond(final CharSequence source) throws ParseException {
1520 if (nanosecondPattern == null) {
1521 nanosecondPattern = Pattern.compile(NANOSECOND_PATTERN);
1522 }
1523
1524 final var matcher = nanosecondPattern.matcher(source);
1525 if (!matcher.matches()) {
1526 return null;
1527 }
1528
1529 return new Time(numberFormat.parse(matcher.group(3)), TimeUnit.NANOSECOND);
1530 }
1531
1532
1533
1534
1535
1536
1537
1538 private StringBuilder appendSpaceIfNeeded(final StringBuilder builder) {
1539 if (!builder.isEmpty()) {
1540 builder.append(SPACE);
1541 }
1542 return builder;
1543 }
1544 }