Source for org.jfree.date.SerialDateUtilities

   1: /* ========================================================================
   2:  * JCommon : a free general purpose class library for the Java(tm) platform
   3:  * ========================================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  * 
   7:  * Project Info:  http://www.jfree.org/jcommon/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ------------------------
  28:  * SerialDateUtilities.java
  29:  * ------------------------
  30:  * (C) Copyright 2001-2003, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: SerialDateUtilities.java,v 1.6 2005/11/16 15:58:40 taqua Exp $
  36:  *
  37:  * Changes (from 26-Oct-2001)
  38:  * --------------------------
  39:  * 26-Oct-2001 : Changed package to com.jrefinery.date.*;
  40:  * 08-Dec-2001 : Dropped isLeapYear() method (DG);
  41:  * 04-Mar-2002 : Renamed SerialDates.java --> SerialDateUtilities.java (DG);
  42:  * 25-Jun-2002 : Fixed a bug in the dayCountActual() method (DG);
  43:  * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  44:  *
  45:  */
  46: 
  47: package org.jfree.date;
  48: 
  49: import java.text.DateFormatSymbols;
  50: import java.util.Calendar;
  51: 
  52: /**
  53:  * A utility class that provides a number of useful methods (some static).
  54:  * Many of these are used in the implementation of the day-count convention
  55:  * classes.  I recognise some limitations in this implementation:
  56:  * <p>
  57:  * [1] some of the methods assume that the default Calendar is a
  58:  * GregorianCalendar (used mostly to determine leap years) - so the code
  59:  * won&rsquo;t work if some other Calendar is the default.  I'm not sure how
  60:  * to handle this properly?
  61:  * <p>
  62:  * [2] a whole bunch of static methods isn't very object-oriented - but I couldn't think of a good
  63:  * way to extend the Date and Calendar classes to add the functions I required,
  64:  * so static methods are doing the job for now.
  65:  *
  66:  * @author David Gilbert
  67:  */
  68: public class SerialDateUtilities {
  69: 
  70:     /** The default date format symbols. */
  71:     private DateFormatSymbols dateFormatSymbols;
  72: 
  73:     /** Strings representing the weekdays. */
  74:     private String[] weekdays;
  75: 
  76:     /** Strings representing the months. */
  77:     private String[] months;
  78: 
  79:     /**
  80:      * Creates a new utility class for the default locale.
  81:      */
  82:     public SerialDateUtilities() {
  83:         this.dateFormatSymbols = new DateFormatSymbols();
  84:         this.weekdays = this.dateFormatSymbols.getWeekdays();
  85:         this.months = this.dateFormatSymbols.getMonths();
  86:     }
  87: 
  88:     /**
  89:      * Returns an array of strings representing the days-of-the-week.
  90:      *
  91:      * @return an array of strings representing the days-of-the-week.
  92:      */
  93:     public String[] getWeekdays() {
  94:         return this.weekdays;
  95:     }
  96: 
  97:     /**
  98:      * Returns an array of strings representing the months.
  99:      *
 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: }