Frames | No Frames |
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’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: }