20
20
21
21
import java .time .ZoneId ;
22
22
import java .time .format .DateTimeFormatter ;
23
+ import java .time .format .DateTimeFormatterBuilder ;
23
24
import java .time .format .DateTimeParseException ;
24
25
import java .time .temporal .TemporalAccessor ;
26
+ import java .time .temporal .TemporalField ;
27
+ import java .util .Arrays ;
28
+ import java .util .Locale ;
29
+ import java .util .Map ;
30
+ import java .util .function .Consumer ;
25
31
26
32
/**
27
33
* wrapper class around java.time.DateTimeFormatter that supports multiple formats for easier parsing,
28
34
* and one specific format for printing
29
35
*/
30
36
public class CompoundDateTimeFormatter {
31
37
38
+ private static final Consumer <DateTimeFormatter []> SAME_TIME_ZONE_VALIDATOR = (parsers ) -> {
39
+ long distinctZones = Arrays .stream (parsers ).map (DateTimeFormatter ::getZone ).distinct ().count ();
40
+ if (distinctZones > 1 ) {
41
+ throw new IllegalArgumentException ("formatters must have the same time zone" );
42
+ }
43
+ };
44
+
32
45
final DateTimeFormatter printer ;
33
46
final DateTimeFormatter [] parsers ;
34
47
35
48
CompoundDateTimeFormatter (DateTimeFormatter ... parsers ) {
36
49
if (parsers .length == 0 ) {
37
50
throw new IllegalArgumentException ("at least one date time formatter is required" );
38
51
}
52
+ SAME_TIME_ZONE_VALIDATOR .accept (parsers );
39
53
this .printer = parsers [0 ];
40
54
this .parsers = parsers ;
41
55
}
@@ -58,7 +72,18 @@ public TemporalAccessor parse(String input) {
58
72
throw failure ;
59
73
}
60
74
75
+ /**
76
+ * Configure a specific time zone for a date formatter
77
+ *
78
+ * @param zoneId The zoneId this formatter shoulduse
79
+ * @return The new formatter with all parsers switched to the specified timezone
80
+ */
61
81
public CompoundDateTimeFormatter withZone (ZoneId zoneId ) {
82
+ // shortcurt to not create new objects unnecessarily
83
+ if (zoneId .equals (parsers [0 ].getZone ())) {
84
+ return this ;
85
+ }
86
+
62
87
final DateTimeFormatter [] parsersWithZone = new DateTimeFormatter [parsers .length ];
63
88
for (int i = 0 ; i < parsers .length ; i ++) {
64
89
parsersWithZone [i ] = parsers [i ].withZone (zoneId );
@@ -67,6 +92,20 @@ public CompoundDateTimeFormatter withZone(ZoneId zoneId) {
67
92
return new CompoundDateTimeFormatter (parsersWithZone );
68
93
}
69
94
95
+ /**
96
+ * Configure defaults for missing values in a parser, then return a new compound date formatter
97
+ */
98
+ CompoundDateTimeFormatter parseDefaulting (Map <TemporalField , Long > fields ) {
99
+ final DateTimeFormatter [] parsersWithDefaulting = new DateTimeFormatter [parsers .length ];
100
+ for (int i = 0 ; i < parsers .length ; i ++) {
101
+ DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder ().append (parsers [i ]);
102
+ fields .forEach (builder ::parseDefaulting );
103
+ parsersWithDefaulting [i ] = builder .toFormatter (Locale .ROOT );
104
+ }
105
+
106
+ return new CompoundDateTimeFormatter (parsersWithDefaulting );
107
+ }
108
+
70
109
public String format (TemporalAccessor accessor ) {
71
110
return printer .format (accessor );
72
111
}
0 commit comments