    001    /* ========================================================================
    002     * JCommon : a free general purpose class library for the Java(tm) platform
    003     * ========================================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jcommon/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ------------------------
    028     * SerialDateUtilities.java
    029     * ------------------------
    030     * (C) Copyright 2001-2003, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: SerialDateUtilities.java,v 1.5 2005/10/18 13:15:08 mungady Exp $
    036     *
    037     * Changes (from 26-Oct-2001)
    038     * --------------------------
    039     * 26-Oct-2001 : Changed package to com.jrefinery.date.*;
    040     * 08-Dec-2001 : Dropped isLeapYear() method (DG);
    041     * 04-Mar-2002 : Renamed SerialDates.java --> SerialDateUtilities.java (DG);
    042     * 25-Jun-2002 : Fixed a bug in the dayCountActual() method (DG);
    043     * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
    044     *
    045     */
    046
    047    package org.jfree.date;
    048
    049    import java.text.DateFormatSymbols;
    050    import java.util.Calendar;
    051
    052    /**
    053     * A utility class that provides a number of useful methods (some static).
    054     * Many of these are used in the implementation of the day-count convention
    055     * classes.  I recognise some limitations in this implementation:
    056     * <p>
    057     * [1] some of the methods assume that the default Calendar is a
    058     * GregorianCalendar (used mostly to determine leap years) - so the code
    059     * won’t work if some other Calendar is the default.  I'm not sure how
    060     * to handle this properly?
    061     * <p>
    062     * [2] a whole bunch of static methods isn't very object-oriented - but I couldn't think of a good
    063     * way to extend the Date and Calendar classes to add the functions I required,
    064     * so static methods are doing the job for now.
    065     *
    066     * @author David Gilbert
    067     */
    068    public class SerialDateUtilities {
    069
    070        /** The default date format symbols. */
    071        private DateFormatSymbols dateFormatSymbols;
    072
    073        /** Strings representing the weekdays. */
    074        private String[] weekdays;
    075
    076        /** Strings representing the months. */
    077        private String[] months;
    078
    079        /**
    080         * Creates a new utility class for the default locale.
    081         */
    082        public SerialDateUtilities() {
    083            this.dateFormatSymbols = new DateFormatSymbols();
    084            this.weekdays = this.dateFormatSymbols.getWeekdays();
    085            this.months = this.dateFormatSymbols.getMonths();
    086        }
    087
    088        /**
    089         * Returns an array of strings representing the days-of-the-week.
    090         *
    091         * @return an array of strings representing the days-of-the-week.
    092         */
    093        public String[] getWeekdays() {
    094            return this.weekdays;
    095        }
    096
    097        /**
    098         * Returns an array of strings representing the months.
    099         *
    100         * @return an array of strings representing the months.
    101         */
    102        public String[] getMonths() {
    103            return this.months;
    104        }
    105
    106        /**
    107         * Converts the specified string to a weekday, using the default locale.
    108         *
    109         * @param s  a string representing the day-of-the-week.
    110         *
    111         * @return an integer representing the day-of-the-week.
    112         */
    113        public int stringToWeekday(final String s) {
    114
    115            if (s.equals(this.weekdays[Calendar.SATURDAY])) {
    116                return SerialDate.SATURDAY;
    117            }
    118            else if (s.equals(this.weekdays[Calendar.SUNDAY])) {
    119                return SerialDate.SUNDAY;
    120            }
    121            else if (s.equals(this.weekdays[Calendar.MONDAY])) {
    122                return SerialDate.MONDAY;
    123            }
    124            else if (s.equals(this.weekdays[Calendar.TUESDAY])) {
    125                return SerialDate.TUESDAY;
    126            }
    127            else if (s.equals(this.weekdays[Calendar.WEDNESDAY])) {
    128                return SerialDate.WEDNESDAY;
    129            }
    130            else if (s.equals(this.weekdays[Calendar.THURSDAY])) {
    131                return SerialDate.THURSDAY;
    132            }
    133            else {
    134                return SerialDate.FRIDAY;
    135            }
    136
    137        }
    138
    139        /**
    140         * Returns the actual number of days between two dates.
    141         *
    142         * @param start  the start date.
    143         * @param end  the end date.
    144         *
    145         * @return the number of days between the start date and the end date.
    146         */
    147        public static int dayCountActual(final SerialDate start, final SerialDate end) {
    148            return end.compare(start);
    149        }
    150
    151        /**
    152         * Returns the number of days between the specified start and end dates,
    153         * assuming that there are thirty days in every month (that is,
    154         * corresponding to the 30/360 day-count convention).
    155         * <P>
    156         * The method handles cases where the start date is before the end date (by
    157         * switching the dates and returning a negative result).
    158         *
    159         * @param start  the start date.
    160         * @param end  the end date.
    161         *
    162         * @return the number of days between the two dates, assuming the 30/360 day-count convention.
    163         */
    164        public static int dayCount30(final SerialDate start, final SerialDate end) {
    165            final int d1;
    166            final int m1;
    167            final int y1;
    168            final int d2;
    169            final int m2;
    170            final int y2;
    171            if (start.isBefore(end)) {  // check the order of the dates
    172                d1 = start.getDayOfMonth();
    173                m1 = start.getMonth();
    174                y1 = start.getYYYY();
    175                d2 = end.getDayOfMonth();
    176                m2 = end.getMonth();
    177                y2 = end.getYYYY();
    178                return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1);
    179            }
    180            else {
    181                return -dayCount30(end, start);
    182            }
    183        }
    184
    185        /**
    186         * Returns the number of days between the specified start and end dates,
    187         * assuming that there are thirty days in every month, and applying the
    188         * ISDA adjustments (that is, corresponding to the 30/360 (ISDA) day-count
    189         * convention).
    190         * <P>
    191         * The method handles cases where the start date is before the end date (by
    192         * switching the dates around and returning a negative result).
    193         *
    194         * @param start  the start date.
    195         * @param end  the end date.
    196         *
    197         * @return The number of days between the two dates, assuming the 30/360
    198         *      (ISDA) day-count convention.
    199         */
    200        public static int dayCount30ISDA(final SerialDate start, final SerialDate end) {
    201            int d1;
    202            final int m1;
    203            final int y1;
    204            int d2;
    205            final int m2;
    206            final int y2;
    207            if (start.isBefore(end)) {
    208                d1 = start.getDayOfMonth();
    209                m1 = start.getMonth();
    210                y1 = start.getYYYY();
    211                if (d1 == 31) {  // first ISDA adjustment
    212                    d1 = 30;
    213                }
    214                d2 = end.getDayOfMonth();
    215                m2 = end.getMonth();
    216                y2 = end.getYYYY();
    217                if ((d2 == 31) && (d1 == 30)) {  // second ISDA adjustment
    218                    d2 = 30;
    219                }
    220                return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1);
    221            }
    222            else if (start.isAfter(end)) {
    223                return -dayCount30ISDA(end, start);
    224            }
    225            else {
    226                return 0;
    227            }
    228        }
    229
    230        /**
    231         * Returns the number of days between the specified start and end dates,
    232         * assuming that there are thirty days in every month, and applying the PSA
    233         * adjustments (that is, corresponding to the 30/360 (PSA) day-count convention).
    234         * The method handles cases where the start date is before the end date (by
    235         * switching the dates around and returning a negative result).
    236         *
    237         * @param start  the start date.
    238         * @param end  the end date.
    239         *
    240         * @return The number of days between the two dates, assuming the 30/360
    241         *      (PSA) day-count convention.
    242         */
    243        public static int dayCount30PSA(final SerialDate start, final SerialDate end) {
    244            int d1;
    245            final int m1;
    246            final int y1;
    247            int d2;
    248            final int m2;
    249            final int y2;
    250
    251            if (start.isOnOrBefore(end)) { // check the order of the dates
    252                d1 = start.getDayOfMonth();
    253                m1 = start.getMonth();
    254                y1 = start.getYYYY();
    255
    256                if (SerialDateUtilities.isLastDayOfFebruary(start)) {
    257                    d1 = 30;
    258                }
    259                if ((d1 == 31) || SerialDateUtilities.isLastDayOfFebruary(start)) {
    260                    // first PSA adjustment
    261                    d1 = 30;
    262                }
    263                d2 = end.getDayOfMonth();
    264                m2 = end.getMonth();
    265                y2 = end.getYYYY();
    266                if ((d2 == 31) && (d1 == 30)) {  // second PSA adjustment
    267                    d2 = 30;
    268                }
    269                return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1);
    270            }
    271            else {
    272                return -dayCount30PSA(end, start);
    273            }
    274        }
    275
    276        /**
    277         * Returns the number of days between the specified start and end dates,
    278         * assuming that there are thirty days in every month, and applying the
    279         * European adjustment (that is, corresponding to the 30E/360 day-count
    280         * convention).
    281         * <P>
    282         * The method handles cases where the start date is before the end date (by
    283         * switching the dates around and returning a negative result).
    284         *
    285         * @param start  the start date.
    286         * @param end  the end date.
    287         *
    288         * @return the number of days between the two dates, assuming the 30E/360
    289         *      day-count convention.
    290         */
    291        public static int dayCount30E(final SerialDate start, final SerialDate end) {
    292            int d1;
    293            final int m1;
    294            final int y1;
    295            int d2;
    296            final int m2;
    297            final int y2;
    298            if (start.isBefore(end)) {
    299                d1 = start.getDayOfMonth();
    300                m1 = start.getMonth();
    301                y1 = start.getYYYY();
    302                if (d1 == 31) {  // first European adjustment
    303                    d1 = 30;
    304                }
    305                d2 = end.getDayOfMonth();
    306                m2 = end.getMonth();
    307                y2 = end.getYYYY();
    308                if (d2 == 31) {  // first European adjustment
    309                    d2 = 30;
    310                }
    311                return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1);
    312            }
    313            else if (start.isAfter(end)) {
    314                return -dayCount30E(end, start);
    315            }
    316            else {
    317                return 0;
    318            }
    319        }
    320
    321        /**
    322         * Returns true if the specified date is the last day in February (that is, the
    323         * 28th in non-leap years, and the 29th in leap years).
    324         *
    325         * @param d  the date to be tested.
    326         *
    327         * @return a boolean that indicates whether or not the specified date is
    328         *      the last day of February.
    329         */
    330        public static boolean isLastDayOfFebruary(final SerialDate d) {
    331
    332            final int dom;
    333            if (d.getMonth() == MonthConstants.FEBRUARY) {
    334                dom = d.getDayOfMonth();
    335                if (SerialDate.isLeapYear(d.getYYYY())) {
    336                    return (dom == 29);
    337                }
    338                else {
    339                    return (dom == 28);
    340                }
    341            }
    342            else { // not even February
    343                return false;
    344            }
    345
    346        }
    347
    348        /**
    349         * Returns the number of times that February 29 falls within the specified
    350         * date range.  The result needs to correspond to the ACT/365 (Japanese)
    351         * day-count convention. The difficult cases are where the start or the
    352         * end date is Feb 29 (include or not?).  Need to find out how JGBs do this
    353         * (since this is where the ACT/365 (Japanese) convention comes from ...
    354         *
    355         * @param start  the start date.
    356         * @param end  the end date.
    357         *
    358         * @return the number of times that February 29 occurs within the date
    359         *      range.
    360         */
    361        public static int countFeb29s(final SerialDate start, final SerialDate end) {
    362            int count = 0;
    363            SerialDate feb29;
    364            final int y1;
    365            final int y2;
    366            int year;
    367
    368            // check the order of the dates
    369            if (start.isBefore(end)) {
    370
    371                y1 = start.getYYYY();
    372                y2 = end.getYYYY();
    373                for (year = y1; year == y2; year++) {
    374                    if (SerialDate.isLeapYear(year)) {
    375                        feb29 = SerialDate.createInstance(29, MonthConstants.FEBRUARY, year);
    376                        if (feb29.isInRange(start, end, SerialDate.INCLUDE_SECOND)) {
    377                            count++;
    378                        }
    379                    }
    380                }
    381                return count;
    382            }
    383            else {
    384                return countFeb29s(end, start);
    385            }
    386        }
    387
    388    }