001/*
002 * ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
003 *
004 * Based in London, we are world leaders in the design and development
005 * of bespoke applications for the securities financing markets.
006 *
007 * <a href="http://www.objectlab.co.uk/open">Click here to learn more</a>
008 *           ___  _     _           _   _          _
009 *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
010 *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
011 *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
012 *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
013 *                   |__/
014 *
015 *                     www.ObjectLab.co.uk
016 *
017 * $Id$
018 *
019 * Copyright 2006 the original author or authors.
020 *
021 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
022 * use this file except in compliance with the License. You may obtain a copy of
023 * the License at
024 *
025 * http://www.apache.org/licenses/LICENSE-2.0
026 *
027 * Unless required by applicable law or agreed to in writing, software
028 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
029 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
030 * License for the specific language governing permissions and limitations under
031 * the License.
032 */
033package net.objectlab.kit.datecalc.jdk8;
034
035import static net.objectlab.kit.datecalc.common.IMMPeriod.QUARTERLY;
036
037import java.time.DayOfWeek;
038import java.time.LocalDate;
039import java.time.Month;
040import java.time.temporal.TemporalAdjusters;
041import java.util.ArrayList;
042import java.util.List;
043
044import net.objectlab.kit.datecalc.common.AbstractIMMDateCalculator;
045import net.objectlab.kit.datecalc.common.IMMPeriod;
046
047/**
048 * Joda <code>LocalDate</code> based implementation of the
049 * {@link net.objectlab.kit.datecalc.common.IMMDateCalculator}.
050 *
051 * @author Benoit Xhenseval
052 *
053 */
054public class LocalDateIMMDateCalculator extends AbstractIMMDateCalculator<LocalDate> {
055    /**
056     * Returns a list of IMM dates between 2 dates, it will exclude the start
057     * date if it is an IMM date but would include the end date if it is an IMM.
058     *
059     * @param start
060     *            start of the interval, excluded
061     * @param end
062     *            end of the interval, may be included.
063     * @param period
064     *            specify when the "next" IMM is, if quarterly then it is the
065     *            conventional algorithm.
066     * @return list of IMM dates
067     */
068    @Override
069    public List<LocalDate> getIMMDates(final LocalDate start, final LocalDate end, final IMMPeriod period) {
070        final List<LocalDate> dates = new ArrayList<>();
071
072        LocalDate date = start;
073        while (true) {
074            date = getNextIMMDate(true, date, period);
075            if (!date.isAfter(end)) {
076                dates.add(date);
077            } else {
078                break;
079            }
080        }
081
082        return dates;
083    }
084
085    @Override
086    protected LocalDate getNextIMMDate(final boolean requestNextIMM, final LocalDate start, final IMMPeriod period) {
087        LocalDate date = start;
088
089        date = calculateIMMMonth(requestNextIMM, date, date.getMonth());
090
091        LocalDate imm = calculate3rdWednesday(date);
092        final Month immMonth = imm.getMonth();
093        final boolean isMarchSept = immMonth == Month.MARCH || immMonth == Month.SEPTEMBER;
094
095        switch (period) {
096
097        case BI_ANNUALY_JUN_DEC:
098            if (isMarchSept) {
099                imm = getNextIMMDate(requestNextIMM, imm, period);
100            }
101            break;
102
103        case BI_ANNUALY_MAR_SEP:
104            if (!isMarchSept) {
105                imm = getNextIMMDate(requestNextIMM, imm, period);
106            }
107            break;
108
109        case ANNUALLY:
110            // second jump
111            imm = getNextIMMDate(requestNextIMM, imm, QUARTERLY);
112            // third jump
113            imm = getNextIMMDate(requestNextIMM, imm, QUARTERLY);
114            // fourth jump
115            imm = getNextIMMDate(requestNextIMM, imm, QUARTERLY);
116            // fifth jump
117            imm = getNextIMMDate(requestNextIMM, imm, QUARTERLY);
118            break;
119
120        case QUARTERLY:
121        default:
122            break;
123        }
124
125        return imm;
126    }
127
128    // -----------------------------------------------------------------------
129    //
130    // ObjectLab, world leaders in the design and development of bespoke
131    // applications for the securities financing markets.
132    // www.ObjectLab.co.uk
133    //
134    // -----------------------------------------------------------------------
135
136    private LocalDate calculateIMMMonth(final boolean requestNextIMM, final LocalDate startDate, final Month month) {
137        int monthOffset;
138        LocalDate date = startDate;
139        switch (month) {
140        case MARCH:
141        case JUNE:
142        case SEPTEMBER:
143        case DECEMBER:
144            final LocalDate immDate = calculate3rdWednesday(date);
145            if (requestNextIMM && !date.isBefore(immDate)) {
146                date = date.plusMonths(MONTHS_IN_QUARTER);
147            } else if (!requestNextIMM && !date.isAfter(immDate)) {
148                date = date.minusMonths(MONTHS_IN_QUARTER);
149            }
150            break;
151
152        default:
153            if (requestNextIMM) {
154                monthOffset = (MONTH_IN_YEAR - month.getValue()) % MONTHS_IN_QUARTER;
155                date = date.plusMonths(monthOffset);
156            } else {
157                monthOffset = month.getValue() % MONTHS_IN_QUARTER;
158                date = date.minusMonths(monthOffset);
159            }
160            break;
161        }
162        return date;
163    }
164
165    /**
166     * Assumes that the month is correct, get the day for the 2rd wednesday.
167     *
168     * @param original
169     *            the start date
170     * @return the 3rd Wednesday of the month
171     */
172    private static LocalDate calculate3rdWednesday(final LocalDate original) {
173        return original.with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY)).plusWeeks(2);
174    }
175
176    /**
177     * Checks if a given date is an official IMM Date (3rd Wednesdays of
178     * March/June/Sept/Dec.
179     *
180     * @param date
181     * @return true if that date is an IMM date.
182     */
183    @Override
184    public boolean isIMMDate(final LocalDate date) {
185        boolean same = false;
186
187        final List<LocalDate> dates = getIMMDates(date.minusDays(1), date, QUARTERLY);
188
189        if (!dates.isEmpty()) {
190            same = date.equals(dates.get(0));
191        }
192
193        return same;
194    }
195}
196
197/*
198 * ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
199 *
200 * Based in London, we are world leaders in the design and development
201 * of bespoke applications for the securities financing markets.
202 *
203 * <a href="http://www.objectlab.co.uk/open">Click here to learn more about us</a>
204 *           ___  _     _           _   _          _
205 *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
206 *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
207 *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
208 *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
209 *                   |__/
210 *
211 *                     www.ObjectLab.co.uk
212 */