/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.types.mcompat.mfuncs; import com.foundationdb.server.error.InvalidDateFormatException; import com.foundationdb.server.error.InvalidParameterValueException; import com.foundationdb.server.types.LazyList; import com.foundationdb.server.types.TExecutionContext; import com.foundationdb.server.types.TScalar; import com.foundationdb.server.types.TOverloadResult; import com.foundationdb.server.types.mcompat.mtypes.MDateAndTime; import com.foundationdb.server.types.mcompat.mtypes.MDateAndTime.ZeroFlag; import com.foundationdb.server.types.mcompat.mtypes.MNumeric; import com.foundationdb.server.types.value.ValueSource; import com.foundationdb.server.types.value.ValueTarget; import com.foundationdb.server.types.texpressions.TInputSetBuilder; import com.foundationdb.server.types.texpressions.TScalarBase; import org.joda.time.DateTimeConstants; import org.joda.time.DateTimeZone; import org.joda.time.MutableDateTime; public abstract class MYearWeek extends TScalarBase { public static final TScalar INSTANCES[] = new TScalar[] { new MYearWeek("YEARWEEK") { @Override protected int getMode(TExecutionContext context, LazyList<? extends ValueSource> inputs) { return 0; } @Override protected void buildInputSets(TInputSetBuilder builder) { builder.covers(MDateAndTime.DATE, 0); } }, new MYearWeek("YEARWEEK") { @Override protected int getMode(TExecutionContext context, LazyList<? extends ValueSource> inputs) { int mode = (int) inputs.get(1).getInt64(); if (mode < 0 || mode > 7) { context.warnClient(new InvalidParameterValueException("MODE out of range [0, 7]: " + mode)); return -1; } return mode; } @Override protected void buildInputSets(TInputSetBuilder builder) { builder.covers(MDateAndTime.DATE, 0).covers(MNumeric.BIGINT, 1); } } }; protected abstract int getMode(TExecutionContext context, LazyList<? extends ValueSource> inputs); private final String name; private MYearWeek(String name) { this.name = name; } @Override protected void doEvaluate(TExecutionContext context, LazyList<? extends ValueSource> inputs, ValueTarget output) { int date = inputs.get(0).getInt32(); long ymd[] = MDateAndTime.decodeDate(date); int mode = getMode(context, inputs); if (!MDateAndTime.isValidDateTime(ymd, ZeroFlag.YEAR) || mode < 0) { context.warnClient(new InvalidDateFormatException("Invalid DATE value " , date + "")); output.putNull(); } else { output.putInt32(modes[(int) mode].getYearWeek(new MutableDateTime(DateTimeZone.forID(context.getCurrentTimezone())), (int)ymd[0], (int)ymd[1], (int)ymd[2])); } } @Override public String displayName() { return name; } @Override public TOverloadResult resultType() { return TOverloadResult.fixed(MNumeric.INT); } //------------------ static helpers----------------------------------------- private static interface Modes { int getYearWeek(MutableDateTime cal, int yr, int mo, int da); } private static final Modes[] modes = new Modes[] { new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode0257(cal, yr, mo, da, DateTimeConstants.SUNDAY, 0);}}, //0 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode1346(cal, yr, mo, da, DateTimeConstants.SUNDAY,1);}}, //1 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode0257(cal, yr, mo, da, DateTimeConstants.SUNDAY, 0);}}, //2 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode1346(cal, yr, mo, da, DateTimeConstants.SUNDAY, 1);}}, //3 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode1346(cal, yr, mo, da, DateTimeConstants.SATURDAY,4);}},//4 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode0257(cal, yr, mo, da, DateTimeConstants.MONDAY, 5);}}, //5 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode1346(cal, yr, mo, da, DateTimeConstants.SATURDAY,4);}},//6 new Modes() {public int getYearWeek(MutableDateTime cal, int yr, int mo, int da){return getMode0257(cal, yr, mo, da, DateTimeConstants.MONDAY,5);}}, //7 }; private static int getMode1346(MutableDateTime cal, int yr, int mo, int da, int firstDay, int lowestVal) { cal.setYear(yr); cal.setMonthOfYear(1); cal.setDayOfMonth(1); int firstD = 1; while (cal.getDayOfWeek() != firstDay) cal.setDayOfMonth(++firstD); cal.setYear(yr); cal.setMonthOfYear(mo); cal.setDayOfMonth(da); int week = cal.getDayOfYear() - (firstD +1 ); // Sun/Mon if (firstD < 4) { if (week < 0) return modes[lowestVal].getYearWeek(cal, yr - 1, 12, 31); else return yr * 100 + week / 7 + 1; } else { if (week < 0) return yr * 100 + 1; else return yr * 100 + week / 7 + 2; } } private static int getMode0257(MutableDateTime cal, int yr, int mo, int da, int firstDay, int lowestVal) { cal.setYear(yr); cal.setMonthOfYear(1); cal.setDayOfMonth(1); int firstD = 1; while (cal.getDayOfWeek() != firstDay) cal.setDayOfMonth(++firstD); cal.setYear(yr); cal.setMonthOfYear(mo); cal.setDayOfMonth(da); int dayOfYear = cal.getDayOfYear(); if (dayOfYear < firstD) return modes[lowestVal].getYearWeek(cal, yr - 1, 12, 31); else return yr * 100 + (dayOfYear - firstD) / 7 +1; } }