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.6 2005/11/16 15:58:40 taqua 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&rsquo;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    }