/* * reserved comment block * DO NOT REMOVE OR ALTER! */ /* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed 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: ExsltDatetime.java,v 1.2.4.1 2005/09/10 18:50:49 jeffsuttor Exp $ */ package com.sun.org.apache.xalan.internal.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 com.sun.org.apache.xpath.internal.objects.XBoolean; import com.sun.org.apache.xpath.internal.objects.XNumber; import com.sun.org.apache.xpath.internal.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().toString(); 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().toString(); 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() { Calendar cal = Calendar.getInstance(); 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(); } }