天天看點

【轉】Struts2中轉換Date類型的問題

好久沒有寫blog了,最近似乎變懶了。今天上班沒有很多事,于是把之前遇到的一個問題記錄下來。

web開發會涉及到很多類型轉換的情況。我們知道,頁面中的一切值都是字元串類型,而到背景,我們需要的可能是其他各種類型;同時,頁面顯示也是字元串類型。這就涉及到web中基本的類型轉換問題:從string轉換為各種類型與從各種類型轉換為string類型。

1、在servlet中,這一切的轉換我們得自己寫代碼完成;

2、在struts1.x中,我們通過apache commons-beanutils中的converters來幫助完成這些事情;

3、在struts2中,使用的則是基于ongl的類型轉換;

……

日期類型的轉換,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,

                                simpledateformat dfmt = (simpledateformat) dateformat.getdateinstance(dateformat.short,

                                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) {

                                dateformat[] dfs = getdateformats(locale);

                                for (dateformat df1 : dfs) {

                                                check = df1.parse(sa);

                                                df = df1;

                                        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)) {

                                                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);

         * convert string to date

         *

         * @param value

         * @return

        private date doconverttodate(object value) {

                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);

         * convert date to string

        private string doconverttostring(object value) {

                simpledateformat simpledateformat = new simpledateformat(datetime_pattern);

                string result = null;

                if (value instanceof date) {

                        result = simpledateformat.format(value);

}

可以将該轉換器注冊為全局的:在classpath下建立xwork-conversion.properties檔案,内容為:java.util.date=你的類型轉換器的完整限定類名