【转】Struts2中转换Date类型的问题

简介:

好久没有写Blog了,最近似乎变懒了。今天上班没有很多事,于是把之前遇到的一个问题记录下来。

Web开发会涉及到很多类型转换的情况。我们知道,页面中的一切值都是字符串类型,而到后台,我们需要的可能是其他各种类型;同时,页面显示也是字符串类型。这就涉及到Web中基本的类型转换问题:从String转换为各种类型与从各种类型转换为String类型。

Java Web开发中,进行上述转换一般有以下几种:
1、在Servlet中,这一切的转换我们得自己写代码完成;
2、在Struts1.x中,我们通过apache commons-beanutils中的converters来帮助完成这些事情;
3、在Struts2中,使用的则是基于ongl的类型转换;
……

由于类型转换的通用性,因而Web框架都会实现大多数类型的转换功能,而不需要程序员编码实现。然而,对于Java.util.Date这种类型的转换,各大框架似乎做得都不尽如人意。如:在Struts1.x中,该类型的转换就会有问题,很多人建议使用java.sql.Date这种类型来解决日期转换的问题(实际上可以自定义一个类型转换器来解决该问题)。在Struts2中,这个问题似乎依然存在,也许你从来没有遇到过。的确,一般人确实不会遇到,会觉得没有这个问题。下面就是我遇到的问题与解决方法。

日期类型的转换,Web开发中几乎都会遇到,我现在做得项目也不例外。在开发的过程中,也许就像你一样,我没有对日期类型的转换做任何特殊的处理,而且Struts2也很好的帮我完成了转换。然而同事测试的时候却出现了一个“莫名其妙”的问题:输入一个常用格式的日期类型yyyy-MM-dd,到后台却报错:找不到对应的set方法——setEffDate(Ljava.lang.String)。的确,程序中只有setEffDate(java.util.Date)这个方法,没有setEffDate(Ljava.lang.String)这个方法。从Ljava.lang.String可以看出,传到后台的String类型并没有转换成Date类型,因而报错。

一开始,我以为是我UT没做好,于是在自己的电脑上模拟同事的测试,结果一点问题也没有。这就奇怪了。经过自己分析,觉得可能是IE浏览器的原因,因为同事测试用的是IE,而我用的是FireFox。于是在自己的机子上用IE测试,同时在同事机子上用FireFox测试,结果这两次测试都没有出现上面的问题。虽然没有找到问题所在,但可以初步肯定:IE的问题,但似乎又不完全是IE的问题,因为在我的电脑上的IE(版本与同事一样,都是IE6)没有上述问题。这就奇怪了,是什么问题呢,真是百思不得其解。

这个时候,我想起了之前遇到的一个不解得情况:从后台获得的日期类型在页面上显示时,跟上面情况一样,在同事的IE中,日期显示的格式竟然是:yyyy-MM-ddTHH:mm:ss。多了一个T,真是莫名其妙,而且只在同事的IE浏览器中出现(当时解决方法是在JS中将'T'替换为空格,没有去深究,但现在必须的深究了)。yyyy-MM-ddTHH:mm:ss这种日期格式有吗?于是查询JDK:在SimpleDateFormat类中找到了该日期格式,这种格式是“美国语言环境中日期和时间的模式之一”。原来还真有这种格式。竟然这是美国语言中使用的日期格式,而Struts2是美国人开发的,也许跟这个有点关系。于是查看Struts2中关于Date类型转换的源码。
在XWorkBasicConverter类中

private Object doConvertToDate(Map<String, Object> context, Object value, Class toType) {
                Date result =  null;

                 if (value  instanceof String && value !=  null && ((String) value).length() > 0) {
                        String sa = (String) value;
                        Locale locale = getLocale(context);

                        DateFormat df =  null;
                         if (java.sql.Time. class == toType) {
                                df = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
                        }  else  if (java.sql.Timestamp. class == toType) {
                                Date check =  null;
                                SimpleDateFormat dtfmt = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT,
                                                DateFormat.MEDIUM,
                                                locale);
                                SimpleDateFormat fullfmt =  new SimpleDateFormat(dtfmt.toPattern() + MILLISECOND_FORMAT,
                                                locale);

                                SimpleDateFormat dfmt = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT,
                                                locale);

                                SimpleDateFormat[] fmts = {fullfmt, dtfmt, dfmt};
                                 for (SimpleDateFormat fmt : fmts) {
                                         try {
                                                check = fmt.parse(sa);
                                                df = fmt;
                                                 if (check !=  null) {
                                                         break;
                                                }
                                        }  catch (ParseException ignore) {
                                        }
                                }
                        }  else  if (java.util.Date. class == toType) {
                                Date check =  null;
                                DateFormat[] dfs = getDateFormats(locale);
                                 for (DateFormat df1 : dfs) {
                                         try {
                                                check = df1.parse(sa);
                                                df = df1;
                                                 if (check !=  null) {
                                                         break;
                                                }
                                        }
                                         catch (ParseException ignore) {
                                        }
                                }
                        }
                         //final fallback for dates without time
                         if (df ==  null) {
                                df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
                        }
                         try {
                                df.setLenient( false);  // let's use strict parsing (XW-341)
                                result = df.parse(sa);
                                 if (!(Date. class == toType)) {
                                         try {
                                                Constructor constructor = toType.getConstructor( new Class[]{ long. class});
                                                 return constructor.newInstance( new Object[]{Long.valueOf(result.getTime())});
                                        }  catch (Exception e) {
                                                 throw  new XWorkException( "Couldn't create class " + toType +  " using default (long) constructor", e);
                                        }
                                }
                        }  catch (ParseException e) {
                                 throw  new XWorkException( "Could not parse date", e);
                        }
                }  else  if (Date. class.isAssignableFrom(value.getClass())) {
                        result = (Date) value;
                }
                 return result;
        }

         private DateFormat[] getDateFormats(Locale locale) {
                DateFormat dt1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG, locale);
                DateFormat dt2 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
                DateFormat dt3 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);

                DateFormat d1 = DateFormat.getDateInstance(DateFormat.SHORT, locale);
                DateFormat d2 = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
                DateFormat d3 = DateFormat.getDateInstance(DateFormat.LONG, locale);

                DateFormat rfc3399 =  new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss");

                DateFormat[] dfs = {dt1, dt2, dt3, rfc3399, d1, d2, d3};  //added RFC 3339 date format (XW-473)
                 return dfs;
        }


其中SHORT、MEDIUM、LONG在JDK中的DateFormat类中有说明。
从这句DateFormat rfc3399 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");可以看出,Struts2硬编码使用了这样一种格式。然而,Struts2中这种格式是放在最后的,为啥只有同事的IE浏览器测试时使用的是这种格式呢?(在调试时,用同时IE,日期输入中按这种格式输入,还真的没有问题了)这说明,同时的电脑中,前面六种DateFormat都没有匹配,查看DateFormat中关于SHORT、MEDIUM、LONG的说明,可以知道,对于yyyy-MM-dd这种日期类型,在英语语言中是没法匹配的,由于Struts2匹配日期时,使用了Locale,可见,同事的IE浏览器默认的语言环境是英语。一经查看,果然如此,把中文设置为默认语言环境,再测试,没问题了。终于知道了原因。

个人觉得,Struts2中,最后一种日期模式写死成美国标准,不是很好。

针对这个问题,我们没法要求客户一定设置中文为默认浏览器的语言环境。因而对于Date类型的转换,可以自己定义一个转换器。以下来自http://www.javaeye.com/wiki/struts2/1365-passing-parameters-in-struts2 中的一个类型转换器定义(不适合国际化的环境),如需要,你可以定义自己的转换器:


public  class DateConverter  extends DefaultTypeConverter {

         private  static  final Logger logger = Logger.getLogger(DateConverter. class);

         private  static  final String DATETIME_PATTERN =  "yyyy-MM-dd HH:mm:ss";

         private  static  final String DATE_PATTERN =  "yyyy-MM-dd";
        
         private  static  final String MONTH_PATTERN =  "yyyy-MM";

         /**
         * Convert value between types
         */

        @SuppressWarnings( "unchecked")
         public Object convertValue(Map ognlContext, Object value, Class toType) {
                Object result =  null;
                 if (toType == Date. class) {
                        result = doConvertToDate(value);
                }  else  if (toType == String. class) {
                        result = doConvertToString(value);
                }
                 return result;
        }

         /**
         * Convert String to Date
         *
         * @param value
         * @return
         */

         private Date doConvertToDate(Object value) {
                Date result =  null;

                 if (value  instanceof String) {
                        result = DateUtils.parseDate((String) value,  new String[] { DATE_PATTERN, DATETIME_PATTERN, MONTH_PATTERN });

                         // all patterns failed, try a milliseconds constructor
                         if (result ==  null && StringUtils.isNotEmpty((String)value)) {

                                 try {
                                        result =  new Date( new Long((String) value).longValue());
                                }  catch (Exception e) {
                                        logger.error( "Converting from milliseconds to Date fails!");
                                        e.printStackTrace();
                                }

                        }

                }  else  if (value  instanceof Object[]) {
                         // let's try to convert the first element only
                        Object[] array = (Object[]) value;

                         if ((array !=  null) && (array.length >= 1)) {
                                value = array[0];
                                result = doConvertToDate(value);
                        }

                }  else  if (Date. class.isAssignableFrom(value.getClass())) {
                        result = (Date) value;
                }
                 return result;
        }

         /**
         * Convert Date to String
         *
         * @param value
         * @return
         */

         private String doConvertToString(Object value) {
                SimpleDateFormat simpleDateFormat =  new SimpleDateFormat(DATETIME_PATTERN);
                String result =  null;
                 if (value  instanceof Date) {
                        result = simpleDateFormat.format(value);
                }
                 return result;
        }
}


可以将该转换器注册为全局的:在classpath下建立xwork-conversion.properties文件,内容为:java.util.Date=你的类型转换器的完整限定类名

相关文章
time模块与datetime模块,字符串与日期类型转换
time模块与datetime模块,字符串与日期类型转换
|
7月前
|
Java
java将Date类型转化为固定格式yyyyMMdd字符串
java将Date类型转化为固定格式yyyyMMdd字符串
439 0
|
安全
Date转换
Date转换
89 0
Date转换
|
7月前
|
Java 数据库
Java【问题记录 04】时间戳类型字符串{Timestamp@15601}2021-07-14 18:56:53.0变成带T的Object类型2021-07-14T10:56:53.000+0000
Java【问题记录 04】时间戳类型字符串{Timestamp@15601}2021-07-14 18:56:53.0变成带T的Object类型2021-07-14T10:56:53.000+0000
56 0
|
Java API
Java时间格式字符串与Date的相互转化
Java时间格式字符串与Date的相互转化
662 0
|
SQL 前端开发 fastjson
使用 fastjson 转换 Date 格式的数据,默认是时间戳,更改为 年-月-日 的方法
使用 fastjson 转换 Date 格式的数据,默认是时间戳,更改为 年-月-日 的方法
1504 0
|
Java Spring
SpringMVC中自定义(日期)类型转换器
SpringMVC中自定义(日期)类型转换器
SpringMVC中自定义(日期)类型转换器
|
存储 SQL 关系型数据库
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(上)
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(上)
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(上)
|
存储 SQL 关系型数据库
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(下)
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(下)
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(下)
|
存储 SQL 安全
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(中)
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(中)
【方向盘】MySql数据类型---日期时间类型的使用(含datetime和timestamp的区别) 0000-00-00 00:00:00问题解释(中)