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 java.time.LocalDate;
036import java.time.Month;
037import java.time.temporal.ChronoUnit;
038import java.time.temporal.TemporalAdjusters;
039
040import net.objectlab.kit.datecalc.common.CalculatorConstants;
041import net.objectlab.kit.datecalc.common.PeriodCountBasis;
042import net.objectlab.kit.datecalc.common.PeriodCountCalculator;
043
044/**
045 * Joda <code>LocalDatePeriod</code> based implementation of the
046 * {@link net.objectlab.kit.datecalc.common.PeriodCountCalculator}.
047 *
048 * @author Benoit Xhenseval
049 *
050 */
051public class LocalDatePeriodCountCalculator implements PeriodCountCalculator<LocalDate> {
052
053    @Override
054    public int dayDiff(final LocalDate start, final LocalDate end, final PeriodCountBasis basis) {
055        int diff;
056
057        switch (basis) {
058        case CONV_30_360:
059            diff = diffConv30v360(start, end);
060            break;
061
062        case CONV_360E_ISDA:
063            diff = diff360EIsda(start, end);
064            break;
065
066        case CONV_360E_ISMA:
067            diff = diff360EIsma(start, end);
068            break;
069        default:
070            diff = (int) ChronoUnit.DAYS.between(start, end);
071        }
072
073        return diff;
074    }
075
076    private int diff360EIsma(final LocalDate start, final LocalDate end) {
077        int dayStart = start.getDayOfMonth();
078        int dayEnd = end.getDayOfMonth();
079        if (dayEnd == CalculatorConstants.MONTH_31_DAYS) {
080            dayEnd = CalculatorConstants.MONTH_30_DAYS;
081        }
082        if (dayStart == CalculatorConstants.MONTH_31_DAYS) {
083            dayStart = CalculatorConstants.MONTH_30_DAYS;
084        }
085        return (end.getYear() - start.getYear()) * CalculatorConstants.YEAR_360 + (end.getMonthValue() - start.getMonthValue()) * CalculatorConstants.MONTH_30_DAYS + dayEnd - dayStart;
086    }
087
088    // See https://en.wikipedia.org/wiki/Day_count_convention#30E.2F360_ISDA
089    private int diff360EIsda(final LocalDate start, final LocalDate end) {
090        if (start.equals(end)) {
091            return 0;
092        }
093        int dayStart = start.getDayOfMonth();
094        int dayEnd = end.getDayOfMonth();
095        if (start.getMonth().length(start.isLeapYear()) == dayStart) {
096            dayStart = CalculatorConstants.MONTH_30_DAYS;
097        }
098        if (end.getMonth() != Month.FEBRUARY && end.getMonth().length(end.isLeapYear()) == dayEnd) {
099            dayEnd = CalculatorConstants.MONTH_30_DAYS;
100        }
101        return (end.getYear() - start.getYear()) * CalculatorConstants.YEAR_360 + (end.getMonthValue() - start.getMonthValue()) * CalculatorConstants.MONTH_30_DAYS + dayEnd - dayStart;
102    }
103
104    private int diffConv30v360(final LocalDate start, final LocalDate end) {
105        int dayStart = start.getDayOfMonth();
106        int dayEnd = end.getDayOfMonth();
107        if (dayEnd == CalculatorConstants.MONTH_31_DAYS && dayStart >= CalculatorConstants.MONTH_30_DAYS) {
108            dayEnd = CalculatorConstants.MONTH_30_DAYS;
109        }
110        if (dayStart == CalculatorConstants.MONTH_31_DAYS) {
111            dayStart = CalculatorConstants.MONTH_30_DAYS;
112        }
113        return (end.getYear() - start.getYear()) * CalculatorConstants.YEAR_360 + (end.getMonthValue() - start.getMonthValue()) * CalculatorConstants.MONTH_30_DAYS + dayEnd - dayStart;
114    }
115
116    // -----------------------------------------------------------------------
117    //
118    // ObjectLab, world leaders in the design and development of bespoke
119    // applications for the securities financing markets.
120    // www.ObjectLab.co.uk
121    //
122    // -----------------------------------------------------------------------
123
124    @Override
125    public double monthDiff(final LocalDate start, final LocalDate end, final PeriodCountBasis basis) {
126        return yearDiff(start, end, basis) * CalculatorConstants.MONTHS_IN_YEAR;
127    }
128
129    @Override
130    public double yearDiff(final LocalDate start, final LocalDate end, final PeriodCountBasis basis) {
131        double diff = 0.0;
132
133        switch (basis) {
134        case ACT_ACT:
135            final int startYear = start.getYear();
136            final int endYear = end.getYear();
137            if (startYear != endYear) {
138
139                final LocalDate endOfStartYear = start.with(TemporalAdjusters.lastDayOfYear());
140                final LocalDate startOfEndYear = end.withDayOfYear(1);
141
142                final long diff1 = ChronoUnit.DAYS.between(start, endOfStartYear);
143                final long diff2 = ChronoUnit.DAYS.between(startOfEndYear, end);
144                diff = (diff1 + 1.0) / start.lengthOfYear() + (endYear - startYear - 1.0) + (double) diff2 / (double) end.lengthOfYear();
145            }
146            break;
147
148        case CONV_30_360:
149        case CONV_360E_ISDA:
150        case CONV_360E_ISMA:
151        case ACT_360:
152            diff = dayDiff(start, end, basis) / CalculatorConstants.YEAR_360_0;
153            break;
154
155        case ACT_365:
156            diff = dayDiff(start, end, basis) / CalculatorConstants.YEAR_365_0;
157            break;
158
159        default:
160            throw new UnsupportedOperationException("Sorry ACT_UST is not supported");
161        }
162
163        return diff;
164    }
165}
166
167/*
168 * ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
169 *
170 * Based in London, we are world leaders in the design and development of
171 * bespoke applications for the securities financing markets.
172 *
173 * <a href="http://www.objectlab.co.uk/open">Click here to learn more about
174 * us</a> ___ _ _ _ _ _ / _ \| |__ (_) ___ ___| |_| | __ _| |__ | | | | '_ \| |/
175 * _ \/ __| __| | / _` | '_ \ | |_| | |_) | | __/ (__| |_| |__| (_| | |_) |
176 * \___/|_.__// |\___|\___|\__|_____\__,_|_.__/ |__/
177 *
178 * www.ObjectLab.co.uk
179 */