001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.gwt.client.ui.input.datebox;
029
030import org.opencms.gwt.client.Messages;
031
032import java.util.Date;
033
034import com.google.gwt.i18n.client.DateTimeFormat;
035
036/**
037 * This class is an Helper with mostly static methods that convert a given date object
038 * or that convert a given String.<p>
039 */
040public final class CmsDateConverter {
041
042    /** A constant for am. */
043    public static final String AM = "am";
044
045    /** A constant for pm. */
046    public static final String PM = "pm";
047
048    /** The part of the 12 hour presentation which signals the am pm. */
049    private static final String AMPM_PATTERN_PART = "aa";
050
051    /** A pattern for date representation. */
052    private static final String DATE_PATTERN = Messages.get().key(Messages.GUI_DATEBOX_DATE_PATTERN_0);
053
054    /** A pattern for date time representation. */
055    private static final String DATETIME_PATTERN = Messages.get().key(Messages.GUI_DATEBOX_DATETIME_PATTERN_0);
056
057    /** A pattern for time representation. */
058    private static final String TIME_PATTERN = Messages.get().key(Messages.GUI_DATEBOX_TIME_PATTERN_0);
059
060    /** The formatter for the date format. */
061    private static final DateTimeFormat Z_DATE_FORMAT = DateTimeFormat.getFormat(DATE_PATTERN);
062
063    /** The format used to parse dates (this needs to be different from Z_DATE_FORMAT because we want to interpret the year 22 as 2022, but don't want to format 1922 as 22. */
064    private static final DateTimeFormat Z_DATE_FORMAT_SHORTYEAR = DateTimeFormat.getFormat(
065        DATE_PATTERN.replace("yyyy", "yy"));
066
067    /** The formatter for the date time format. */
068    private static final DateTimeFormat Z_DATETIME_FORMAT = DateTimeFormat.getFormat(DATETIME_PATTERN);
069
070    /** The format used to parse date/time combinations (this needs to be different from Z_DATETIME_FORMAT because we want to interpret the year 22 as 2022, but don't want to format 1922 as 22. */
071    private static final DateTimeFormat Z_DATETIME_FORMAT_SHORTYEAR = DateTimeFormat.getFormat(
072        DATETIME_PATTERN.replace("yyyy", "yy"));
073
074    /** The formatter for the time format. */
075    private static final DateTimeFormat Z_TIME_FORMAT = DateTimeFormat.getFormat(TIME_PATTERN);
076
077    /**
078     * Hiding constructor for final class.<p>
079     */
080    private CmsDateConverter() {
081
082        // nothing to do
083    }
084
085    /**
086     * Cuts the suffix (am or pm) from a given time String.<p>
087     *
088     * If the given String has less than 5 characters an dosen't
089     * contains an am or pm in it the original String is returned.<p>
090     *
091     * @param time the time String to cut the suffix from
092     *
093     * @return the time String without the suffix or the original String if the format of the String is incorrect
094     */
095    public static String cutSuffix(String time) {
096
097        String ret = time.toLowerCase();
098        if (ret.toLowerCase().contains(CmsDateConverter.AM) || ret.toLowerCase().contains(CmsDateConverter.PM)) {
099            if (ret.length() > 5) {
100                ret = ret.replace(CmsDateConverter.AM, "");
101                ret = ret.replace(CmsDateConverter.PM, "");
102                ret = ret.trim();
103            }
104        }
105        return ret;
106    }
107
108    /**
109     * Formats the provided date to a date only representation.<p>
110     *
111     * @param date the date to format
112     *
113     * @return the formatted date as a string
114     */
115    public static String dateToString(final Date date) {
116
117        String result;
118        if (date == null) {
119            result = "";
120        } else {
121            result = Z_DATE_FORMAT.format(date);
122        }
123        return result;
124    }
125
126    /**
127     * Merges a given Date object with a given time String.<p>
128     *
129     * Returns a <code>null</code> if the given time format coudn't be parsed.<p>
130     *
131     * The expected time String should include the am pm information if the time format is in 12 hour presentation.<p>
132     *
133     * @param date the given Date object which time has to be set
134     * @param time the given time String which should be inserted into the Date Object
135     *
136     * @return the merged date Object
137     */
138    @SuppressWarnings("deprecation")
139    public static Date getDateWithTime(Date date, String time) {
140
141        return getDateWithTime(date, time, Z_TIME_FORMAT);
142    }
143
144    /**
145     * Changes the time portion of date to another value.
146     *
147     * @param date the original date
148     * @param time the string representing the time to be set in the result
149     * @param timeFormat the format to use for parsing the time
150     *
151     * @return a new Date with the date from the date parameter and the time from the time parameter.
152     */
153    public static Date getDateWithTime(Date date, String time, DateTimeFormat timeFormat) {
154
155        Date result;
156        try {
157            Date timeAsDate = timeFormat.parse(time);
158            result = new Date(date.getYear(), date.getMonth(), date.getDate());
159            result.setHours(timeAsDate.getHours());
160            result.setMinutes(timeAsDate.getMinutes());
161            result.setSeconds(timeAsDate.getSeconds());
162        } catch (Exception e) {
163            result = null;
164        }
165        return result;
166
167    }
168
169    /**
170     * Returns the short time format of a given date as String.<p>
171     *
172     * @param date the date to get the short time format from
173     *
174     * @return the short time format of a given date
175     */
176    public static String getTime(Date date) {
177
178        return Z_TIME_FORMAT.format(date);
179    }
180
181    /**
182     * Returns <code>true</code> if the current date format is in the 12 hour
183     * representation mode <code>false</code> otherwise.<p>
184     *
185     * @return <code>true</code> if an am or a pm is in a new Date object <code>false</code> otherwise
186     */
187    public static boolean is12HourPresentation() {
188
189        return DATETIME_PATTERN.toLowerCase().contains(AMPM_PATTERN_PART);
190    }
191
192    /**
193     * Returns <code>true</code> if an am is in the given date object <code>false</code> otherwise.<p>
194     *
195     * @param date the date to check
196     *
197     * @return <code>true</code> if an am is in the given Date object <code>false</code> otherwise
198     */
199    public static boolean isAm(Date date) {
200
201        String time = getTime(date);
202        return time.toLowerCase().contains(AM);
203    }
204
205    /**
206     * Parses the provided String as a date.<p>
207     *
208     * First try to parse the String with the given time format.<p>
209     *
210     * If that fails try to parse the date with the browser settings.<p>
211     *
212     * @param dateText the string representing a date
213     *
214     * @return the date created, or null if there was a parse error
215     *
216     * @throws Exception in case the text can not be parsed to a date
217     */
218    public static Date toDate(final String dateText) throws Exception {
219
220        Date date = null;
221        if (dateText.length() > 0) {
222            date = Z_DATETIME_FORMAT_SHORTYEAR.parse(dateText.trim());
223            if (!validateDate(date)) {
224                throw new IllegalArgumentException();
225            }
226        }
227        return date;
228    }
229
230    /**
231     * Formats the provided date as only a date format (dd/mm/yyyy). Note, a null date is a possible input.
232     *
233     * @param date the date to format
234     *
235     * @return the formatted date as a string
236     */
237    public static String toDateString(final Date date) {
238
239        String result;
240        if (date == null) {
241            result = "";
242        } else {
243            result = Z_DATE_FORMAT.format(date);
244        }
245        return result;
246    }
247
248    /**
249     * Parses the provided String as a date.<p>
250     *
251     * First try to parse the String with the given time format.<p>
252     *
253     * If that fails try to parse the date with the browser settings.<p>
254     *
255     * @param dateText the string representing a date
256     *
257     * @return the date created, or null if there was a parse error
258     *
259     * @throws Exception in case the text can not be parsed to a date
260     */
261    public static Date toDayDate(final String dateText) throws Exception {
262
263        Date date = null;
264        if (dateText.length() > 0) {
265            date = Z_DATE_FORMAT_SHORTYEAR.parse(dateText.trim());
266        }
267        return date;
268    }
269
270    /**
271     * Formats the provided date. Note, a null date is a possible input.
272     *
273     * @param date the date to format
274     *
275     * @return the formatted date as a string
276     */
277    public static String toString(final Date date) {
278
279        String result;
280        if (date == null) {
281            result = "";
282        } else {
283            result = Z_DATETIME_FORMAT.format(date);
284        }
285        return result;
286    }
287
288    /**
289     * Validates a time String if it matches one of the two regular expressions.<p>
290     *
291     * Returns <code>true</code> if the given date matches to one of the regular
292     * expressions, <code>false</code> otherwise.<p>
293     *
294     * @param date the date String to check
295     *
296     * @return <code>true</code> if the given time matches to one of the regular expressions, <code>false</code> otherwise
297     */
298    public static boolean validateDate(Date date) {
299
300        String time = getTime(date);
301        boolean timeOk = validateTime(time);
302        return timeOk;
303
304    }
305
306    /**
307     * Validates a time String if it matches the regular expressions.<p>
308     *
309     * Returns <code>true</code> if the given time matches the regular
310     * expressions, <code>false</code> otherwise.<p>
311     *
312     * @param time the time String to check
313     *
314     * @return <code>true</code> if the given time matches the regular expressions, <code>false</code> otherwise
315     */
316    public static native boolean validateTime(String time) /*-{
317                                                           var hasMeridian = false;
318                                                           var re = /^\d{1,2}[:]\d{2}([:]\d{2})?( [aApP][mM]?)?$/;
319                                                           if (!re.test(time)) {
320                                                           return false;
321                                                           }
322                                                           if (time.toLowerCase().indexOf("p") != -1) {
323                                                           hasMeridian = true;
324                                                           }
325                                                           if (time.toLowerCase().indexOf("a") != -1) {
326                                                           hasMeridian = true;
327                                                           }
328                                                           var values = time.split(":");
329                                                           if ((parseFloat(values[0]) < 0) || (parseFloat(values[0]) > 23)) {
330                                                           return false;
331                                                           }
332                                                           if (hasMeridian) {
333                                                           if ((parseFloat(values[0]) < 1) || (parseFloat(values[0]) > 12)) {
334                                                           return false;
335                                                           }
336                                                           }
337                                                           if ((parseFloat(values[1]) < 0) || (parseFloat(values[1]) > 59)) {
338                                                           return false;
339                                                           }
340                                                           if (values.length > 2) {
341                                                           if ((parseFloat(values[2]) < 0) || (parseFloat(values[2]) > 59)) {
342                                                           return false;
343                                                           }
344                                                           }
345                                                           return true;
346                                                           }-*/;
347}