18
18
*/
19
19
package com .opensymphony .xwork2 .conversion .impl ;
20
20
21
- import org .apache .logging .log4j .LogManager ;
22
- import org .apache .logging .log4j .Logger ;
23
- import org .apache .struts2 .conversion .TypeConversionException ;
24
- import com .opensymphony .xwork2 .ActionContext ;
25
- import com .opensymphony .xwork2 .TextProvider ;
26
- import com .opensymphony .xwork2 .util .ValueStack ;
27
-
28
21
import java .lang .reflect .Constructor ;
29
22
import java .lang .reflect .Member ;
30
23
import java .text .DateFormat ;
31
24
import java .text .ParseException ;
32
25
import java .text .SimpleDateFormat ;
26
+ import java .time .LocalDateTime ;
27
+ import java .time .format .DateTimeFormatter ;
28
+ import java .time .format .DateTimeParseException ;
29
+ import java .time .temporal .TemporalAccessor ;
33
30
import java .util .Date ;
34
31
import java .util .Locale ;
35
32
import java .util .Map ;
36
33
34
+ import org .apache .logging .log4j .LogManager ;
35
+ import org .apache .logging .log4j .Logger ;
36
+ import org .apache .struts2 .conversion .TypeConversionException ;
37
+
38
+ import com .opensymphony .xwork2 .ActionContext ;
39
+ import com .opensymphony .xwork2 .TextProvider ;
40
+ import com .opensymphony .xwork2 .util .ValueStack ;
41
+
37
42
public class DateConverter extends DefaultTypeConverter {
38
43
39
44
private final static Logger LOG = LogManager .getLogger (DateConverter .class );
40
45
41
46
@ Override
42
- public Object convertValue (Map <String , Object > context , Object target , Member member , String propertyName , Object value , Class toType ) {
47
+ public Object convertValue (Map <String , Object > context , Object target , Member member , String propertyName ,
48
+ Object value , Class toType ) {
43
49
Date result = null ;
44
50
45
51
if (value instanceof String && ((String ) value ).length () > 0 ) {
@@ -52,15 +58,12 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
52
58
} else if (java .sql .Timestamp .class == toType ) {
53
59
Date check = null ;
54
60
SimpleDateFormat dtfmt = (SimpleDateFormat ) DateFormat .getDateTimeInstance (DateFormat .SHORT ,
55
- DateFormat .MEDIUM ,
56
- locale );
57
- SimpleDateFormat fullfmt = new SimpleDateFormat (dtfmt .toPattern () + MILLISECOND_FORMAT ,
58
- locale );
61
+ DateFormat .MEDIUM , locale );
62
+ SimpleDateFormat fullfmt = new SimpleDateFormat (dtfmt .toPattern () + MILLISECOND_FORMAT , locale );
59
63
60
- SimpleDateFormat dfmt = (SimpleDateFormat ) DateFormat .getDateInstance (DateFormat .SHORT ,
61
- locale );
64
+ SimpleDateFormat dfmt = (SimpleDateFormat ) DateFormat .getDateInstance (DateFormat .SHORT , locale );
62
65
63
- SimpleDateFormat [] fmts = {fullfmt , dtfmt , dfmt };
66
+ SimpleDateFormat [] fmts = { fullfmt , dtfmt , dfmt };
64
67
for (SimpleDateFormat fmt : fmts ) {
65
68
try {
66
69
check = fmt .parse (sa );
@@ -85,8 +88,33 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
85
88
} catch (ParseException ignore ) {
86
89
}
87
90
}
91
+ } else if (java .time .LocalDateTime .class == toType ) {
92
+ DateTimeFormatter dtf = null ;
93
+ TemporalAccessor check ;
94
+ DateTimeFormatter [] dfs = getDateTimeFormats (ActionContext .of (context ), locale );
95
+
96
+ for (DateTimeFormatter df1 : dfs ) {
97
+ try {
98
+ check = df1 .parse (sa );
99
+ dtf = df1 ;
100
+ if (check != null ) {
101
+ break ;
102
+ }
103
+ } catch (DateTimeParseException ignore ) {
104
+ }
105
+ }
106
+ if (dtf == null ) {
107
+ throw new TypeConversionException ("Could not parse date" );
108
+ } else {
109
+ try {
110
+ return LocalDateTime .parse (sa , dtf );
111
+ } catch (DateTimeParseException e ) {
112
+ throw new TypeConversionException ("Could not parse date" , e );
113
+ }
114
+ }
88
115
}
89
- //final fallback for dates without time
116
+
117
+ // final fallback for dates without time
90
118
if (df == null ) {
91
119
df = DateFormat .getDateInstance (DateFormat .SHORT , locale );
92
120
}
@@ -95,32 +123,34 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
95
123
result = df .parse (sa );
96
124
if (!(Date .class == toType )) {
97
125
try {
98
- Constructor <?> constructor = toType .getConstructor (new Class []{ long .class });
99
- return constructor .newInstance (new Object []{ Long .valueOf (result .getTime ())});
126
+ Constructor <?> constructor = toType .getConstructor (new Class [] { long .class });
127
+ return constructor .newInstance (new Object [] { Long .valueOf (result .getTime ()) });
100
128
} catch (Exception e ) {
101
- throw new TypeConversionException ("Couldn't create class " + toType + " using default (long) constructor" , e );
129
+ throw new TypeConversionException (
130
+ "Couldn't create class " + toType + " using default (long) constructor" , e );
102
131
}
103
132
}
104
133
} catch (ParseException e ) {
105
134
throw new TypeConversionException ("Could not parse date" , e );
106
135
}
136
+
107
137
} else if (Date .class .isAssignableFrom (value .getClass ())) {
108
138
result = (Date ) value ;
109
139
}
110
140
return result ;
111
141
}
112
142
113
143
/**
114
- * The user defined global date format,
115
- * see {@link org.apache.struts2.components.Date#DATETAG_PROPERTY}
144
+ * The user defined global date format, see
145
+ * {@link org.apache.struts2.components.Date#DATETAG_PROPERTY}
116
146
*
117
147
* @param context current ActionContext
118
- * @param locale current Locale to convert to
119
- * @return defined global format
148
+ *
149
+ * @return defined global date string format
120
150
*/
121
- protected DateFormat getGlobalDateFormat (ActionContext context , Locale locale ) {
151
+ protected String getGlobalDateString (ActionContext context ) {
122
152
final String dateTagProperty = org .apache .struts2 .components .Date .DATETAG_PROPERTY ;
123
- SimpleDateFormat globalDateFormat = null ;
153
+ String globalDateString = null ;
124
154
125
155
final TextProvider tp = findProviderInStack (context .getValueStack ());
126
156
@@ -130,23 +160,29 @@ protected DateFormat getGlobalDateFormat(ActionContext context, Locale locale) {
130
160
// is the same as input = DATETAG_PROPERTY
131
161
if (globalFormat != null && !dateTagProperty .equals (globalFormat )) {
132
162
LOG .debug ("Found \" {}\" as \" {}\" " , dateTagProperty , globalFormat );
133
- globalDateFormat = new SimpleDateFormat ( globalFormat , locale ) ;
163
+ globalDateString = globalFormat ;
134
164
} else {
135
165
LOG .debug ("\" {}\" has not been defined, ignoring it" , dateTagProperty );
136
166
}
137
167
}
138
168
139
- return globalDateFormat ;
169
+ return globalDateString ;
140
170
}
141
171
142
172
/**
143
173
* Retrieves the list of date formats to be used when converting dates
174
+ *
144
175
* @param context the current ActionContext
145
- * @param locale the current locale of the action
176
+ * @param locale the current locale of the action
146
177
* @return a list of DateFormat to be used for date conversion
147
178
*/
148
179
private DateFormat [] getDateFormats (ActionContext context , Locale locale ) {
149
- DateFormat globalDateFormat = getGlobalDateFormat (context , locale );
180
+ DateFormat globalDateFormat = null ;
181
+ String globalFormat = getGlobalDateString (context );
182
+ //
183
+ if (globalFormat != null ) {
184
+ globalDateFormat = new SimpleDateFormat (globalFormat , locale );
185
+ }
150
186
151
187
DateFormat dt1 = DateFormat .getDateTimeInstance (DateFormat .SHORT , DateFormat .LONG , locale );
152
188
DateFormat dt2 = DateFormat .getDateTimeInstance (DateFormat .SHORT , DateFormat .MEDIUM , locale );
@@ -156,20 +192,37 @@ private DateFormat[] getDateFormats(ActionContext context, Locale locale) {
156
192
DateFormat d2 = DateFormat .getDateInstance (DateFormat .MEDIUM , locale );
157
193
DateFormat d3 = DateFormat .getDateInstance (DateFormat .LONG , locale );
158
194
159
- DateFormat rfc3339 = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss" );
195
+ DateFormat rfc3339 = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ss" );
160
196
DateFormat rfc3339dateOnly = new SimpleDateFormat ("yyyy-MM-dd" );
161
197
162
198
final DateFormat [] dateFormats ;
163
199
164
200
if (globalDateFormat == null ) {
165
- dateFormats = new DateFormat []{ dt1 , dt2 , dt3 , rfc3339 , d1 , d2 , d3 , rfc3339dateOnly };
201
+ dateFormats = new DateFormat [] { dt1 , dt2 , dt3 , rfc3339 , d1 , d2 , d3 , rfc3339dateOnly };
166
202
} else {
167
- dateFormats = new DateFormat []{ globalDateFormat , dt1 , dt2 , dt3 , rfc3339 , d1 , d2 , d3 , rfc3339dateOnly };
203
+ dateFormats = new DateFormat [] { globalDateFormat , dt1 , dt2 , dt3 , rfc3339 , d1 , d2 , d3 , rfc3339dateOnly };
168
204
}
169
205
170
206
return dateFormats ;
171
207
}
172
208
209
+ /**
210
+ * Retrieves the list of date time formats to be used when converting dates
211
+ *
212
+ * @param context the current ActionContext
213
+ * @param locale the current locale of the action
214
+ *
215
+ * @return a list of DateTimeFormatter to be used for date conversion
216
+ */
217
+ protected DateTimeFormatter [] getDateTimeFormats (ActionContext context , Locale locale ) {
218
+
219
+ DateTimeFormatter df1 = DateTimeFormatter .ISO_LOCAL_DATE_TIME ;
220
+
221
+ final DateTimeFormatter [] dateFormats = new DateTimeFormatter [] { df1 };
222
+
223
+ return dateFormats ;
224
+ }
225
+
173
226
private TextProvider findProviderInStack (ValueStack stack ) {
174
227
// TODO: ValueStack will never be null, this is just a workaround for tests
175
228
if (stack == null ) {
0 commit comments