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’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 }