| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id$ |
| */ |
| |
| package org.apache.xalan.lib; |
| |
| |
| import java.text.ParseException; |
| import java.text.SimpleDateFormat; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.Locale; |
| import java.util.TimeZone; |
| |
| import org.apache.xpath.objects.XBoolean; |
| import org.apache.xpath.objects.XNumber; |
| import org.apache.xpath.objects.XObject; |
| |
| /** |
| * This class contains EXSLT dates and times extension functions. |
| * It is accessed by specifying a namespace URI as follows: |
| * <pre> |
| * xmlns:datetime="http://exslt.org/dates-and-times" |
| * </pre> |
| * |
| * The documentation for each function has been copied from the relevant |
| * EXSLT Implementer page. |
| * |
| * @see <a href="http://www.exslt.org/">EXSLT</a> |
| * @xsl.usage general |
| */ |
| |
| public class ExsltDatetime |
| { |
| // Datetime formats (era and zone handled separately). |
| static final String dt = "yyyy-MM-dd'T'HH:mm:ss"; |
| static final String d = "yyyy-MM-dd"; |
| static final String gym = "yyyy-MM"; |
| static final String gy = "yyyy"; |
| static final String gmd = "--MM-dd"; |
| static final String gm = "--MM--"; |
| static final String gd = "---dd"; |
| static final String t = "HH:mm:ss"; |
| static final String EMPTY_STR = ""; |
| |
| /** |
| * The date:date-time function returns the current date and time as a date/time string. |
| * The date/time string that's returned must be a string in the format defined as the |
| * lexical representation of xs:dateTime in |
| * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">[3.2.7 dateTime]</a> of |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The date/time format is basically CCYY-MM-DDThh:mm:ss, although implementers should consult |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a> and |
| * <a href="http://www.iso.ch/markete/8601.pdf">[ISO 8601]</a> for details. |
| * The date/time string format must include a time zone, either a Z to indicate Coordinated |
| * Universal Time or a + or - followed by the difference between the difference from UTC |
| * represented as hh:mm. |
| */ |
| public static String dateTime() |
| { |
| Calendar cal = Calendar.getInstance(); |
| Date datetime = cal.getTime(); |
| // Format for date and time. |
| SimpleDateFormat dateFormat = new SimpleDateFormat(dt); |
| |
| StringBuffer buff = new StringBuffer(dateFormat.format(datetime)); |
| // Must also include offset from UTF. |
| // Get the offset (in milliseconds). |
| int offset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); |
| // If there is no offset, we have "Coordinated |
| // Universal Time." |
| if (offset == 0) |
| buff.append("Z"); |
| else |
| { |
| // Convert milliseconds to hours and minutes |
| int hrs = offset/(60*60*1000); |
| // In a few cases, the time zone may be +/-hh:30. |
| int min = offset%(60*60*1000); |
| char posneg = hrs < 0? '-': '+'; |
| buff.append(posneg + formatDigits(hrs) + ':' + formatDigits(min)); |
| } |
| return buff.toString(); |
| } |
| |
| /** |
| * Represent the hours and minutes with two-digit strings. |
| * @param q hrs or minutes. |
| * @return two-digit String representation of hrs or minutes. |
| */ |
| private static String formatDigits(int q) |
| { |
| String dd = String.valueOf(Math.abs(q)); |
| return dd.length() == 1 ? '0' + dd : dd; |
| } |
| |
| /** |
| * The date:date function returns the date specified in the date/time string given |
| * as the argument. If no argument is given, then the current local date/time, as |
| * returned by date:date-time is used as a default argument. |
| * The date/time string that's returned must be a string in the format defined as the |
| * lexical representation of xs:dateTime in |
| * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">[3.2.7 dateTime]</a> of |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * If the argument is not in either of these formats, date:date returns an empty string (''). |
| * The date/time format is basically CCYY-MM-DDThh:mm:ss, although implementers should consult |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a> and |
| * <a href="http://www.iso.ch/markete/8601.pdf">[ISO 8601]</a> for details. |
| * The date is returned as a string with a lexical representation as defined for xs:date in |
| * [3.2.9 date] of [XML Schema Part 2: Datatypes]. The date format is basically CCYY-MM-DD, |
| * although implementers should consult [XML Schema Part 2: Datatypes] and [ISO 8601] for details. |
| * If no argument is given or the argument date/time specifies a time zone, then the date string |
| * format must include a time zone, either a Z to indicate Coordinated Universal Time or a + or - |
| * followed by the difference between the difference from UTC represented as hh:mm. If an argument |
| * is specified and it does not specify a time zone, then the date string format must not include |
| * a time zone. |
| */ |
| public static String date(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String leader = edz[0]; |
| String datetime = edz[1]; |
| String zone = edz[2]; |
| if (datetime == null || zone == null) |
| return EMPTY_STR; |
| |
| String[] formatsIn = {dt, d}; |
| String formatOut = d; |
| Date date = testFormats(datetime, formatsIn); |
| if (date == null) return EMPTY_STR; |
| |
| SimpleDateFormat dateFormat = new SimpleDateFormat(formatOut); |
| dateFormat.setLenient(false); |
| String dateOut = dateFormat.format(date); |
| if (dateOut.length() == 0) |
| return EMPTY_STR; |
| else |
| return (leader + dateOut + zone); |
| } |
| |
| |
| /** |
| * See above. |
| */ |
| public static String date() |
| { |
| String datetime = dateTime(); |
| String date = datetime.substring(0, datetime.indexOf("T")); |
| String zone = datetime.substring(getZoneStart(datetime)); |
| return (date + zone); |
| } |
| |
| /** |
| * The date:time function returns the time specified in the date/time string given |
| * as the argument. If no argument is given, then the current local date/time, as |
| * returned by date:date-time is used as a default argument. |
| * The date/time string that's returned must be a string in the format defined as the |
| * lexical representation of xs:dateTime in |
| * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime">[3.2.7 dateTime]</a> of |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * If the argument string is not in this format, date:time returns an empty string (''). |
| * The date/time format is basically CCYY-MM-DDThh:mm:ss, although implementers should consult |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a> and |
| * <a href="http://www.iso.ch/markete/8601.pdf">[ISO 8601]</a> for details. |
| * The date is returned as a string with a lexical representation as defined for xs:time in |
| * <a href="http://www.w3.org/TR/xmlschema-2/#time">[3.2.8 time]</a> of [XML Schema Part 2: Datatypes]. |
| * The time format is basically hh:mm:ss, although implementers should consult [XML Schema Part 2: |
| * Datatypes] and [ISO 8601] for details. |
| * If no argument is given or the argument date/time specifies a time zone, then the time string |
| * format must include a time zone, either a Z to indicate Coordinated Universal Time or a + or - |
| * followed by the difference between the difference from UTC represented as hh:mm. If an argument |
| * is specified and it does not specify a time zone, then the time string format must not include |
| * a time zone. |
| */ |
| public static String time(String timeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(timeIn); |
| String time = edz[1]; |
| String zone = edz[2]; |
| if (time == null || zone == null) |
| return EMPTY_STR; |
| |
| String[] formatsIn = {dt, d, t}; |
| String formatOut = t; |
| Date date = testFormats(time, formatsIn); |
| if (date == null) return EMPTY_STR; |
| SimpleDateFormat dateFormat = new SimpleDateFormat(formatOut); |
| String out = dateFormat.format(date); |
| return (out + zone); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static String time() |
| { |
| String datetime = dateTime(); |
| String time = datetime.substring(datetime.indexOf("T")+1); |
| |
| // The datetime() function returns the zone on the datetime string. If we |
| // append it, we get the zone substring duplicated. |
| // Fix for JIRA 2013 |
| |
| // String zone = datetime.substring(getZoneStart(datetime)); |
| // return (time + zone); |
| return (time); |
| } |
| |
| /** |
| * The date:year function returns the year of a date as a number. If no |
| * argument is given, then the current local date/time, as returned by |
| * date:date-time is used as a default argument. |
| * The date/time string specified as the first argument must be a right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime in one |
| * of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * xs:gYearMonth (CCYY-MM) |
| * xs:gYear (CCYY) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double year(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| boolean ad = edz[0].length() == 0; // AD (Common Era -- empty leader) |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, d, gym, gy}; |
| double yr = getNumber(datetime, formats, Calendar.YEAR); |
| if (ad || yr == Double.NaN) |
| return yr; |
| else |
| return -yr; |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double year() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.YEAR); |
| } |
| |
| /** |
| * The date:month-in-year function returns the month of a date as a number. If no argument |
| * is given, then the current local date/time, as returned by date:date-time is used |
| * as a default argument. |
| * The date/time string specified as the first argument is a left or right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime in one of |
| * the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * xs:gYearMonth (CCYY-MM) |
| * xs:gMonth (--MM--) |
| * xs:gMonthDay (--MM-DD) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double monthInYear(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, d, gym, gm, gmd}; |
| return getNumber(datetime, formats, Calendar.MONTH) + 1; |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double monthInYear() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.MONTH) + 1; |
| } |
| |
| /** |
| * The date:week-in-year function returns the week of the year as a number. If no argument |
| * is given, then the current local date/time, as returned by date:date-time is used as the |
| * default argument. For the purposes of numbering, counting follows ISO 8601: week 1 in a year |
| * is the week containing the first Thursday of the year, with new weeks beginning on a Monday. |
| * The date/time string specified as the argument is a right-truncated string in the format |
| * defined as the lexical representation of xs:dateTime in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. The |
| * permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double weekInYear(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, d}; |
| return getNumber(datetime, formats, Calendar.WEEK_OF_YEAR); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double weekInYear() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.WEEK_OF_YEAR); |
| } |
| |
| /** |
| * The date:day-in-year function returns the day of a date in a year |
| * as a number. If no argument is given, then the current local |
| * date/time, as returned by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double dayInYear(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, d}; |
| return getNumber(datetime, formats, Calendar.DAY_OF_YEAR); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double dayInYear() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.DAY_OF_YEAR); |
| } |
| |
| |
| /** |
| * The date:day-in-month function returns the day of a date as a number. |
| * If no argument is given, then the current local date/time, as returned |
| * by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a left or right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * xs:gMonthDay (--MM-DD) |
| * xs:gDay (---DD) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double dayInMonth(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| String[] formats = {dt, d, gmd, gd}; |
| double day = getNumber(datetime, formats, Calendar.DAY_OF_MONTH); |
| return day; |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double dayInMonth() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.DAY_OF_MONTH); |
| } |
| |
| /** |
| * The date:day-of-week-in-month function returns the day-of-the-week |
| * in a month of a date as a number (e.g. 3 for the 3rd Tuesday in May). |
| * If no argument is given, then the current local date/time, as returned |
| * by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a right-truncated string |
| * in the format defined as the lexical representation of xs:dateTime in one |
| * of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double dayOfWeekInMonth(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, d}; |
| return getNumber(datetime, formats, Calendar.DAY_OF_WEEK_IN_MONTH); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double dayOfWeekInMonth() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); |
| } |
| |
| |
| /** |
| * The date:day-in-week function returns the day of the week given in a |
| * date as a number. If no argument is given, then the current local date/time, |
| * as returned by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a right-truncated string |
| * in the format defined as the lexical representation of xs:dateTime in one |
| * of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| The numbering of days of the week starts at 1 for Sunday, 2 for Monday and so on up to 7 for Saturday. |
| */ |
| public static double dayInWeek(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, d}; |
| return getNumber(datetime, formats, Calendar.DAY_OF_WEEK); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double dayInWeek() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.DAY_OF_WEEK); |
| } |
| |
| /** |
| * The date:hour-in-day function returns the hour of the day as a number. |
| * If no argument is given, then the current local date/time, as returned |
| * by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:time (hh:mm:ss) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double hourInDay(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, t}; |
| return getNumber(datetime, formats, Calendar.HOUR_OF_DAY); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double hourInDay() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.HOUR_OF_DAY); |
| } |
| |
| /** |
| * The date:minute-in-hour function returns the minute of the hour |
| * as a number. If no argument is given, then the current local |
| * date/time, as returned by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:time (hh:mm:ss) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double minuteInHour(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt,t}; |
| return getNumber(datetime, formats, Calendar.MINUTE); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double minuteInHour() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.MINUTE); |
| } |
| |
| /** |
| * The date:second-in-minute function returns the second of the minute |
| * as a number. If no argument is given, then the current local |
| * date/time, as returned by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:time (hh:mm:ss) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static double secondInMinute(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return Double.NaN; |
| |
| String[] formats = {dt, t}; |
| return getNumber(datetime, formats, Calendar.SECOND); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static double secondInMinute() |
| { |
| Calendar cal = Calendar.getInstance(); |
| return cal.get(Calendar.SECOND); |
| } |
| |
| /** |
| * The date:leap-year function returns true if the year given in a date |
| * is a leap year. If no argument is given, then the current local |
| * date/time, as returned by date:date-time is used as a default argument. |
| * The date/time string specified as the first argument must be a |
| * right-truncated string in the format defined as the lexical representation |
| * of xs:dateTime in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * xs:gYearMonth (CCYY-MM) |
| * xs:gYear (CCYY) |
| * If the date/time string is not in one of these formats, then NaN is returned. |
| */ |
| public static XObject leapYear(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return new XNumber(Double.NaN); |
| |
| String[] formats = {dt, d, gym, gy}; |
| double dbl = getNumber(datetime, formats, Calendar.YEAR); |
| if (dbl == Double.NaN) |
| return new XNumber(Double.NaN); |
| int yr = (int)dbl; |
| return new XBoolean(yr % 400 == 0 || (yr % 100 != 0 && yr % 4 == 0)); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static boolean leapYear() |
| { |
| Calendar cal = Calendar.getInstance(); |
| int yr = (int)cal.get(Calendar.YEAR); |
| return (yr % 400 == 0 || (yr % 100 != 0 && yr % 4 == 0)); |
| } |
| |
| /** |
| * The date:month-name function returns the full name of the month of a date. |
| * If no argument is given, then the current local date/time, as returned by |
| * date:date-time is used the default argument. |
| * The date/time string specified as the argument is a left or right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime in |
| * one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * xs:gYearMonth (CCYY-MM) |
| * xs:gMonth (--MM--) |
| * If the date/time string is not in one of these formats, then an empty string ('') |
| * is returned. |
| * The result is an English month name: one of 'January', 'February', 'March', |
| * 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November' |
| * or 'December'. |
| */ |
| public static String monthName(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return EMPTY_STR; |
| |
| String[] formatsIn = {dt, d, gym, gm}; |
| String formatOut = "MMMM"; |
| return getNameOrAbbrev(datetimeIn, formatsIn, formatOut); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static String monthName() |
| { |
| String format = "MMMM"; |
| return getNameOrAbbrev(format); |
| } |
| |
| /** |
| * The date:month-abbreviation function returns the abbreviation of the month of |
| * a date. If no argument is given, then the current local date/time, as returned |
| * by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a left or right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime in |
| * one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * xs:gYearMonth (CCYY-MM) |
| * xs:gMonth (--MM--) |
| * If the date/time string is not in one of these formats, then an empty string ('') |
| * is returned. |
| * The result is a three-letter English month abbreviation: one of 'Jan', 'Feb', 'Mar', |
| * 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov' or 'Dec'. |
| * An implementation of this extension function in the EXSLT date namespace must conform |
| * to the behaviour described in this document. |
| */ |
| public static String monthAbbreviation(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return EMPTY_STR; |
| |
| String[] formatsIn = {dt, d, gym, gm}; |
| String formatOut = "MMM"; |
| return getNameOrAbbrev(datetimeIn, formatsIn, formatOut); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static String monthAbbreviation() |
| { |
| String format = "MMM"; |
| return getNameOrAbbrev(format); |
| } |
| |
| /** |
| * The date:day-name function returns the full name of the day of the week |
| * of a date. If no argument is given, then the current local date/time, |
| * as returned by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a left or right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * If the date/time string is not in one of these formats, then the empty string ('') |
| * is returned. |
| * The result is an English day name: one of 'Sunday', 'Monday', 'Tuesday', 'Wednesday', |
| * 'Thursday' or 'Friday'. |
| * An implementation of this extension function in the EXSLT date namespace must conform |
| * to the behaviour described in this document. |
| */ |
| public static String dayName(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return EMPTY_STR; |
| |
| String[] formatsIn = {dt, d}; |
| String formatOut = "EEEE"; |
| return getNameOrAbbrev(datetimeIn, formatsIn, formatOut); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static String dayName() |
| { |
| String format = "EEEE"; |
| return getNameOrAbbrev(format); |
| } |
| |
| /** |
| * The date:day-abbreviation function returns the abbreviation of the day |
| * of the week of a date. If no argument is given, then the current local |
| * date/time, as returned by date:date-time is used the default argument. |
| * The date/time string specified as the argument is a left or right-truncated |
| * string in the format defined as the lexical representation of xs:dateTime |
| * in one of the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * xs:date (CCYY-MM-DD) |
| * If the date/time string is not in one of these formats, then the empty string |
| * ('') is returned. |
| * The result is a three-letter English day abbreviation: one of 'Sun', 'Mon', 'Tue', |
| * 'Wed', 'Thu' or 'Fri'. |
| * An implementation of this extension function in the EXSLT date namespace must conform |
| * to the behaviour described in this document. |
| */ |
| public static String dayAbbreviation(String datetimeIn) |
| throws ParseException |
| { |
| String[] edz = getEraDatetimeZone(datetimeIn); |
| String datetime = edz[1]; |
| if (datetime == null) |
| return EMPTY_STR; |
| |
| String[] formatsIn = {dt, d}; |
| String formatOut = "EEE"; |
| return getNameOrAbbrev(datetimeIn, formatsIn, formatOut); |
| } |
| |
| /** |
| * See above. |
| */ |
| public static String dayAbbreviation() |
| { |
| String format = "EEE"; |
| return getNameOrAbbrev(format); |
| } |
| |
| /** |
| * Returns an array with the 3 components that a datetime input string |
| * may contain: - (for BC era), datetime, and zone. If the zone is not |
| * valid, return null for that component. |
| */ |
| private static String[] getEraDatetimeZone(String in) |
| { |
| String leader = ""; |
| String datetime = in; |
| String zone = ""; |
| if (in.charAt(0)=='-' && !in.startsWith("--")) |
| { |
| leader = "-"; // '+' is implicit , not allowed |
| datetime = in.substring(1); |
| } |
| int z = getZoneStart(datetime); |
| if (z > 0) |
| { |
| zone = datetime.substring(z); |
| datetime = datetime.substring(0, z); |
| } |
| else if (z == -2) |
| zone = null; |
| //System.out.println("'" + leader + "' " + datetime + " " + zone); |
| return new String[]{leader, datetime, zone}; |
| } |
| |
| /** |
| * Get the start of zone information if the input ends |
| * with 'Z' or +/-hh:mm. If a zone string is not |
| * found, return -1; if the zone string is invalid, |
| * return -2. |
| */ |
| private static int getZoneStart (String datetime) |
| { |
| if (datetime.indexOf("Z") == datetime.length()-1) |
| return datetime.length()-1; |
| else if (datetime.length() >=6 |
| && datetime.charAt(datetime.length()-3) == ':' |
| && (datetime.charAt(datetime.length()-6) == '+' |
| || datetime.charAt(datetime.length()-6) == '-')) |
| { |
| try |
| { |
| SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm"); |
| dateFormat.setLenient(false); |
| Date d = dateFormat.parse(datetime.substring(datetime.length() -5)); |
| return datetime.length()-6; |
| } |
| catch (ParseException pe) |
| { |
| System.out.println("ParseException " + pe.getErrorOffset()); |
| return -2; // Invalid. |
| } |
| |
| } |
| return -1; // No zone information. |
| } |
| |
| /** |
| * Attempt to parse an input string with the allowed formats, returning |
| * null if none of the formats work. |
| */ |
| private static Date testFormats (String in, String[] formats) |
| throws ParseException |
| { |
| for (int i = 0; i <formats.length; i++) |
| { |
| try |
| { |
| SimpleDateFormat dateFormat = new SimpleDateFormat(formats[i]); |
| dateFormat.setLenient(false); |
| return dateFormat.parse(in); |
| } |
| catch (ParseException pe) |
| { |
| } |
| } |
| return null; |
| } |
| |
| |
| /** |
| * Parse the input string and return the corresponding calendar field |
| * number. |
| */ |
| private static double getNumber(String in, String[] formats, int calField) |
| throws ParseException |
| { |
| Calendar cal = Calendar.getInstance(); |
| cal.setLenient(false); |
| // Try the allowed formats, from longest to shortest. |
| Date date = testFormats(in, formats); |
| if (date == null) return Double.NaN; |
| cal.setTime(date); |
| return cal.get(calField); |
| } |
| |
| /** |
| * Get the full name or abbreviation of the month or day. |
| */ |
| private static String getNameOrAbbrev(String in, |
| String[] formatsIn, |
| String formatOut) |
| throws ParseException |
| { |
| for (int i = 0; i <formatsIn.length; i++) // from longest to shortest. |
| { |
| try |
| { |
| SimpleDateFormat dateFormat = new SimpleDateFormat(formatsIn[i], Locale.ENGLISH); |
| dateFormat.setLenient(false); |
| Date dt = dateFormat.parse(in); |
| dateFormat.applyPattern(formatOut); |
| return dateFormat.format(dt); |
| } |
| catch (ParseException pe) |
| { |
| } |
| } |
| return ""; |
| } |
| /** |
| * Get the full name or abbreviation for the current month or day |
| * (no input string). |
| */ |
| private static String getNameOrAbbrev(String format) |
| { |
| Calendar cal = Calendar.getInstance(); |
| SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.ENGLISH); |
| return dateFormat.format(cal.getTime()); |
| } |
| |
| /** |
| * The date:format-date function formats a date/time according to a pattern. |
| * <p> |
| * The first argument to date:format-date specifies the date/time to be |
| * formatted. It must be right or left-truncated date/time strings in one of |
| * the formats defined in |
| * <a href="http://www.w3.org/TR/xmlschema-2/">[XML Schema Part 2: Datatypes]</a>. |
| * The permitted formats are as follows: |
| * <ul> |
| * <li>xs:dateTime (CCYY-MM-DDThh:mm:ss) |
| * <li>xs:date (CCYY-MM-DD) |
| * <li>xs:time (hh:mm:ss) |
| * <li>xs:gYearMonth (CCYY-MM) |
| * <li>xs:gYear (CCYY) |
| * <li>xs:gMonthDay (--MM-DD) |
| * <li>xs:gMonth (--MM--) |
| * <li>xs:gDay (---DD) |
| * </ul> |
| * The second argument is a string that gives the format pattern used to |
| * format the date. The format pattern must be in the syntax specified by |
| * the JDK 1.1 SimpleDateFormat class. The format pattern string is |
| * interpreted as described for the JDK 1.1 SimpleDateFormat class. |
| * <p> |
| * If the date/time format is right-truncated (i.e. in a format other than |
| * xs:time, or xs:dateTime) then any missing components are assumed to be as |
| * follows: if no month is specified, it is given a month of 01; if no day |
| * is specified, it is given a day of 01; if no time is specified, it is |
| * given a time of 00:00:00. |
| * <p> |
| * If the date/time format is left-truncated (i.e. xs:time, xs:gMonthDay, |
| * xs:gMonth or xs:gDay) and the format pattern has a token that uses a |
| * component that is missing from the date/time format used, then that token |
| * is replaced with an empty string ('') within the result. |
| * |
| * The author is Helg Bredow (helg.bredow@kalido.com) |
| */ |
| public static String formatDate(String dateTime, String pattern) |
| { |
| final String yearSymbols = "Gy"; |
| final String monthSymbols = "M"; |
| final String daySymbols = "dDEFwW"; |
| TimeZone timeZone; |
| String zone; |
| |
| // Get the timezone information if it was supplied and modify the |
| // dateTime so that SimpleDateFormat will understand it. |
| if (dateTime.endsWith("Z") || dateTime.endsWith("z")) |
| { |
| timeZone = TimeZone.getTimeZone("GMT"); |
| dateTime = dateTime.substring(0, dateTime.length()-1) + "GMT"; |
| zone = "z"; |
| } |
| else if ((dateTime.length() >= 6) |
| && (dateTime.charAt(dateTime.length()-3) == ':') |
| && ((dateTime.charAt(dateTime.length()-6) == '+') |
| || (dateTime.charAt(dateTime.length()-6) == '-'))) |
| { |
| String offset = dateTime.substring(dateTime.length()-6); |
| |
| if ("+00:00".equals(offset) || "-00:00".equals(offset)) |
| { |
| timeZone = TimeZone.getTimeZone("GMT"); |
| } |
| else |
| { |
| timeZone = TimeZone.getTimeZone("GMT" + offset); |
| } |
| zone = "z"; |
| // Need to adjust it since SimpleDateFormat requires GMT+hh:mm but |
| // we have +hh:mm. |
| dateTime = dateTime.substring(0, dateTime.length()-6) + "GMT" + offset; |
| } |
| else |
| { |
| // Assume local time. |
| timeZone = TimeZone.getDefault(); |
| zone = ""; |
| // Leave off the timezone since SimpleDateFormat will assume local |
| // time if time zone is not included. |
| } |
| String[] formats = {dt + zone, d, gym, gy}; |
| |
| // Try the time format first. We need to do this to prevent |
| // SimpleDateFormat from interpreting a time as a year. i.e we just need |
| // to check if it's a time before we check it's a year. |
| try |
| { |
| SimpleDateFormat inFormat = new SimpleDateFormat(t + zone); |
| inFormat.setLenient(false); |
| Date d= inFormat.parse(dateTime); |
| SimpleDateFormat outFormat = new SimpleDateFormat(strip |
| (yearSymbols + monthSymbols + daySymbols, pattern)); |
| outFormat.setTimeZone(timeZone); |
| return outFormat.format(d); |
| } |
| catch (ParseException pe) |
| { |
| } |
| |
| // Try the right truncated formats. |
| for (int i = 0; i < formats.length; i++) |
| { |
| try |
| { |
| SimpleDateFormat inFormat = new SimpleDateFormat(formats[i]); |
| inFormat.setLenient(false); |
| Date d = inFormat.parse(dateTime); |
| SimpleDateFormat outFormat = new SimpleDateFormat(pattern); |
| outFormat.setTimeZone(timeZone); |
| return outFormat.format(d); |
| } |
| catch (ParseException pe) |
| { |
| } |
| } |
| |
| // Now try the left truncated ones. The Java format() function doesn't |
| // return the correct strings in this case. We strip any pattern |
| // symbols that shouldn't be output so that they are not defaulted to |
| // inappropriate values in the output. |
| try |
| { |
| SimpleDateFormat inFormat = new SimpleDateFormat(gmd); |
| inFormat.setLenient(false); |
| Date d = inFormat.parse(dateTime); |
| SimpleDateFormat outFormat = new SimpleDateFormat(strip(yearSymbols, pattern)); |
| outFormat.setTimeZone(timeZone); |
| return outFormat.format(d); |
| } |
| catch (ParseException pe) |
| { |
| } |
| try |
| { |
| SimpleDateFormat inFormat = new SimpleDateFormat(gm); |
| inFormat.setLenient(false); |
| Date d = inFormat.parse(dateTime); |
| SimpleDateFormat outFormat = new SimpleDateFormat(strip(yearSymbols, pattern)); |
| outFormat.setTimeZone(timeZone); |
| return outFormat.format(d); |
| } |
| catch (ParseException pe) |
| { |
| } |
| try |
| { |
| SimpleDateFormat inFormat = new SimpleDateFormat(gd); |
| inFormat.setLenient(false); |
| Date d = inFormat.parse(dateTime); |
| SimpleDateFormat outFormat = new SimpleDateFormat(strip(yearSymbols + monthSymbols, pattern)); |
| outFormat.setTimeZone(timeZone); |
| return outFormat.format(d); |
| } |
| catch (ParseException pe) |
| { |
| } |
| return EMPTY_STR; |
| } |
| |
| /** |
| * Strips occurrences of the given character from a date format pattern. |
| * @param symbols list of symbols to strip. |
| * @param pattern |
| * @return |
| */ |
| private static String strip(String symbols, String pattern) |
| { |
| int quoteSemaphore = 0; |
| int i = 0; |
| StringBuffer result = new StringBuffer(pattern.length()); |
| |
| while (i < pattern.length()) |
| { |
| char ch = pattern.charAt(i); |
| if (ch == '\'') |
| { |
| // Assume it's an openening quote so simply copy the quoted |
| // text to the result. There is nothing to strip here. |
| int endQuote = pattern.indexOf('\'', i + 1); |
| if (endQuote == -1) |
| { |
| endQuote = pattern.length(); |
| } |
| result.append(pattern.substring(i, endQuote)); |
| i = endQuote++; |
| } |
| else if (symbols.indexOf(ch) > -1) |
| { |
| // The char needs to be stripped. |
| i++; |
| } |
| else |
| { |
| result.append(ch); |
| i++; |
| } |
| } |
| return result.toString(); |
| } |
| |
| } |