18
18
package org .apache .seatunnel .common .utils ;
19
19
20
20
import java .time .Instant ;
21
+ import java .time .LocalDate ;
21
22
import java .time .LocalDateTime ;
23
+ import java .time .LocalTime ;
22
24
import java .time .ZoneId ;
23
25
import java .time .format .DateTimeFormatter ;
26
+ import java .time .format .DateTimeFormatterBuilder ;
27
+ import java .time .format .SignStyle ;
28
+ import java .time .temporal .TemporalAccessor ;
29
+ import java .time .temporal .TemporalQueries ;
24
30
import java .util .HashMap ;
31
+ import java .util .LinkedHashMap ;
32
+ import java .util .LinkedHashSet ;
25
33
import java .util .Map ;
34
+ import java .util .Set ;
35
+ import java .util .regex .Pattern ;
36
+
37
+ import static java .time .temporal .ChronoField .DAY_OF_MONTH ;
38
+ import static java .time .temporal .ChronoField .MONTH_OF_YEAR ;
39
+ import static java .time .temporal .ChronoField .YEAR ;
26
40
27
41
public class DateTimeUtils {
28
42
@@ -48,6 +62,162 @@ public class DateTimeUtils {
48
62
FORMATTER_MAP .put (
49
63
Formatter .YYYY_MM_DD_HH_MM_SS_ISO8601 ,
50
64
DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_ISO8601 .value ));
65
+ FORMATTER_MAP .put (
66
+ Formatter .YYYY_MM_DD_HH_MM_SS_SSS_ISO8601 ,
67
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_SSS_ISO8601 .value ));
68
+ FORMATTER_MAP .put (
69
+ Formatter .YYYY_MM_DD_HH_MM_SS_SSSSSS_ISO8601 ,
70
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_SSSSSS_ISO8601 .value ));
71
+ FORMATTER_MAP .put (
72
+ Formatter .YYYY_MM_DD_HH_MM_SS_SSSSSSSSS_ISO8601 ,
73
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_SSSSSSSSS_ISO8601 .value ));
74
+ }
75
+
76
+ // if the datatime string length is 19, find the DateTimeFormatter from this map
77
+ public static final Map <Pattern , DateTimeFormatter > YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP =
78
+ new LinkedHashMap <>();
79
+ public static Set <Map .Entry <Pattern , DateTimeFormatter >>
80
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP_ENTRY_SET = new LinkedHashSet <>();
81
+
82
+ // if the datatime string length bigger than 19, find the DateTimeFormatter from this map
83
+ public static final Map <Pattern , DateTimeFormatter > YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP =
84
+ new LinkedHashMap <>();
85
+ public static Set <Map .Entry <Pattern , DateTimeFormatter >>
86
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP_ENTRY_SET = new LinkedHashSet <>();
87
+
88
+ // if the datatime string length is 14, use this formatter
89
+ public static final DateTimeFormatter YYYY_MM_DD_HH_MM_SS_14_FORMATTER =
90
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_NO_SPLIT .value );
91
+
92
+ static {
93
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP .put (
94
+ Pattern .compile ("\\ d{4}-\\ d{2}-\\ d{2}\\ s\\ d{2}:\\ d{2}:\\ d{2}" ),
95
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS .value ));
96
+
97
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP .put (
98
+ Pattern .compile ("\\ d{4}-\\ d{2}-\\ d{2}\\ s\\ d{2}:\\ d{2}.*" ),
99
+ new DateTimeFormatterBuilder ()
100
+ .parseCaseInsensitive ()
101
+ .append (DateTimeFormatter .ISO_LOCAL_DATE )
102
+ .appendLiteral (' ' )
103
+ .append (DateTimeFormatter .ISO_LOCAL_TIME )
104
+ .toFormatter ());
105
+
106
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP .put (
107
+ Pattern .compile ("\\ d{4}-\\ d{2}-\\ d{2}T\\ d{2}:\\ d{2}:\\ d{2}" ),
108
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_ISO8601 .value ));
109
+
110
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP .put (
111
+ Pattern .compile ("\\ d{4}-\\ d{2}-\\ d{2}T\\ d{2}:\\ d{2}.*" ),
112
+ DateTimeFormatter .ISO_LOCAL_DATE_TIME );
113
+
114
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP .put (
115
+ Pattern .compile ("\\ d{4}/\\ d{2}/\\ d{2}\\ s\\ d{2}:\\ d{2}:\\ d{2}" ),
116
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_SLASH .value ));
117
+
118
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP .put (
119
+ Pattern .compile ("\\ d{4}/\\ d{2}/\\ d{2}\\ s\\ d{2}:\\ d{2}.*" ),
120
+ new DateTimeFormatterBuilder ()
121
+ .parseCaseInsensitive ()
122
+ .append (
123
+ new DateTimeFormatterBuilder ()
124
+ .appendValue (YEAR , 4 , 10 , SignStyle .EXCEEDS_PAD )
125
+ .appendLiteral ('/' )
126
+ .appendValue (MONTH_OF_YEAR , 2 )
127
+ .appendLiteral ('/' )
128
+ .appendValue (DAY_OF_MONTH , 2 )
129
+ .toFormatter ())
130
+ .appendLiteral (' ' )
131
+ .append (DateTimeFormatter .ISO_LOCAL_TIME )
132
+ .toFormatter ());
133
+
134
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP .put (
135
+ Pattern .compile ("\\ d{4}\\ .\\ d{2}\\ .\\ d{2}\\ s\\ d{2}:\\ d{2}:\\ d{2}" ),
136
+ DateTimeFormatter .ofPattern (Formatter .YYYY_MM_DD_HH_MM_SS_SPOT .value ));
137
+
138
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP .put (
139
+ Pattern .compile ("\\ d{4}\\ .\\ d{2}\\ .\\ d{2}\\ s\\ d{2}:\\ d{2}.*" ),
140
+ new DateTimeFormatterBuilder ()
141
+ .parseCaseInsensitive ()
142
+ .append (
143
+ new DateTimeFormatterBuilder ()
144
+ .appendValue (YEAR , 4 , 10 , SignStyle .EXCEEDS_PAD )
145
+ .appendLiteral ('.' )
146
+ .appendValue (MONTH_OF_YEAR , 2 )
147
+ .appendLiteral ('.' )
148
+ .appendValue (DAY_OF_MONTH , 2 )
149
+ .toFormatter ())
150
+ .appendLiteral (' ' )
151
+ .append (DateTimeFormatter .ISO_LOCAL_TIME )
152
+ .toFormatter ());
153
+
154
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP .put (
155
+ Pattern .compile ("\\ d{4}年\\ d{2}月\\ d{2}日\\ s\\ d{2}时\\ d{2}分\\ d{2}秒" ),
156
+ DateTimeFormatter .ofPattern ("yyyy年MM月dd日 HH时mm分ss秒" ));
157
+
158
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP_ENTRY_SET .addAll (
159
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP .entrySet ());
160
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP_ENTRY_SET .addAll (
161
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP .entrySet ());
162
+ }
163
+
164
+ /**
165
+ * gave a datetime string and return the {@link DateTimeFormatter} which can be used to parse
166
+ * it.
167
+ *
168
+ * @param dateTime eg: 2020-02-03 12:12:10.101
169
+ * @return the DateTimeFormatter matched, will return null when not matched any pattern
170
+ */
171
+ public static DateTimeFormatter matchDateTimeFormatter (String dateTime ) {
172
+ if (dateTime .length () == 19 ) {
173
+ for (Map .Entry <Pattern , DateTimeFormatter > entry :
174
+ YYYY_MM_DD_HH_MM_SS_19_FORMATTER_MAP_ENTRY_SET ) {
175
+ if (entry .getKey ().matcher (dateTime ).matches ()) {
176
+ return entry .getValue ();
177
+ }
178
+ }
179
+ } else if (dateTime .length () > 19 ) {
180
+ for (Map .Entry <Pattern , DateTimeFormatter > entry :
181
+ YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP_ENTRY_SET ) {
182
+ if (entry .getKey ().matcher (dateTime ).matches ()) {
183
+ return entry .getValue ();
184
+ }
185
+ }
186
+ } else if (dateTime .length () == 14 ) {
187
+ return YYYY_MM_DD_HH_MM_SS_14_FORMATTER ;
188
+ }
189
+ return null ;
190
+ }
191
+
192
+ public static LocalDateTime parse (String dateTime , DateTimeFormatter dateTimeFormatter ) {
193
+ TemporalAccessor parsedTimestamp = dateTimeFormatter .parse (dateTime );
194
+ LocalTime localTime = parsedTimestamp .query (TemporalQueries .localTime ());
195
+ LocalDate localDate = parsedTimestamp .query (TemporalQueries .localDate ());
196
+ return LocalDateTime .of (localDate , localTime );
197
+ }
198
+
199
+ /**
200
+ * gave a datetime string and return {@link LocalDateTime}
201
+ *
202
+ * <p>Due to the need to determine the rules of the formatter through regular expressions, there
203
+ * will be a certain performance loss. When tested on 8c16g macos, the most significant
204
+ * performance decrease compared to directly passing the formatter is
205
+ * 'Pattern.compile("\\d{4}\\.\\d{2}\\.\\d{2}\\s\\d{2}:\\d{2}.*")' has increased from 4.5
206
+ * seconds to 10 seconds in a scenario where 1000w calculations are performed.
207
+ *
208
+ * <p>Analysis shows that there are two main reasons: one is that the regular expression
209
+ * position in the map is 4, before this, three regular expression matches are required.
210
+ *
211
+ * <p>Another reason is to support the length of non fixed millisecond bits (minimum 0, maximum
212
+ * 9), we used {@link DateTimeFormatter#ISO_LOCAL_TIME}, which also increases the time for time
213
+ * conversion.
214
+ *
215
+ * @param dateTime eg: 2020-02-03 12:12:10.101
216
+ * @return {@link LocalDateTime}
217
+ */
218
+ public static LocalDateTime parse (String dateTime ) {
219
+ DateTimeFormatter dateTimeFormatter = matchDateTimeFormatter (dateTime );
220
+ return LocalDateTime .parse (dateTime , dateTimeFormatter );
51
221
}
52
222
53
223
public static LocalDateTime parse (String dateTime , Formatter formatter ) {
@@ -78,7 +248,10 @@ public enum Formatter {
78
248
YYYY_MM_DD_HH_MM_SS_SPOT ("yyyy.MM.dd HH:mm:ss" ),
79
249
YYYY_MM_DD_HH_MM_SS_SLASH ("yyyy/MM/dd HH:mm:ss" ),
80
250
YYYY_MM_DD_HH_MM_SS_NO_SPLIT ("yyyyMMddHHmmss" ),
81
- YYYY_MM_DD_HH_MM_SS_ISO8601 ("yyyy-MM-dd'T'HH:mm:ss" );
251
+ YYYY_MM_DD_HH_MM_SS_ISO8601 ("yyyy-MM-dd'T'HH:mm:ss" ),
252
+ YYYY_MM_DD_HH_MM_SS_SSS_ISO8601 ("yyyy-MM-dd'T'HH:mm:ss.SSS" ),
253
+ YYYY_MM_DD_HH_MM_SS_SSSSSS_ISO8601 ("yyyy-MM-dd'T'HH:mm:ss.SSSSSS" ),
254
+ YYYY_MM_DD_HH_MM_SS_SSSSSSSSS_ISO8601 ("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS" );
82
255
83
256
private final String value ;
84
257
0 commit comments