package org.basex.query.func; import static org.basex.query.util.Err.*; import java.math.BigDecimal; import java.util.Calendar; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.item.DTd; import org.basex.query.item.Dat; import org.basex.query.item.Date; import org.basex.query.item.Dec; import org.basex.query.item.Dtm; import org.basex.query.item.Dur; import org.basex.query.item.Item; import org.basex.query.item.Int; import org.basex.query.item.AtomType; import org.basex.query.item.Tim; import org.basex.query.item.Type; import org.basex.query.util.Err; import org.basex.util.InputInfo; /** * Date functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class FNDate extends StandardFunc { /** * Constructor. * @param ii input info * @param f function definition * @param e arguments */ public FNDate(final InputInfo ii, final Function f, final Expr... e) { super(ii, f, e); } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { // functions have 1 or 2 arguments... final Item it = expr[0].item(ctx, input); if(it == null) return null; final boolean d = expr.length == 2; final Item zon = d ? expr[1].item(ctx, input) : null; switch(sig) { case YEARS_FROM_DURATION: return yea(checkDur(it)); case YEAR_FROM_DATETIME: return yea(checkDate(it, AtomType.DTM, ctx)); case YEAR_FROM_DATE: return yea(checkDate(it, AtomType.DAT, ctx)); case MONTHS_FROM_DURATION: return mon(checkDur(it)); case MONTH_FROM_DATETIME: return mon(checkDate(it, AtomType.DTM, ctx)); case MONTH_FROM_DATE: return mon(checkDate(it, AtomType.DAT, ctx)); case DAYS_FROM_DURATION: return day(checkDur(it)); case DAY_FROM_DATETIME: return day(checkDate(it, AtomType.DTM, ctx)); case DAY_FROM_DATE: return day(checkDate(it, AtomType.DAT, ctx)); case HOURS_FROM_DURATION: return hou(checkDur(it)); case HOURS_FROM_DATETIME: return hou(checkDate(it, AtomType.DTM, ctx)); case HOURS_FROM_TIME: return hou(checkDate(it, AtomType.TIM, ctx)); case MINUTES_FROM_DURATION: return min(checkDur(it)); case MINUTES_FROM_DATETIME: return min(checkDate(it, AtomType.DTM, ctx)); case MINUTES_FROM_TIME: return min(checkDate(it, AtomType.TIM, ctx)); case SECONDS_FROM_DURATION: return sec(checkDur(it)); case SECONDS_FROM_DATETIME: return sec(checkDate(it, AtomType.DTM, ctx)); case SECONDS_FROM_TIME: return sec(checkDate(it, AtomType.TIM, ctx)); case TIMEZONE_FROM_DATETIME: return zon(checkDate(it, AtomType.DTM, ctx)); case TIMEZONE_FROM_DATE: return zon(checkDate(it, AtomType.DAT, ctx)); case TIMEZONE_FROM_TIME: return zon(checkDate(it, AtomType.TIM, ctx)); case ADJUST_DATE_TO_TIMEZONE: return datzon(it, zon, d); case ADJUST_DATETIME_TO_TIMEZONE: return dtmzon(it, zon, d); case ADJUST_TIME_TO_TIMEZONE: return timzon(it, zon, d); case DATETIME: return dattim(it, zon); default: return super.item(ctx, ii); } } /** * Returns the years of the specified date. * @param it date * @return years */ private static Item yea(final Item it) { return Int.get(it instanceof Dur ? ((Dur) it).yea() : ((Date) it).xc.getYear()); } /** * Returns the months of the specified date. * @param it date * @return months */ private static Item mon(final Item it) { return Int.get(it instanceof Dur ? ((Dur) it).mon() : ((Date) it).xc.getMonth()); } /** * Returns the days of the specified date. * @param it date * @return days */ private static Item day(final Item it) { return Int.get(it instanceof Dur ? (int) ((Dur) it).day() : ((Date) it).xc.getDay()); } /** * Returns the hours of the specified date. * @param it date * @return hours */ private static Item hou(final Item it) { return Int.get(it instanceof Dur ? (int) ((Dur) it).hou() : ((Date) it).xc.getHour()); } /** * Returns the minutes of the specified date. * @param it date * @return minutes */ private static Item min(final Item it) { return Int.get(it instanceof Dur ? ((Dur) it).min() : ((Date) it).xc.getMinute()); } /** * Returns the seconds of the specified date. * @param it date * @return seconds */ private static Item sec(final Item it) { if(it instanceof Dur) return Dec.get(((Dur) it).sec().doubleValue()); final int s = ((Date) it).xc.getSecond(); final BigDecimal d = ((Date) it).xc.getFractionalSecond(); return Dec.get(s + (d != null ? d.doubleValue() : 0)); } /** * Returns the timezone. * @param it input item * @return timezone */ private static Item zon(final Item it) { final int z = ((Date) it).xc.getTimezone(); return z == Item.UNDEF ? null : new DTd(z); } /** * Checks if the specified item has the specified type. * If it's an untyped item, the specified type is returned. * @param it item to be checked * @param t target type * @param ctx query context * @return date * @throws QueryException query exception */ private Item checkDate(final Item it, final Type t, final QueryContext ctx) throws QueryException { return it.type.isUntyped() ? t.cast(it, ctx, input) : checkType(it, t); } /** * Checks if the specified item is a duration. If it's an untyped item, * a duration is returned. * @param it item to be checked * @return duration * @throws QueryException query exception */ private Item checkDur(final Item it) throws QueryException { final Type ip = it.type; if(ip.isUntyped()) return new Dur(it.string(input), input); if(!ip.isDuration()) Err.type(this, AtomType.DUR, it); return it; } /** * Adjusts the Date to the time zone. * @param it item to be checked * @param zon timezone * @param d zone was defined * @return duration * @throws QueryException query exception */ private Item datzon(final Item it, final Item zon, final boolean d) throws QueryException { final Item i = it.type.isUntyped() ? new Dat(it.string(input), input) : checkType(it, AtomType.DAT); return adjust((Date) i, zon, d); } /** * Adjusts the DateTime to the time zone. * @param it item to be checked * @param zon timezone * @param d zone was defined * @return duration * @throws QueryException query exception */ private Item dtmzon(final Item it, final Item zon, final boolean d) throws QueryException { final Item i = it.type.isUntyped() ? new Dtm(it.string(input), input) : checkType(it, AtomType.DTM); return adjust((Date) i, zon, d); } /** * Adjusts the Time to the time zone. * @param it item to be checked * @param zon timezone * @param d zone was defined * @return duration * @throws QueryException query exception */ private Item timzon(final Item it, final Item zon, final boolean d) throws QueryException { final Item i = it.type.isUntyped() ? new Tim(it.string(input), input) : checkType(it, AtomType.TIM); return adjust((Date) i, zon, d); } /** * Returns a DateTime. * @param date item to be checked * @param tm time zone * @return duration * @throws QueryException query exception */ private Item dattim(final Item date, final Item tm) throws QueryException { if(tm == null) return null; final Item d = date.type.isUntyped() ? new Dat(date.string(input), input) : date; final Item t = tm.type.isUntyped() ? new Tim(tm.string(input), input) : tm; final Dtm dtm = new Dtm((Dat) checkType(d, AtomType.DAT)); final Tim tim = (Tim) checkType(t, AtomType.TIM); dtm.xc.setTime(tim.xc.getHour(), tim.xc.getMinute(), tim.xc.getSecond(), tim.xc.getMillisecond()); final int zone = tim.xc.getTimezone(); if(dtm.xc.getTimezone() == Item.UNDEF) { dtm.xc.setTimezone(zone); } else if(dtm.xc.getTimezone() != zone && zone != Item.UNDEF) { FUNZONE.thrw(input, dtm, tim); } return dtm; } /** * Adjusts the timezone. * @param date input date * @param zon timezone * @param d zone was specified * @return adjusted date * @throws QueryException query exception */ private Date adjust(final Date date, final Item zon, final boolean d) throws QueryException { if(d && zon == null) { date.xc.setTimezone(Item.UNDEF); return date; } final int zn = date.xc.getTimezone(); final int tz; if(zon == null) { final Calendar c = Calendar.getInstance(); tz = (c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET)) / 60000; } else { final DTd dtd = (DTd) checkType(zon, AtomType.DTD); tz = (int) (dtd.min() + dtd.hou() * 60); if(dtd.sec().signum() != 0 || Math.abs(tz) > 840) { INVALZONE.thrw(input, zon); } } if(zn != Item.UNDEF) date.xc.add(Date.df.newDuration(-60000L * (zn - tz))); date.xc.setTimezone(tz); return date; } }