001    /*******************************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     * http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     *******************************************************************************/
019    package org.apache.commons.convert;
020    
021    import java.sql.Time;
022    import java.sql.Timestamp;
023    import java.text.DateFormat;
024    import java.text.ParseException;
025    import java.text.SimpleDateFormat;
026    import java.util.Calendar;
027    import java.util.Date;
028    import java.util.Locale;
029    import java.util.TimeZone;
030    
031    /** Date/time <code>Converter</code> classes. */
032    public class DateTimeConverters implements ConverterLoader {
033    
034        /**
035         * Calendar format string: <code>EEE MMM dd HH:mm:ss.SSS z yyyy</code>. 
036         */
037        public static final String CALENDAR_FORMAT = "EEE MMM dd HH:mm:ss.SSS z yyyy";
038        /**
039         * JDBC DATE format string: <code>yyyy-MM-dd</code>. 
040         */
041        public static final String JDBC_DATE_FORMAT = "yyyy-MM-dd";
042        /**
043         * JDBC TIME format string: <code>HH:mm:ss</code>. 
044         */
045        public static final String JDBC_TIME_FORMAT = "HH:mm:ss";
046    
047        /**
048         * Returns an initialized DateFormat object.
049         * 
050         * @param tz
051         * @return DateFormat object
052         */
053        protected static DateFormat toDateFormat(TimeZone tz) {
054            DateFormat df = new SimpleDateFormat(JDBC_DATE_FORMAT);
055            df.setTimeZone(tz);
056            return df;
057        }
058    
059        /**
060         * Returns an initialized DateFormat object.
061         * 
062         * @param dateTimeFormat
063         *            optional format string
064         * @param tz
065         * @param locale
066         *            can be null if dateTimeFormat is not null
067         * @return DateFormat object
068         */
069        protected static DateFormat toDateTimeFormat(String dateTimeFormat, TimeZone tz, Locale locale) {
070            DateFormat df = null;
071            if (Util.isEmpty(dateTimeFormat)) {
072                df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM, locale);
073            } else {
074                df = new SimpleDateFormat(dateTimeFormat);
075            }
076            df.setTimeZone(tz);
077            return df;
078        }
079    
080        /**
081         * Returns an initialized DateFormat object.
082         * 
083         * @param tz
084         * @return DateFormat object
085         */
086        protected static DateFormat toTimeFormat(TimeZone tz) {
087            DateFormat df = new SimpleDateFormat(JDBC_TIME_FORMAT);
088            df.setTimeZone(tz);
089            return df;
090        }
091    
092        public void loadConverters() {
093            Converters.loadContainedConverters(DateTimeConverters.class);
094            Converters.registerConverter(new GenericDateToLong<java.util.Date>(java.util.Date.class));
095            Converters.registerConverter(new GenericDateToLong<java.sql.Date>(java.sql.Date.class));
096            Converters.registerConverter(new GenericDateToLong<java.sql.Time>(java.sql.Time.class));
097            Converters.registerConverter(new GenericDateToLong<java.sql.Timestamp>(java.sql.Timestamp.class));
098            Converters.registerConverter(new GenericSingletonToList<java.util.Calendar>(java.util.Calendar.class));
099            Converters.registerConverter(new GenericSingletonToList<java.util.Date>(java.util.Date.class));
100            Converters.registerConverter(new GenericSingletonToList<java.util.TimeZone>(java.util.TimeZone.class));
101            Converters.registerConverter(new GenericSingletonToList<java.sql.Date>(java.sql.Date.class));
102            Converters.registerConverter(new GenericSingletonToList<java.sql.Time>(java.sql.Time.class));
103            Converters.registerConverter(new GenericSingletonToList<java.sql.Timestamp>(java.sql.Timestamp.class));
104            Converters.registerConverter(new GenericSingletonToSet<java.util.Calendar>(java.util.Calendar.class));
105            Converters.registerConverter(new GenericSingletonToSet<java.util.Date>(java.util.Date.class));
106            Converters.registerConverter(new GenericSingletonToSet<java.util.TimeZone>(java.util.TimeZone.class));
107            Converters.registerConverter(new GenericSingletonToSet<java.sql.Date>(java.sql.Date.class));
108            Converters.registerConverter(new GenericSingletonToSet<java.sql.Time>(java.sql.Time.class));
109            Converters.registerConverter(new GenericSingletonToSet<java.sql.Timestamp>(java.sql.Timestamp.class));
110        }
111    
112        /**
113         * An object that converts a <code>Calendar</code> to a <code>Date</code>.
114         */
115        public static class CalendarToDate extends AbstractConverter<Calendar, Date> {
116            public CalendarToDate() {
117                super(Calendar.class, Date.class);
118            }
119    
120            @Override
121            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
122                return Util.instanceOf(sourceClass, this.getSourceClass()) && Date.class.equals(targetClass);
123            }
124    
125            public Date convert(Calendar obj) throws ConversionException {
126                return obj.getTime();
127            }
128        }
129    
130        /**
131         * An object that converts a <code>Calendar</code> to a <code>Long</code>.
132         */
133        public static class CalendarToLong extends AbstractConverter<Calendar, Long> {
134            public CalendarToLong() {
135                super(Calendar.class, Long.class);
136            }
137    
138            /**
139             * Returns the millisecond value of <code>obj</code>.
140             */
141            public Long convert(Calendar obj) throws ConversionException {
142                return obj.getTimeInMillis();
143            }
144        }
145    
146        /**
147         * An object that converts a <code>Calendar</code> to a <code>String</code>.
148         */
149        public static class CalendarToString extends AbstractLocalizedConverter<Calendar, String> {
150            public CalendarToString() {
151                super(Calendar.class, String.class);
152            }
153    
154            /**
155             * Converts <code>obj</code> to a <code>String</code> formatted as
156             * {@link DateTimeConverters#CALENDAR_FORMAT}. The returned string is
157             * referenced to the default time zone.
158             */
159            public String convert(Calendar obj) throws ConversionException {
160                DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
161                df.setCalendar(obj);
162                return df.format(obj.getTime());
163            }
164    
165            /**
166             * Converts <code>obj</code> to a <code>String</code> using the supplied
167             * locale, time zone, and format string. If <code>formatString</code> is
168             * <code>null</code>, the string is formatted as
169             * {@link DateTimeConverters#CALENDAR_FORMAT}.
170             */
171            public String convert(Calendar obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
172                DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
173                df.setCalendar(obj);
174                return df.format(obj.getTime());
175            }
176        }
177    
178        /**
179         * An object that converts a <code>Calendar</code> to a <code>Timestamp</code>.
180         */
181        public static class CalendarToTimestamp extends AbstractConverter<Calendar, Timestamp> {
182            public CalendarToTimestamp() {
183                super(Calendar.class, Timestamp.class);
184            }
185    
186            @Override
187            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
188                return Util.instanceOf(sourceClass, this.getSourceClass()) && Timestamp.class.equals(targetClass);
189            }
190    
191            public Timestamp convert(Calendar obj) throws ConversionException {
192                return new Timestamp(obj.getTimeInMillis());
193            }
194        }
195    
196    
197        /**
198         * An object that converts a <code>Date</code> to a <code>Calendar</code>.
199         */
200        public static class DateToCalendar extends GenericLocalizedConverter<Date, Calendar> {
201            public DateToCalendar() {
202                super(Date.class, Calendar.class);
203            }
204    
205            public Calendar convert(Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
206                Calendar cal = Calendar.getInstance(timeZone, locale);
207                cal.setTime(obj);
208                return cal;
209            }
210        }
211    
212        /**
213         * An object that converts a <code>java.util.Date</code> to a
214         * <code>java.sql.Date</code>.
215         */
216        public static class DateToSqlDate extends AbstractConverter<Date, java.sql.Date> {
217            public DateToSqlDate() {
218                super(Date.class, java.sql.Date.class);
219            }
220    
221            @Override
222            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
223                return (java.util.Date.class.equals(sourceClass) || java.sql.Timestamp.class.equals(sourceClass)) && java.sql.Date.class.equals(targetClass);
224            }
225    
226            /**
227             * Returns <code>obj</code> converted to a <code>java.sql.Date</code>.
228             */
229            @SuppressWarnings("deprecation")
230            public java.sql.Date convert(Date obj) throws ConversionException {
231                Calendar cal = Calendar.getInstance();
232                cal.setTimeInMillis(obj.getTime());
233                return new java.sql.Date(cal.get(Calendar.YEAR) - 1900, cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
234            }
235        }
236    
237        /**
238         * An object that converts a <code>java.util.Date</code> to a
239         * <code>java.sql.Time</code>.
240         */
241        public static class DateToSqlTime extends AbstractConverter<java.util.Date, java.sql.Time> {
242            public DateToSqlTime() {
243                super(Date.class, java.sql.Time.class);
244            }
245    
246            @Override
247            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
248                return sourceClass == this.getSourceClass() && targetClass == this.getTargetClass();
249            }
250    
251            @SuppressWarnings("deprecation")
252            public java.sql.Time convert(Date obj) throws ConversionException {
253                Calendar cal = Calendar.getInstance();
254                cal.setTimeInMillis(obj.getTime());
255                return new java.sql.Time(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND));
256            }
257        }
258    
259        /**
260         * An object that converts a <code>java.util.Date</code> to a
261         * <code>String</code>.
262         */
263        public static class DateToString extends AbstractLocalizedConverter<Date, String> {
264            public DateToString() {
265                super(Date.class, String.class);
266            }
267    
268            @Override
269            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
270                return Date.class.equals(sourceClass) && String.class.equals(targetClass);
271            }
272    
273            /**
274             * Converts <code>obj</code> to a <code>String</code> formatted as
275             * {@link DateTimeConverters#CALENDAR_FORMAT}. The returned string is
276             * referenced to the default time zone.
277             */
278            public String convert(Date obj) throws ConversionException {
279                DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
280                return df.format(obj);
281            }
282    
283            /**
284             * Converts <code>obj</code> to a <code>String</code> using the supplied
285             * locale, time zone, and format string. If <code>formatString</code> is
286             * <code>null</code>, the string is formatted as
287             * {@link DateTimeConverters#CALENDAR_FORMAT}.
288             */
289            public String convert(Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
290                DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
291                return df.format(obj);
292            }
293        }
294    
295        /**
296         * An object that converts a <code>java.util.Date</code> to a
297         * <code>java.sql.Timestamp</code>.
298         */
299        public static class DateToTimestamp extends AbstractConverter<Date, java.sql.Timestamp> {
300            public DateToTimestamp() {
301                super(Date.class, java.sql.Timestamp.class);
302            }
303    
304            @Override
305            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
306                return (java.util.Date.class.equals(sourceClass) || java.sql.Timestamp.class.equals(sourceClass)) && java.sql.Timestamp.class.equals(targetClass);
307            }
308    
309            /**
310             * Returns <code>obj</code> converted to a <code>java.sql.Timestamp</code>.
311             */
312            public java.sql.Timestamp convert(Date obj) throws ConversionException {
313                return new java.sql.Timestamp(obj.getTime());
314            }
315        }
316    
317        /**
318         * An object that converts a <code>java.util.Date</code> (and its subclasses) to a
319         * <code>Long</code>.
320         */
321        public static class GenericDateToLong<S extends Date> extends AbstractConverter<S, Long> {
322            public GenericDateToLong(Class<S> source) {
323                super(source, Long.class);
324            }
325    
326            /**
327             * Returns the millisecond value of <code>obj</code>.
328             */
329            public Long convert(S obj) throws ConversionException {
330                return obj.getTime();
331            }
332        }
333    
334        public static abstract class GenericLocalizedConverter<S, T> extends AbstractLocalizedConverter<S, T> {
335            protected GenericLocalizedConverter(Class<S> sourceClass, Class<T> targetClass) {
336                super(sourceClass, targetClass);
337            }
338    
339            public T convert(S obj) throws ConversionException {
340                return convert(obj, Locale.getDefault(), TimeZone.getDefault(), null);
341            }
342    
343            public T convert(S obj, Locale locale, TimeZone timeZone) throws ConversionException {
344                return convert(obj, locale, timeZone, null);
345            }
346        }
347    
348        /**
349         * An object that converts a <code>Long</code> to a
350         * <code>Calendar</code>.
351         */
352        public static class LongToCalendar extends AbstractLocalizedConverter<Long, Calendar> {
353            public LongToCalendar() {
354                super(Long.class, Calendar.class);
355            }
356    
357            /**
358             * Returns <code>obj</code> converted to a <code>Calendar</code>,
359             * initialized with the default locale and time zone.
360             */
361            public Calendar convert(Long obj) throws ConversionException {
362                Calendar cal = Calendar.getInstance();
363                cal.setTimeInMillis(obj);
364                return cal;
365            }
366    
367            /**
368             * Returns <code>obj</code> converted to a <code>Calendar</code>,
369             * initialized with the specified locale and time zone. The
370             * <code>formatString</code> parameter is ignored.
371             */
372            public Calendar convert(Long obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
373                Calendar cal = Calendar.getInstance(timeZone, locale);
374                cal.setTimeInMillis(obj);
375                return cal;
376            }
377        }
378    
379        /**
380         * An object that converts a <code>Long</code> to a
381         * <code>java.util.Date</code>.
382         */
383        public static class LongToDate extends AbstractConverter<Long, Date> {
384            public LongToDate() {
385                super(Long.class, Date.class);
386            }
387    
388            @Override
389            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
390                return Long.class.equals(sourceClass) && Date.class.equals(targetClass);
391            }
392    
393            /**
394             * Returns <code>obj</code> converted to a <code>java.util.Date</code>.
395             */
396            public Date convert(Long obj) throws ConversionException {
397                return new Date(obj.longValue());
398            }
399        }
400    
401        /**
402         * An object that converts a <code>Long</code> to a
403         * <code>java.sql.Date</code>.
404         */
405        public static class LongToSqlDate extends AbstractConverter<Long, java.sql.Date> {
406            public LongToSqlDate() {
407                super(Long.class, java.sql.Date.class);
408            }
409    
410            @Override
411            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
412                return Long.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass);
413            }
414    
415            /**
416             * Returns <code>obj</code> converted to a <code>java.sql.Date</code>.
417             */
418            public java.sql.Date convert(Long obj) throws ConversionException {
419                return new java.sql.Date(obj.longValue());
420            }
421        }
422    
423        /**
424         * An object that converts a <code>Long</code> to a
425         * <code>java.sql.Time</code>.
426         */
427        public static class LongToSqlTime extends AbstractConverter<Long, java.sql.Time> {
428            public LongToSqlTime() {
429                super(Long.class, java.sql.Time.class);
430            }
431    
432            @Override
433            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
434                return Long.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass);
435            }
436    
437            /**
438             * Returns <code>obj</code> converted to a <code>java.sql.Time</code>.
439             */
440            public java.sql.Time convert(Long obj) throws ConversionException {
441                return new java.sql.Time(obj.longValue());
442            }
443        }
444    
445        /**
446         * An object that converts a <code>Long</code> to a
447         * <code>java.sql.Timestamp</code>.
448         */
449        public static class LongToTimestamp extends AbstractConverter<Long, java.sql.Timestamp> {
450            public LongToTimestamp() {
451                super(Long.class, java.sql.Timestamp.class);
452            }
453    
454            @Override
455            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
456                return Long.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass);
457            }
458    
459            /**
460             * Returns <code>obj</code> converted to a <code>java.sql.Timestamp</code>.
461             */
462            public java.sql.Timestamp convert(Long obj) throws ConversionException {
463                return new java.sql.Timestamp(obj.longValue());
464            }
465        }
466    
467        /**
468         * An object that converts a <code>java.sql.Date</code> to a
469         * <code>java.util.Date</code>.
470         */
471        public static class SqlDateToDate extends AbstractConverter<java.sql.Date, Date> {
472            public SqlDateToDate() {
473                super(java.sql.Date.class, Date.class);
474            }
475    
476            @Override
477            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
478                return java.sql.Date.class.equals(sourceClass) && java.util.Date.class.equals(targetClass);
479            }
480    
481            /**
482             * Returns <code>obj</code> converted to a <code>java.util.Date</code>.
483             */
484            public Date convert(java.sql.Date obj) throws ConversionException {
485                return new Date(obj.getTime());
486            }
487        }
488    
489        /**
490         * An object that converts a <code>java.sql.Date</code> to a
491         * <code>String</code>.
492         */
493        public static class SqlDateToString extends AbstractLocalizedConverter<java.sql.Date, String> {
494            public SqlDateToString() {
495                super(java.sql.Date.class, String.class);
496            }
497    
498            @Override
499            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
500                return java.sql.Date.class.equals(sourceClass) && String.class.equals(targetClass);
501            }
502    
503            public String convert(java.sql.Date obj) throws ConversionException {
504                return obj.toString();
505            }
506    
507            /**
508             * Converts <code>obj</code> to a <code>String</code> using the supplied
509             * time zone. The <code>formatString</code> parameter is
510             * ignored. The returned string is formatted as
511             * {@link DateTimeConverters#JDBC_DATE_FORMAT}.
512             */
513            public String convert(java.sql.Date obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
514                DateFormat df = toDateFormat(timeZone);
515                return df.format(obj);
516            }
517        }
518    
519        /**
520         * An object that converts a <code>java.sql.Date</code> to a
521         * <code>java.sql.Timestamp</code>.
522         */
523        public static class SqlDateToTimestamp extends AbstractConverter<java.sql.Date, java.sql.Timestamp> {
524            public SqlDateToTimestamp() {
525                super(java.sql.Date.class, java.sql.Timestamp.class);
526            }
527    
528            @Override
529            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
530                return java.sql.Date.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass);
531            }
532    
533            /**
534             * Returns <code>obj</code> converted to a <code>java.sql.Timestamp</code>.
535             */
536            public java.sql.Timestamp convert(java.sql.Date obj) throws ConversionException {
537                return new java.sql.Timestamp(obj.getTime());
538            }
539        }
540    
541        /**
542         * An object that converts a <code>java.sql.Time</code> to a
543         * <code>String</code>.
544         */
545        public static class SqlTimeToString extends AbstractLocalizedConverter<java.sql.Time, String> {
546            public SqlTimeToString() {
547                super(java.sql.Time.class, String.class);
548            }
549    
550            @Override
551            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
552                return java.sql.Time.class.equals(sourceClass) && String.class.equals(targetClass);
553            }
554    
555            public String convert(java.sql.Time obj) throws ConversionException {
556                return obj.toString();
557            }
558    
559            /**
560             * Converts <code>obj</code> to a <code>String</code> using the supplied
561             * time zone. The <code>formatString</code> parameter is
562             * ignored. The returned string is formatted as
563             * {@link DateTimeConverters#JDBC_TIME_FORMAT}.
564             */
565            public String convert(java.sql.Time obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
566                DateFormat df = toTimeFormat(timeZone);
567                return df.format(obj);
568            }
569        }
570    
571        /**
572         * An object that converts a <code>String</code> to a
573         * <code>java.util.Calendar</code>.
574         */
575        public static class StringToCalendar extends AbstractLocalizedConverter<String, Calendar> {
576            public StringToCalendar() {
577                super(String.class, Calendar.class);
578            }
579    
580            /**
581             * Converts <code>obj</code> to a <code>java.util.Calendar</code> initialized to
582             * the default locale and time zone. The string must be formatted as
583             * {@link DateTimeConverters#CALENDAR_FORMAT}.
584             */
585            public Calendar convert(String obj) throws ConversionException {
586                try {
587                    DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
588                    Calendar cal = Calendar.getInstance();
589                    cal.setTime(df.parse(obj));
590                    return cal;
591                } catch (ParseException e) {
592                    throw new ConversionException(e);
593                }
594            }
595    
596            /**
597             * Converts <code>obj</code> to a <code>java.util.Calendar</code> initialized to
598             * the supplied locale and time zone. If <code>formatString</code> is
599             * <code>null</code>, the string is formatted as
600             * {@link DateTimeConverters#CALENDAR_FORMAT}.
601             */
602            public Calendar convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
603                DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
604                try {
605                    Date date = df.parse(obj);
606                    Calendar cal = Calendar.getInstance(timeZone, locale);
607                    cal.setTimeInMillis(date.getTime());
608                    return cal;
609                } catch (ParseException e) {
610                    throw new ConversionException(e);
611                }
612            }
613        }
614    
615        /**
616         * An object that converts a <code>String</code> to a
617         * <code>java.util.Date</code>.
618         */
619        public static class StringToDate extends AbstractLocalizedConverter<String, Date> {
620            public StringToDate() {
621                super(String.class, Date.class);
622            }
623    
624            @Override
625            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
626                return String.class.equals(sourceClass) && Date.class.equals(targetClass);
627            }
628    
629            /**
630             * Converts <code>obj</code> to a <code>java.util.Date</code>.
631             * The string must be formatted as
632             * {@link DateTimeConverters#CALENDAR_FORMAT}.
633             */
634            public Date convert(String obj) throws ConversionException {
635                try {
636                    DateFormat df = new SimpleDateFormat(CALENDAR_FORMAT);
637                    return df.parse(obj);
638                } catch (ParseException e) {
639                    throw new ConversionException(e);
640                }
641            }
642    
643            /**
644             * Converts <code>obj</code> to a <code>java.util.Date</code>. If
645             * <code>formatString</code> is <code>null</code>, the string is formatted as
646             * {@link DateTimeConverters#CALENDAR_FORMAT}.
647             */
648            public Date convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
649                DateFormat df = toDateTimeFormat(formatString == null ? CALENDAR_FORMAT : formatString, timeZone, locale);
650                try {
651                    return df.parse(obj);
652                } catch (ParseException e) {
653                    throw new ConversionException(e);
654                }
655            }
656        }
657    
658        /**
659         * An object that converts a <code>String</code> to a
660         * <code>java.sql.Date</code>.
661         */
662        public static class StringToSqlDate extends AbstractLocalizedConverter<String, java.sql.Date> {
663            public StringToSqlDate() {
664                super(String.class, java.sql.Date.class);
665            }
666    
667            @Override
668            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
669                return String.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass);
670            }
671    
672            public java.sql.Date convert(String obj) throws ConversionException {
673                return java.sql.Date.valueOf(obj);
674            }
675    
676            /**
677             * Converts <code>obj</code> to a <code>java.sql.Date</code> using the supplied
678             * time zone. The <code>locale</code> and <code>formatString</code> parameters are
679             * ignored. The string must be formatted as
680             * {@link DateTimeConverters#JDBC_DATE_FORMAT}.
681             */
682            public java.sql.Date convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
683                DateFormat df = toDateFormat(timeZone);
684                try {
685                    return new java.sql.Date(df.parse(obj).getTime());
686                } catch (ParseException e) {
687                    throw new ConversionException(e);
688                }
689            }
690        }
691    
692        /**
693         * An object that converts a <code>String</code> to a
694         * <code>java.sql.Time</code>.
695         */
696        public static class StringToSqlTime extends AbstractLocalizedConverter<String, java.sql.Time> {
697            public StringToSqlTime() {
698                super(String.class, java.sql.Time.class);
699            }
700    
701            @Override
702            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
703                return String.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass);
704            }
705    
706            public java.sql.Time convert(String obj) throws ConversionException {
707                return java.sql.Time.valueOf(obj);
708            }
709    
710            /**
711             * Converts <code>obj</code> to a <code>java.sql.Time</code> using the supplied
712             * time zone. The <code>locale</code> and <code>formatString</code> parameters are
713             * ignored. The string must be formatted as
714             * {@link DateTimeConverters#JDBC_TIME_FORMAT}.
715             */
716            public Time convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
717                DateFormat df = toTimeFormat(timeZone);
718                try {
719                    return new java.sql.Time(df.parse(obj).getTime());
720                } catch (ParseException e) {
721                    throw new ConversionException(e);
722                }
723            }
724        }
725    
726        /**
727         * An object that converts a <code>String</code> to a
728         * <code>java.sql.Timestamp</code>.
729         */
730        public static class StringToTimestamp extends AbstractLocalizedConverter<String, java.sql.Timestamp> {
731            public StringToTimestamp() {
732                super(String.class, java.sql.Timestamp.class);
733            }
734    
735            @Override
736            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
737                return String.class.equals(sourceClass) && java.sql.Timestamp.class.equals(targetClass);
738            }
739    
740            public Timestamp convert(String obj) throws ConversionException {
741                return java.sql.Timestamp.valueOf(obj);
742            }
743    
744            /**
745             * Converts <code>obj</code> to a <code>java.sql.Timestamp</code>.
746             * <p>Note that the string representation is referenced to the <code>timeZone</code>
747             * argument, not UTC. The <code>Timestamp</code> that is returned is adjusted to UTC.
748             * This behavior is intended to accommodate user-entered timestamps, where users are
749             * accustomed to using their own time zone.</p>
750             * </p>
751             */
752            public java.sql.Timestamp convert(String obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
753                try {
754                    // The String is referenced to the time zone represented by the timeZone
755                    // argument, but the parsing code assumes a reference to UTC. So, we need
756                    // to "adjust" the parsed Timestamp's value.
757                    Timestamp parsedStamp = Timestamp.valueOf(obj);
758                    Calendar cal = Calendar.getInstance(timeZone, locale);
759                    cal.setTime(parsedStamp);
760                    cal.add(Calendar.MILLISECOND, 0 - timeZone.getOffset(parsedStamp.getTime()));
761                    Timestamp result = new Timestamp(cal.getTimeInMillis());
762                    result.setNanos(parsedStamp.getNanos());
763                    return result;
764                } catch (Exception e) {
765                    throw new ConversionException(e);
766                }
767            }
768        }
769    
770        /**
771         * An object that converts a <code>String</code> ID to a
772         * <code>java.util.TimeZone</code>.
773         */
774        public static class StringToTimeZone extends AbstractConverter<String, TimeZone> {
775            public StringToTimeZone() {
776                super(String.class, TimeZone.class);
777            }
778    
779            public TimeZone convert(String obj) throws ConversionException {
780                return TimeZone.getTimeZone(obj);
781            }
782        }
783    
784        /**
785         * An object that converts a <code>java.sql.Timestamp</code> to a
786         * <code>java.util.Date</code>.
787         */
788        public static class TimestampToDate extends AbstractConverter<java.sql.Timestamp, Date> {
789            public TimestampToDate() {
790                super(java.sql.Timestamp.class, Date.class);
791            }
792    
793            @Override
794            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
795                return java.sql.Timestamp.class.equals(sourceClass) && java.util.Date.class.equals(targetClass);
796            }
797    
798            public Date convert(java.sql.Timestamp obj) throws ConversionException {
799                return new Timestamp(obj.getTime());
800            }
801        }
802    
803        /**
804         * An object that converts a <code>java.sql.Timestamp</code> to a
805         * <code>java.sql.Date</code>.
806         */
807        public static class TimestampToSqlDate extends AbstractConverter<java.sql.Timestamp, java.sql.Date> {
808            public TimestampToSqlDate() {
809                super(java.sql.Timestamp.class, java.sql.Date.class);
810            }
811    
812            @Override
813            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
814                return java.sql.Timestamp.class.equals(sourceClass) && java.sql.Date.class.equals(targetClass);
815            }
816    
817            public java.sql.Date convert(java.sql.Timestamp obj) throws ConversionException {
818                return new java.sql.Date(obj.getTime());
819            }
820        }
821    
822        /**
823         * An object that converts a <code>java.sql.Timestamp</code> to a
824         * <code>java.sql.Time</code>.
825         */
826        public static class TimestampToSqlTime extends AbstractConverter<java.sql.Timestamp, java.sql.Time> {
827            public TimestampToSqlTime() {
828                super(java.sql.Timestamp.class, java.sql.Time.class);
829            }
830    
831            @Override
832            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
833                return java.sql.Timestamp.class.equals(sourceClass) && java.sql.Time.class.equals(targetClass);
834            }
835    
836            public java.sql.Time convert(java.sql.Timestamp obj) throws ConversionException {
837                return new java.sql.Time(obj.getTime());
838            }
839        }
840    
841        /**
842         * An object that converts a <code>java.sql.Timestamp</code> to a
843         * <code>String</code>.
844         */
845        public static class TimestampToString extends AbstractLocalizedConverter<java.sql.Timestamp, String> {
846            public TimestampToString() {
847                super(java.sql.Timestamp.class, String.class);
848            }
849    
850            @Override
851            public boolean canConvert(Class<?> sourceClass, Class<?> targetClass) {
852                return java.sql.Timestamp.class.equals(sourceClass) && String.class.equals(targetClass);
853            }
854    
855            public String convert(java.sql.Timestamp obj) throws ConversionException {
856                return obj.toString();
857            }
858    
859            /**
860             * Converts <code>obj</code> to a <code>String</code> using the supplied
861             * time zone.
862             * <p>Note that the string representation is referenced to the <code>timeZone</code>
863             * argument, not UTC. The <code>Timestamp</code> is adjusted to the specified
864             * time zone before conversion. This behavior is intended to accommodate user interfaces,
865             * where users are accustomed to viewing timestamps in their own time zone.</p>
866             * </p>
867             */
868            public String convert(Timestamp obj, Locale locale, TimeZone timeZone, String formatString) throws ConversionException {
869                try {
870                    // The Timestamp is referenced to UTC, but the String result needs to be
871                    // referenced to the time zone represented by the timeZone argument.
872                    // So, we need to "adjust" the Timestamp's value before conversion.
873                    Calendar cal = Calendar.getInstance(timeZone, locale);
874                    cal.setTime(obj);
875                    cal.add(Calendar.MILLISECOND, timeZone.getOffset(obj.getTime()));
876                    Timestamp result = new Timestamp(cal.getTimeInMillis());
877                    result.setNanos(obj.getNanos());
878                    return result.toString();
879                } catch (Exception e) {
880                    throw new ConversionException(e);
881                }
882            }
883        }
884    
885        /**
886         * An object that converts a <code>java.util.TimeZone</code> to a
887         * <code>String</code> ID.
888         */
889        public static class TimeZoneToString extends AbstractConverter<TimeZone, String> {
890            public TimeZoneToString() {
891                super(TimeZone.class, String.class);
892            }
893    
894            public String convert(TimeZone obj) throws ConversionException {
895                return obj.getID();
896            }
897        }
898    }