All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.option.exslt.Date Maven / Gradle / Ivy

There is a newer version: 10.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package net.sf.saxon.option.exslt;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.functions.Component;
import net.sf.saxon.lib.ConversionRules;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.type.ConversionResult;
import net.sf.saxon.type.Converter;
import net.sf.saxon.type.ValidationFailure;
import net.sf.saxon.value.*;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * This class implements extension functions in the
 * http://exslt.org/dates-and-times namespace. 

*/ public final class Date { /** * Private constructor to disallow instantiation */ private Date() { } /** * 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 [3.2.7 dateTime] of [XML Schema Part 2: Datatypes]. * The date/time format is basically CCYY-MM-DDThh:mm:ss+hh:mm. * 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. * @param context the XPath dynamic context * @return the current date and time as a date/time string */ public static String dateTime(XPathContext context) throws XPathException { return context.getCurrentDateTime().getStringValue(); } /** * 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 * [3.2.7 dateTime] of * [XML Schema Part 2: Datatypes]. * 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 * [XML Schema Part 2: Datatypes] and * [ISO 8601] 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(XPathContext context, String datetimeIn) { ConversionRules rules = context.getConfiguration().getConversionRules(); datetimeIn = nn(datetimeIn); if (datetimeIn.indexOf('T') >= 0) { ConversionResult cr = DateTimeValue.makeDateTimeValue(datetimeIn, rules); if (cr instanceof ValidationFailure) { return ""; } else { return ((DateTimeValue)cr).toDateValue().getStringValue(); } } else { ConversionResult cr = DateValue.makeDateValue(datetimeIn, rules); if (cr instanceof ValidationFailure) { return ""; } else { return ((AtomicValue)cr).getStringValue(); } } } /** * The date:date function returns the current date. * @param context the XPath dynamic context * @return the current date as a string */ public static String date(XPathContext context) throws XPathException { return date(context, dateTime(context)); } /** * The date:time function returns the time specified in the date/time string given as the * argument. * @param dateTime must start with [+|-]CCYY-MM-DDThh:mm:ss * @return the time part of the string */ public static String time(XPathContext context, String dateTime) { dateTime = nn(dateTime); if (dateTime.indexOf('T') >= 0) { ConversionResult cr = DateTimeValue.makeDateTimeValue(dateTime, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return ""; } else { return ((DateTimeValue)cr).toTimeValue().getStringValue(); } } else { ConversionResult cr = TimeValue.makeTimeValue(dateTime); if (cr instanceof ValidationFailure) { return ""; } else { return ((AtomicValue)cr).getStringValue(); } } } /** * The date:time function returns the current time. * @param context the XPath dynamic context * @return the current time as a string */ public static String time(XPathContext context) throws XPathException { return time(context, dateTime(context)); } /** * 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 * [XML Schema Part 2: Datatypes]. * 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. *

Note: although not specifically permitted in the EXSLT specification, the Saxon * implementation also allows the input value to contain a timezone

* @throws XPathException */ public static double year(XPathContext context, String datetimeIn) { datetimeIn = nn(datetimeIn); try { ConversionResult cr = CalendarValue.makeCalendarValue(datetimeIn, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return Double.NaN; } if (cr instanceof GMonthValue || cr instanceof GMonthDayValue || cr instanceof GDayValue || cr instanceof TimeValue) { return Double.NaN; } AtomicValue year = ((CalendarValue)cr).getComponent(Component.YEAR); return ((NumericValue)year).getDoubleValue(); } catch (XPathException e) { return Double.NaN; } } /** * The date:year function returns the current year. * @param context the XPath dynamic context * @return the current year as a double */ public static double year(XPathContext context) throws XPathException { return year(context, dateTime(context)); } /** * Return true if the year specified in the date/time string * given as the argument is a leap year. * @param dateTime a dateTime as a string * @return true if the year is a leap year * @throws XPathException */ public static boolean leapYear(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); double year = year(context, dateTime); if (Double.isNaN(year)) { return false; } int y = (int)year; return (y % 4 == 0) && !((y % 100 == 0) && !(y % 400 == 0)); } /** * Returns true if the current year is a leap year * @param context the XPath dynamic context * @return true if the current year is a leap year */ public static boolean leapYear(XPathContext context) throws XPathException { return leapYear(context, dateTime(context)); } /** * Return the month number from a date. * The date must start with either "CCYY-MM" or "--MM" * @param dateTime a dateTime as a string * @return the month extracted from the dateTime */ public static double monthInYear(XPathContext context, String dateTime) { dateTime = nn(dateTime); try { ConversionResult cr = CalendarValue.makeCalendarValue(dateTime, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return Double.NaN; } if (cr instanceof GYearValue || cr instanceof GDayValue || cr instanceof TimeValue) { return Double.NaN; } AtomicValue month = ((CalendarValue)cr).getComponent(Component.MONTH); return ((NumericValue)month).getDoubleValue(); } catch (XPathException e) { return Double.NaN; } } /** * Return the month number from the current date. * @param context the XPath dynamic context * @return the current month number */ public static double monthInYear(XPathContext context) throws XPathException { return monthInYear(context, dateTime(context)); } /** * Return the month name from a date. * The date must start with either "CCYY-MM" or "--MM" * @param date the date/time as a string * @return the English month name, for example "January", "February" */ public static String monthName(XPathContext context, String date) { date = nn(date); String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; double m = monthInYear(context, date); if (Double.isNaN(m)) { return ""; } return months[(int)m - 1]; } /** * Return the month name from the current date. * @param context the XPath dynamic context * @return the English month name, for example "January", "February" */ public static String monthName(XPathContext context) throws XPathException { return monthName(context, dateTime(context)); } /** * Return the month abbreviation from a date. * @param date The date must start with either "CCYY-MM" or "--MM" * @return the English month abbreviation, for example "Jan", "Feb" */ public static String monthAbbreviation(XPathContext context, String date) { date = nn(date); String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; double m = monthInYear(context, date); if (Double.isNaN(m)) { return ""; } return months[(int)m - 1]; } /** * Return the month abbreviation from the current date. * @param context the XPath dynamic context * @return the English month abbreviation, for example "Jan", "Feb" */ public static String monthAbbreviation(XPathContext context) throws XPathException { return monthAbbreviation(context, dateTime(context)); } /** * Return the ISO week number of a specified date within the year * (Note, this returns the ISO week number: the result in EXSLT is underspecified) * @param dateTime the current date starting CCYY-MM-DD * @return the ISO week number */ public static double weekInYear(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); int dayInYear = (int)dayInYear(context, dateTime); String firstJan = dateTime.substring(0, 4) + "-01-01"; int jan1day = ((int)dayInWeek(context, firstJan) + 5) % 7; int daysInFirstWeek = (jan1day == 0 ? 0 : 7 - jan1day); int rawWeek = (dayInYear - daysInFirstWeek + 6) / 7; if (daysInFirstWeek >= 4) { return rawWeek + 1; } else { if (rawWeek > 0) { return rawWeek; } else { // week number should be 52 or 53: same as 31 Dec in previous year int lastYear = Integer.parseInt(dateTime.substring(0, 4)) - 1; String dec31 = lastYear + "-12-31"; // assumes year > 999 return weekInYear(context, dec31); } } } /** * Return the ISO week number of the current date * @param context the XPath dynamic context * (Note, this returns the ISO week number: the result in EXSLT is underspecified) * @return the ISO week number */ public static double weekInYear(XPathContext context) throws XPathException { return weekInYear(context, dateTime(context)); } /** * Return the week number of a specified date within the month * (Note, this function is underspecified in EXSLT) * @param dateTime the date starting CCYY-MM-DD * @return the week number within the month */ public static double weekInMonth(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); return (double)(int)((dayInMonth(context, dateTime) - 1) / 7 + 1); } /** * Return the ISO week number of the current date within the month * @param context the XPath dynamic context * @return the week number within the month */ public static double weekInMonth(XPathContext context) throws XPathException { return weekInMonth(context, dateTime(context)); } /** * Return the day number of a specified date within the year * @param dateTime the date starting with CCYY-MM-DD * @return the day number within the year, as a double */ public static double dayInYear(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); int month = (int)monthInYear(context, dateTime); int day = (int)dayInMonth(context, dateTime); int[] prev = {0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31}; int leap = (month > 2 && leapYear(context, dateTime) ? 1 : 0); return prev[month - 1] + leap + day; } /** * Return the day number of the current date within the year * @param context the XPath dynamic context * @return the day number within the year, as a double */ public static double dayInYear(XPathContext context) throws XPathException { return dayInYear(context, dateTime(context)); } /** * Return the day number of a specified date within the month * @param dateTime must start with CCYY-MM-DD, or --MM-DD, or ---DD * @return the day number within the month, as a double */ public static double dayInMonth(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); try { ConversionResult cr = CalendarValue.makeCalendarValue(dateTime, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return Double.NaN; } if (cr instanceof GYearValue || cr instanceof GYearMonthValue || cr instanceof GMonthValue || cr instanceof TimeValue) { return Double.NaN; } AtomicValue day = ((CalendarValue)cr).getComponent(Component.DAY); return ((NumericValue)day).getDoubleValue(); } catch (XPathException e) { return Double.NaN; } } /** * Return the day number of the current date within the month * @param context the XPath dynamic context * @return the current day number, as a double */ public static double dayInMonth(XPathContext context) throws XPathException { return dayInMonth(context, dateTime(context)); } /** * Return the day-of-the-week in a month of a date as a number * (for example 3 for the 3rd Tuesday in May). * @param dateTime must start with CCYY-MM-DD * @return the the day-of-the-week in a month of a date as a number * (for example 3 for the 3rd Tuesday in May). */ public static double dayOfWeekInMonth(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); double dd = dayInMonth(context, dateTime); if (Double.isNaN(dd)) { return dd; } return (((int)dd) - 1) / 7 + 1; } /** * Return the day-of-the-week in a month of the current date as a number * (for example 3 for the 3rd Tuesday in May). * @param context the XPath dynamic context * @return the the day-of-the-week in a month of the current date as a number * (for example 3 for the 3rd Tuesday in May). */ public static double dayOfWeekInMonth(XPathContext context) throws XPathException { return dayOfWeekInMonth(context, dateTime(context)); } /** * Return the day of the week given in a date as a number. * The numbering of days of the week starts at 1 for Sunday, 2 for Monday * and so on up to 7 for Saturday. * @param dateTime must start with CCYY-MM-DD * @return the day of the week as a number */ public static double dayInWeek(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); double yy = year(context, dateTime); double mm = monthInYear(context, dateTime); double dd = dayInMonth(context, dateTime); if (Double.isNaN(yy) || Double.isNaN(mm) || Double.isNaN(dd)) { return Double.NaN; } GregorianCalendar calDate = new GregorianCalendar( (int)yy, (int)mm - 1, (int)dd); calDate.setFirstDayOfWeek(Calendar.SUNDAY); return calDate.get(Calendar.DAY_OF_WEEK); } /** * Return the day of the week in the current date as a number. * The numbering of days of the week starts at 1 for Sunday, 2 for Monday * and so on up to 7 for Saturday. * @param context the XPath dynamic context * @return the day of the week as a number */ public static double dayInWeek(XPathContext context) throws XPathException { return dayInWeek(context, dateTime(context)); } /** * Return the day of the week given in a date as an English day name: * one of 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'. * @param dateTime must start with CCYY-MM-DD * @return the English name of the day of the week */ public static String dayName(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); String[] days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; double d = dayInWeek(context, dateTime); if (Double.isNaN(d)) { return ""; } return days[(int)d - 1]; } /** * Return the day of the week given in the current date as an English day name: * one of 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday' or 'Friday'. * @param context the XPath dynamic context * @return the English name of the day of the week */ public static String dayName(XPathContext context) throws XPathException { return dayName(context, dateTime(context)); } /** * Return the day of the week given in a date as an English day abbreviation: * one of 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', or 'Sat'. * @param dateTime must start with CCYY-MM-DD * @return the English day abbreviation */ public static String dayAbbreviation(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); String[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; double d = dayInWeek(context, dateTime); if (Double.isNaN(d)) { return ""; } return days[(int)d - 1]; } /** * Return the day of the week given in the current date as an English day abbreviation: * one of 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', or 'Sat'. * @param context the XPath dynamic context * @return the English day abbreviation */ public static String dayAbbreviation(XPathContext context) throws XPathException { return dayAbbreviation(context, dateTime(context)); } /** * Return the hour of the day in the specified date or date/time * @param dateTime must start with CCYY-MM-DDThh:mm:ss or hh:mm:ss * @return the hour */ public static double hourInDay(XPathContext context, String dateTime) { dateTime = nn(dateTime); try { ConversionResult cr = CalendarValue.makeCalendarValue(dateTime, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return Double.NaN; } if (!(cr instanceof DateTimeValue || cr instanceof TimeValue)) { return Double.NaN; } AtomicValue hour = ((CalendarValue)cr).getComponent(Component.HOURS); return ((NumericValue)hour).getDoubleValue(); } catch (XPathException e) { return Double.NaN; } } /** * Return the current hour of the day * @param context the XPath dynamic context * @return the hour */ public static double hourInDay(XPathContext context) throws XPathException { return hourInDay(context, dateTime(context)); } /** * Return the minute of the hour in the specified date or date/time * @param dateTime must start with CCYY-MM-DDThh:mm:ss or hh:mm:ss * @return the minute */ public static double minuteInHour(XPathContext context, String dateTime) { dateTime = nn(dateTime); try { ConversionResult cr = CalendarValue.makeCalendarValue(dateTime, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return Double.NaN; } if (!(cr instanceof DateTimeValue || cr instanceof TimeValue)) { return Double.NaN; } AtomicValue minute = ((CalendarValue)cr).getComponent(Component.MINUTES); return ((NumericValue)minute).getDoubleValue(); } catch (XPathException e) { return Double.NaN; } } /** * Return the current minute of the hour * @param context the XPath dynamic context * @return the minute */ public static double minuteInHour(XPathContext context) throws XPathException { return minuteInHour(context, dateTime(context)); } /** * Return the second of the minute in the specified date or date/time * @param dateTime must start with CCYY-MM-DDThh:mm:ss or hh:mm:ss * @return the second */ public static double secondInMinute(XPathContext context, String dateTime) throws XPathException { dateTime = nn(dateTime); try { ConversionResult cr = CalendarValue.makeCalendarValue(dateTime, context.getConfiguration().getConversionRules()); if (cr instanceof ValidationFailure) { return Double.NaN; } if (!(cr instanceof DateTimeValue || cr instanceof TimeValue)) { return Double.NaN; } AtomicValue second = ((CalendarValue)cr).getComponent(Component.SECONDS); return ((NumericValue)second).getDoubleValue(); } catch (XPathException e) { return Double.NaN; } } /** * Return the current second of the minute * @param context the XPath dynamic context * @return the second */ public static double secondInMinute(XPathContext context) throws XPathException { return secondInMinute(context, dateTime(context)); } /** * The date:add function returns the date/time resulting from adding a duration to a date/time. * The first argument must be right-truncated date/time strings in one of the formats defined in * [XML Schema Part 2: Datatypes]. * 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) * The second argument is a string in the format defined for xs:duration in [3.2.6 duration] of * [XML Schema Part 2: Datatypes]. * The return value is a right-truncated date/time strings in one of the formats defined in * [XML Schema Part 2: Datatypes] and listed above. * This value is calculated using the algorithm described in * [Appendix E Adding durations to dateTimes] of [XML Schema Part 2: Datatypes]. * @throws XPathException */ public static String add(XPathContext context, String datetimeIn, String durationIn) throws XPathException { datetimeIn = nn(datetimeIn); durationIn = nn(durationIn); ConversionResult cr0 = CalendarValue.makeCalendarValue(datetimeIn, context.getConfiguration().getConversionRules()); if (cr0 instanceof ValidationFailure) { return ""; } CalendarValue cv0 = (CalendarValue)cr0; if (specificity(cv0) < 0) { return ""; } DateTimeValue v0 = cv0.toDateTime(); ConversionResult cr1 = DurationValue.makeDuration(durationIn); if (cr1 instanceof ValidationFailure) { return ""; } //final ConversionRules rules = context.getConfiguration().getConversionRules(); DurationValue v1 = (DurationValue)cr1; YearMonthDurationValue v1m = (YearMonthDurationValue) Converter.DURATION_TO_YEAR_MONTH_DURATION.convert(v1); //v1.convertPrimitive(BuiltInAtomicType.YEAR_MONTH_DURATION, false, rules); DayTimeDurationValue v1s = (DayTimeDurationValue) Converter.DURATION_TO_DAY_TIME_DURATION.convert(v1); //v1.convertPrimitive(BuiltInAtomicType.DAY_TIME_DURATION, false, rules); DateTimeValue sum = (DateTimeValue)v0.add(v1m).add(v1s); return Converter.convert(sum, cv0.getPrimitiveType(), context.getConfiguration().getConversionRules()).getStringValue(); } /** * The date:sum function adds a set of durations together. * The string values of the nodes in the node set passed as an argument * are interpreted as durations and added together as if using the date:add-duration * function. The string values of the nodes in the node set passed as the argument * must be in the format defined for xs:duration in [3.2.6 duration] of * [XML Schema Part 2: Datatypes]. If any of the string values of these nodes * are not in this format, or if the node set is empty, the function returns an * empty string (''). The result is a string in the format defined for xs:duration * in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. * The durations can be summed by summing the numbers given for each of the components in the durations. */ public static String sum(XPathContext context, SequenceIterator nodes) throws XPathException { DurationValue tot = (DurationValue)DurationValue.makeDuration("PT0S"); while (true) { Item it = nodes.next(); if (it == null) { break; } ConversionResult cr = DurationValue.makeDuration(it.getStringValueCS()); if (cr instanceof ValidationFailure) { return ""; } tot = addDurationValues(tot, (DurationValue)cr, context); if (tot == null) { return ""; } } return tot.getStringValue(); } /** * The date:add-duration function returns the duration resulting from adding two durations together. * Both arguments are strings in the format defined for xs:duration in [3.2.6 duration] of * [XML Schema Part 2: Datatypes]. If either argument is not in this format, the function returns * an empty string (''). The return value is a string in the format defined for xs:duration in * [3.2.6 duration] of [XML Schema Part 2: Datatypes]. The durations can usually be added by * summing the numbers given for each of the components in the durations. However, if the durations * are differently signed, then this sometimes results in durations that are impossible to express * in this syntax (e.g. 'P1M' + '-P1D'). In these cases, the function returns an empty string (''). */ public String addDuration(XPathContext context, String duration0, String duration1) { duration0 = nn(duration0); duration1 = nn(duration1); ConversionResult dv0 = DurationValue.makeDuration(duration0); ConversionResult dv1 = DurationValue.makeDuration(duration1); if (dv0 instanceof ValidationFailure || dv1 instanceof ValidationFailure) { return ""; } DurationValue result = addDurationValues((DurationValue)dv0, (DurationValue)dv1, context); return (result==null ? "" : result.getStringValue()); } /** * Add two duration values, according to the rules for the add-duration() and sum() functions * @param dv0 the first duration * @param dv1 the second duration * @param context the dynamic evaluation context * @return the sum of the two durations, or null if they are not summable (e.g. P1Y + -P1D) */ /*@Nullable*/ private static DurationValue addDurationValues(DurationValue dv0, DurationValue dv1, XPathContext context) { final ConversionRules rules = context.getConfiguration().getConversionRules(); YearMonthDurationValue dv0m = (YearMonthDurationValue) Converter.DURATION_TO_YEAR_MONTH_DURATION.convert((DurationValue)dv0); //((DurationValue)dv0).convertPrimitive(BuiltInAtomicType.YEAR_MONTH_DURATION, false, rules); DayTimeDurationValue dv0s = (DayTimeDurationValue) Converter.DURATION_TO_DAY_TIME_DURATION.convert((DurationValue)dv0); //((DurationValue)dv0).convertPrimitive(BuiltInAtomicType.DAY_TIME_DURATION, false, rules); YearMonthDurationValue dv1m = (YearMonthDurationValue) Converter.DURATION_TO_YEAR_MONTH_DURATION.convert((DurationValue)dv1); //((DurationValue)dv1).convertPrimitive(BuiltInAtomicType.YEAR_MONTH_DURATION, false, rules); DayTimeDurationValue dv1s = (DayTimeDurationValue) Converter.DURATION_TO_DAY_TIME_DURATION.convert((DurationValue)dv1); //((DurationValue)dv1).convertPrimitive(BuiltInAtomicType.DAY_TIME_DURATION, false, rules); int months = dv0m.getLengthInMonths() + dv1m.getLengthInMonths(); long micros = dv0s.getLengthInMicroseconds() + dv1s.getLengthInMicroseconds(); if (Integer.signum(months) * Long.signum(micros) < 0) { return null; } boolean positive = months >= 0 && micros >= 0; if (!positive) { months = -months; micros = -micros; } return new DurationValue(positive, 0, months, 0, 0, 0, (int)(micros/1000000), (int)(micros%1000000), BuiltInAtomicType.DURATION); } /** * The date:difference function returns the duration between the first date and the second date. * If the first date occurs before the second date, then the result is a positive duration; * if it occurs after the second date, the result is a negative duration. * The two dates must both be right-truncated date/time strings in one of the formats defined in * [XML Schema Part 2: Datatypes]. * The date/time with the most specific format (i.e. the least truncation) is converted into the * same format as the date with the most specific format (i.e. the most truncation). * The permitted formats are as follows, from most specific to least specific: * xs:dateTime (CCYY-MM-DDThh:mm:ss) * xs:date (CCYY-MM-DD) * xs:gYearMonth (CCYY-MM) * xs:gYear (CCYY) *

* If either of the arguments is not in one of these formats, date:difference * returns the empty string (''). * The difference between the date/times is returned as a string in the format defined for * xs:duration in [3.2.6 duration] of [XML Schema Part 2: Datatypes]. * If the date/time string with the least specific format is in either xs:gYearMonth or xs:gYear format, * then the number of days, hours, minutes and seconds in the duration string must be equal to zero. * (The format of the string will be PnYnM.) * The number of months specified in the duration must be less than 12. * Otherwise, the number of years and months in the duration string must be equal to zero. * (The format of the string will be PnDTnHnMnS.) * The number of seconds specified in the duration string must be less than 60; * the number of minutes must be less than 60; the number of hours must be less than 24. * @throws XPathException */ public static String difference(XPathContext context, String dateLeftIn, String dateRightIn) throws XPathException { dateLeftIn = nn(dateLeftIn); dateRightIn = nn(dateRightIn); final ConversionRules rules = context.getConfiguration().getConversionRules(); ConversionResult op0 = CalendarValue.makeCalendarValue(dateLeftIn, rules); ConversionResult op1 = CalendarValue.makeCalendarValue(dateRightIn, rules); if (op0 instanceof ValidationFailure || op1 instanceof ValidationFailure) { return ""; } CalendarValue v0 = (CalendarValue)op0; CalendarValue v1 = (CalendarValue)op1; int s0 = specificity(v0); int s1 = specificity(v1); if (s0 < 0 || s1 < 0) { return ""; } if (s0 < s1) { v1 = (CalendarValue)Converter.convert(v1, v0.getPrimitiveType(), rules); } else if (s1 < s0) { v0 = (CalendarValue)Converter.convert(v0, v1.getPrimitiveType(), rules); } if (v0 instanceof GYearValue) { int y0 = ((GYearValue)v0).getYear(); int y1 = ((GYearValue)v1).getYear(); return YearMonthDurationValue.fromMonths(12*(y1-y0)).getStringValue(); } else if (v0 instanceof GYearMonthValue) { int y0 = ((GYearMonthValue)v0).getYear(); int y1 = ((GYearMonthValue)v1).getYear(); int m0 = ((GYearMonthValue)v0).getMonth(); int m1 = ((GYearMonthValue)v1).getMonth(); return YearMonthDurationValue.fromMonths(12*(y1-y0)+(m1-m0)).getStringValue(); } else { DateTimeValue dt0 = v0.toDateTime(); DateTimeValue dt1 = v1.toDateTime(); return dt1.subtract(dt0, context).getStringValue(); } } /** * Rank the right-truncated calendar types in order of specificity * @param val a value of a given calendar type * @return the specificity of the type: 0 for least specific, 4 for most specific, -1 if this * is not a right-truncated type */ private static int specificity(CalendarValue val) { if (val instanceof GYearValue) { return 0; } else if (val instanceof GYearMonthValue) { return 1; } else if (val instanceof DateValue) { return 2; } else if (val instanceof DateTimeValue) { return 3; } else { return -1; } } /** * The date:duration function returns a duration string representing the number of seconds * specified by the argument string. If no argument is given, then the result of calling * date:seconds without any arguments is used as a default argument. The duration is returned * as a string in the format defined for xs:duration in [3.2.6 duration] of * [XML Schema Part 2: Datatypes]. The number of years and months in the duration string * must be equal to zero. (The format of the string will be PnDTnHnMnS.) * The number of seconds specified in the duration string must be less than 60; * the number of minutes must be less than 60; the number of hours must be less than 24. * If the argument is Infinity, -Infinity or NaN, then date:duration returns an empty string (''). */ public static String duration(double seconds) { DayTimeDurationValue v = DayTimeDurationValue.fromSeconds(new BigDecimal(seconds)); return v.getStringValue(); } /** * Return the number of seconds since 1 Jan 1970 * @return the number of seconds since 1 Jan 1970 (the "epoch" according to Java and Unix) */ public static double seconds(XPathContext context) throws XPathException{ DateTimeValue now = context.getCurrentDateTime(); DurationValue diff = now.subtract(DateTimeValue.EPOCH, context); return diff.getLengthInSeconds(); } /** * The date:seconds function returns the number of seconds specified by the argument string. * If no argument is given, then the current local date/time, as returned by date:date-time * is used as a default argument. * The argument string may be in one of the following formats: * 1. A right-truncated date/time string in one of the formats defined in * [XML Schema Part 2: Datatypes]. * In these cases, the difference between the date/time string and 1970-01-01T00:00:00Z * is calculated as with date:difference and the result is converted to seconds with * date:seconds. * The legal formats are as follows: * xs:dateTime (CCYY-MM-DDThh:mm:ss) * xs:date (CCYY-MM-DD) * xs:gYearMonth (CCYY-MM) * xs:gYear (CCYY) * 2. A duration specified in days, hours, minutes and seconds in the format defined for * xs:duration in * [3.2.6 duration] * of * [XML Schema Part 2: Datatypes]. * The number of years and months in the duration string must both be equal to zero: * either P0Y0M120D or P120D are permitted, but P3M is not. * If the argument to date:seconds is defined as a duration, the number returned is the * result of converting the duration to seconds by assuming that * 1 day = 24 hours, 1 hour = 60 minutes and 1 minute = 60 seconds. * The permitted duration format is basically PnDTnHnMnS, although implementers should * consult * [XML Schema Part 2: Datatypes] * and * [ISO 8601] * for details. * If the argument is not in any of these formats, date:seconds returns NaN. * @throws XPathException */ public static double seconds(XPathContext context, String datetimeIn) throws XPathException { datetimeIn = nn(datetimeIn); ConversionResult cr = CalendarValue.makeCalendarValue(datetimeIn, context.getConfiguration().getConversionRules()); if (cr instanceof DateTimeValue || cr instanceof DateValue || cr instanceof GYearValue || cr instanceof GYearMonthValue) { DateTimeValue dateTime = ((CalendarValue)cr).toDateTime(); DayTimeDurationValue diff = dateTime.subtract(DateTimeValue.EPOCH, context); return diff.getLengthInSeconds(); } cr = DurationValue.makeDuration(datetimeIn); if (cr instanceof DurationValue) { DurationValue duration = ((DurationValue)cr); if (duration.getYears() != 0 || duration.getMonths() != 0) { return Double.NaN; } else { return duration.getLengthInSeconds(); } } else { return Double.NaN; } } /** * Treat null as zero-length string * @param in input string * @return zero-length string if input is null, else the input string unchanged. */ private static String nn(String in) { return (in == null ? "" : in); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy