【Code】《代码整洁之道》笔记-附录B-org.jfree.date.SerialDate

附录B org.jfree.date.SerialDate

代码清单B-1 SerialDate.Java

   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  * 
   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  * SerialDate.java
  29  * ---------------
  30  * (C) Copyright 2001-2005, by Object Refinery Limited.
  31  *
  32  * Original Author:  David Gilbert (for Object Refinery Limited);
  33  * Contributor(s):   -;
  34  *
  35  * $Id: SerialDate.java,v 1.7 2005/11/03 09:25:17 mungady Exp $
  36  *
  37  * Changes (from 11-Oct-2001)
  38  * --------------------------
  39  * 11-Oct-2001 : Re-organised the class and moved it to new package 
  40  *               com.jrefinery.date (DG);
  41  * 05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate 
  42  *               class (DG);
  43  * 12-Nov-2001 : IBD requires setDescription() method, now that NotableDate 
  44  *               class is gone (DG);  Changed getPreviousDayOfWeek(), 
  45  *               getFollowingDayOfWeek() and getNearestDayOfWeek() to correct 
  46  *               bugs (DG);
  47  * 05-Dec-2001 : Fixed bug in SpreadsheetDate class (DG);
  48  * 29-May-2002 : Moved the month constants into a separate interface 
  49  *               (MonthConstants) (DG);
  50  * 27-Aug-2002 : Fixed bug in addMonths() method, thanks to N???levka Petr (DG);
  51  * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  52  * 13-Mar-2003 : Implemented Serializable (DG);
  53  * 29-May-2003 : Fixed bug in addMonths method (DG);
  54  * 04-Sep-2003 : Implemented Comparable.  Updated the isInRange javadocs (DG);
  55  * 05-Jan-2005 : Fixed bug in addYears() method (1096282) (DG);
  56  * 
  57  */
  58 
  59 package org.jfree.date;
  60 
  61 import java.io.Serializable;
  62 import java.text.DateFormatSymbols;
  63 import java.text.SimpleDateFormat;
  64 import java.util.Calendar;
  65 import java.util.GregorianCalendar;
  66 
  67 /**
  68  * An abstract class that defines our requirements for manipulating dates,
  69  * without tying down a particular implementation.
  70  * <P>
  71  * Requirement 1 : match at least what Excel does for dates;
  72  * Requirement 2 : class is immutable;
  73  * <P>
  74  * Why not just use java.util.Date?  We will, when it makes sense.  At times,
  75  * java.util.Date can be *too* precise - it represents an instant in time,
  76  * accurate to 1/1000th of a second (with the date itself depending on the
  77  * time-zone).  Sometimes we just want to represent a particular day (e.g. 21
  78  * January 2015) without concerning ourselves about the time of day, or the
  79  * time-zone, or anything else.  That's what we've defined SerialDate for.
  80  * <P>
  81  * You can call getInstance() to get a concrete subclass of SerialDate,
  82  * without worrying about the exact implementation.
  83  *
  84  * @author David Gilbert
  85  */
  86 public abstract class SerialDate implements Comparable, 
  87                                             Serializable, 
  88                                             MonthConstants {
  89 
  90   /** For serialization. */
  91   private static final long serialVersionUID = -293716040467423637L;
  92
  93   /** Date format symbols. */
  94   public static final DateFormatSymbols
  95       DATE_FORMAT_SYMBOLS = new SimpleDateFormat().getDateFormatSymbols();
  96 
  97   /** The serial number for 1 January 1900. */
  98   public static final int SERIAL_LOWER_BOUND = 2;
  99 
 100   /** The serial number for 31 December 9999. */
 101   public static final int SERIAL_UPPER_BOUND = 2958465;
 102 
 103   /** The lowest year value supported by this date format. */
 104   public static final int MINIMUM_YEAR_SUPPORTED = 1900;
 105 
 106   /** The highest year value supported by this date format. */
 107   public static final int MAXIMUM_YEAR_SUPPORTED = 9999;
 108 
 109   /** Useful constant for Monday. Equivalent to java.util.Calendar.MONDAY. */
 110   public static final int MONDAY = Calendar.MONDAY;
 111 
 112   /** 
 113    * Useful constant for Tuesday. Equivalent to java.util.Calendar.TUESDAY. 
 114    */
 115   public static final int TUESDAY = Calendar.TUESDAY;
 116 
 117   /** 
 118    * Useful constant for Wednesday. Equivalent to 
 119    * java.util.Calendar.WEDNESDAY. 
 120    */
 121   public static final int WEDNESDAY = Calendar.WEDNESDAY;
 122 
 123   /** 
 124    * Useful constant for Thrusday. Equivalent to java.util.Calendar.THURSDAY. 
 125    */
 126   public static final int THURSDAY = Calendar.THURSDAY;
 127 
 128   /** Useful constant for Friday. Equivalent to java.util.Calendar.FRIDAY. */
 129   public static final int FRIDAY = Calendar.FRIDAY;
 130 
 131   /** 
 132    * Useful constant for Saturday. Equivalent to java.util.Calendar.SATURDAY.
 133    */
 134   public static final int SATURDAY = Calendar.SATURDAY;
 135 
 136   /** Useful constant for Sunday. Equivalent to java.util.Calendar.SUNDAY. */
 137   public static final int SUNDAY = Calendar.SUNDAY;
 138 
 139   /** The number of days in each month in non leap years. */
 140   static final int[] LAST_DAY_OF_MONTH =
 141       {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 142 
 143   /** The number of days in a (non-leap) year up to the end of each month. */
 144   static final int[] AGGREGATE_DAYS_TO_END_OF_MONTH =
 145       {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
 146 
 147   /** The number of days in a year up to the end of the preceding month. */
 148   static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
 149       {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
 150 
 151   /** The number of days in a leap year up to the end of each month. */
 152   static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH =
 153       {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
 154 
 155   /** 
 156    * The number of days in a leap year up to the end of the preceding month. 
 157    */
 158   static final int[] 
 159       LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
 160           {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
 161 
 162   /** A useful constant for referring to the first week in a month. */
 163   public static final int FIRST_WEEK_IN_MONTH = 1;
 164 
 165   /** A useful constant for referring to the second week in a month. */
 166   public static final int SECOND_WEEK_IN_MONTH = 2;
 167 
 168   /** A useful constant for referring to the third week in a month. */
 169   public static final int THIRD_WEEK_IN_MONTH = 3;
 170 
 171   /** A useful constant for referring to the fourth week in a month. */
 172   public static final int FOURTH_WEEK_IN_MONTH = 4;
 173 
 174   /** A useful constant for referring to the last week in a month. */
 175   public static final int LAST_WEEK_IN_MONTH = 0;
 176 
 177   /** Useful range constant. */
 178   public static final int INCLUDE_NONE = 0;
 179 
 180   /** Useful range constant. */
 181   public static final int INCLUDE_FIRST = 1;
 182 
 183   /** Useful range constant. */
 184   public static final int INCLUDE_SECOND = 2;
 185 
 186   /** Useful range constant. */
 187   public static final int INCLUDE_BOTH = 3;
 188 
 189   /** 
 190    * Useful constant for specifying a day of the week relative to a fixed 
 191    * date. 
 192    */
 193   public static final int PRECEDING = -1;
 194 
 195   /** 
 196    * Useful constant for specifying a day of the week relative to a fixed 
 197    * date. 
 198    */
 199   public static final int NEAREST = 0;
 200 
 201   /** 
 202    * Useful constant for specifying a day of the week relative to a fixed 
 203    * date. 
 204    */
 205   public static final int FOLLOWING = 1;
 206 
 207   /** A description for the date. */
 208   private String description;
 209 
 210   /**
 211    * Default constructor.
 212    */
 213   protected SerialDate() {
 214   }
 215 
 216   /**
 217    * Returns <code>true</code> if the supplied integer code represents a 
 218    * valid day-of-the-week, and <code>false</code> otherwise.
 219    *
 220    * @param code  the code being checked for validity.
 221    *
 222    * @return <code>true</code> if the supplied integer code represents a 
 223    *         valid day-of-the-week, and <code>false</code> otherwise.
 224    */
 225   public static boolean isValidWeekdayCode(final int code) {
 226 
 227     switch(code) {
 228       case SUNDAY: 
 229       case MONDAY: 
 230       case TUESDAY: 
 231       case WEDNESDAY: 
 232       case THURSDAY: 
 233       case FRIDAY: 
 234       case SATURDAY: 
 235         return true;
 236       default: 
 237         return false;
 238     }
 239 
 240   }
 241 
 242   /**
 243    * Converts the supplied string to a day of the week.
 244    *
 245    * @param s  a string representing the day of the week.
 246    *
 247    * @return <code>-1</code> if the string is not convertable, the day of 
 248    *         the week otherwise.
 249    */
 250   public static int stringToWeekdayCode(String s) {
 251 
 252     final String[] shortWeekdayNames 
 253         = DATE_FORMAT_SYMBOLS.getShortWeekdays();
 254     final String[] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays();
 255 
 256     int result = -1;
 257     s = s.trim();
 258     for (int i = 0; i < weekDayNames.length; i++) {
 259       if (s.equals(shortWeekdayNames[i])) {
 260         result = i;
 261         break;
 262       }
 263       if (s.equals(weekDayNames[i])) {
 264         result = i;
 265         break;
 266       }
 267     }
 268     return result;
 269 
 270   }
 271 
 272   /**
 273    * Returns a string representing the supplied day-of-the-week.
 274    * <P>
 275    * Need to find a better approach.
 276    *
 277    * @param weekday  the day of the week.
 278    *
 279    * @return a string representing the supplied day-of-the-week.
 280    */
 281   public static String weekdayCodeToString(final int weekday) {
 282 
 283       final String[] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays();
 284       return weekdays[weekday];
 285 
 286   }
 287 
 288   /**
 289    * Returns an array of month names.
 290    *
 291    * @return an array of month names.
 292    */
 293   public static String[] getMonths() {
 294 
 295     return getMonths(false);
 296 
 297   }
 298 
 299   /**
 300    * Returns an array of month names.
 301    *
 302    * @param shortened  a flag indicating that shortened month names should 
 303    *                   be returned.
 304    *
 305    * @return an array of month names.
 306    */
 307   public static String[] getMonths(final boolean shortened) {
 308 
 309     if (shortened) {
 310       return DATE_FORMAT_SYMBOLS.getShortMonths();
 311     }
 312     else {
 313       return DATE_FORMAT_SYMBOLS.getMonths();
 314     }
 315 
 316   }
 317 
 318   /**
 319    * Returns true if the supplied integer code represents a valid month.
 320    *
 321    * @param code  the code being checked for validity.
 322    *
 323    * @return <code>true</code> if the supplied integer code represents a valid month.
 324    *         
 325    */
 326   public static boolean isValidMonthCode(final int code) {
 327 
 328     switch(code) {
 329       case JANUARY: 
 330       case FEBRUARY: 
 331       case MARCH: 
 332       case APRIL: 
 333       case MAY: 
 334       case JUNE: 
 335       case JULY: 
 336       case AUGUST: 
 337       case SEPTEMBER: 
 338       case OCTOBER: 
 339       case NOVEMBER: 
 340       case DECEMBER: 
 341         return true;
 342       default: 
 343         return false;
 344     }
 345 
 346   }
 347 
 348   /**
 349    * Returns the quarter for the specified month.
 350    *
 351    * @param code  the month code (1-12).
 352    *
 353    * @return the quarter that the month belongs to.
 354    * @throws java.lang.IllegalArgumentException
 355    */
 356   public static int monthCodeToQuarter(final int code) {
 357 
 358     switch(code) {
 359       case JANUARY: 
 360       case FEBRUARY: 
 361       case MARCH: return 1;
 362       case APRIL: 
 363       case MAY: 
 364       case JUNE: return 2;
 365       case JULY: 
 366       case AUGUST: 
 367       case SEPTEMBER: return 3;
 368       case OCTOBER: 
 369       case NOVEMBER: 
 370       case DECEMBER: return 4;
 371       default: throw new IllegalArgumentException(
 372         "SerialDate.monthCodeToQuarter: invalid month code.");
 373     }
 374 
 375   }
 376 
 377   /**
 378    * Returns a string representing the supplied month.
 379    * <P>
 380    * The string returned is the long form of the month name taken from the 
 381    * default locale.
 382    *
 383    * @param month  the month.
 384    *
 385    * @return a string representing the supplied month.
 386    */
 387   public static String monthCodeToString(final int month) {
 388 
 389     return monthCodeToString(month, false);
 390 
 391   }
 392 
 393   /**
 394    * Returns a string representing the supplied month.
 395    * <P>
 396    * The string returned is the long or short form of the month name taken 
 397    * from the default locale.
 398    *
 399    * @param month  the month.
 400    * @param shortened  if <code>true</code> return the abbreviation of the 
 401    *                   month.
 402    *
 403    * @return a string representing the supplied month.
 404    * @throws java.lang.IllegalArgumentException
 405    */
 406   public static String monthCodeToString(final int month, 
 407                                          final boolean shortened) {
 408 
 409     // check arguments...
 410     if (!isValidMonthCode(month)) {
 411       throw new IllegalArgumentException(
 412         "SerialDate.monthCodeToString: month outside valid range.");
 413     }
 414 
 415     final String[] months;
 416 
 417     if (shortened) {
 418       months = DATE_FORMAT_SYMBOLS.getShortMonths();
 419     }
 420     else {
 421         months = DATE_FORMAT_SYMBOLS.getMonths();
 422     }
 423 
 424     return months[month - 1];
 425 
 426   }
 427 
 428   /**
 429    * Converts a string to a month code.
 430    * <P>
 431    * This method will return one of the constants JANUARY, FEBRUARY, ..., 
 432    * DECEMBER that corresponds to the string.  If the string is not 
 433    * recognised, this method returns -1.
 434    *
 435    * @param s  the string to parse.
 436    *
 437    * @return <code>-1</code> if the string is not parseable, the month of the
 438    *         year otherwise.
 439    */
 440   public static int stringToMonthCode(String s) {
 441 
 442     final String[] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths();
 443     final String[] monthNames = DATE_FORMAT_SYMBOLS.getMonths();
 444 
 445     int result = -1;
 446     s = s.trim();
 447 
 448     // first try parsing the string as an integer (1-12)...
 449     try {
 450       result = Integer.parseInt(s);
 451     }
 452     catch (NumberFormatException e) {
 453       // suppress
 454     }
 455 
 456     // now search through the month names...
 457     if ((result < 1) || (result > 12)) {
 458       for (int i = 0; i < monthNames.length; i++) {
 459         if (s.equals(shortMonthNames[i])) {
 460           result = i + 1;
 461           break;
 462         }
 463         if (s.equals(monthNames[i])) {
 464           result = i + 1;
 465           break;
 466         }
 467       }
 468     }
 469 
 470     return result;
 471 
 472   }
 473 
 474   /**
 475    * Returns true if the supplied integer code represents a valid 
 476    * week-in-the-month, and false otherwise.
 477    *
 478    * @param code  the code being checked for validity.
 479    * @return <code>true</code> if the supplied integer code represents a 
 480    *         valid week-in-the-month.
 481    */
 482   public static boolean isValidWeekInMonthCode(final int code) {
 483 
 484     switch(code) {
 485       case FIRST_WEEK_IN_MONTH: 
 486       case SECOND_WEEK_IN_MONTH: 
 487       case THIRD_WEEK_IN_MONTH: 
 488       case FOURTH_WEEK_IN_MONTH: 
 489       case LAST_WEEK_IN_MONTH: return true;
 490       default: return false;
 491     }
 492 
 493   }
 494 
 495   /**
 496    * Determines whether or not the specified year is a leap year.
 497    *
 498    * @param yyyy  the year (in the range 1900 to 9999).
 499    *
 500    * @return <code>true</code> if the specified year is a leap year.
 501    */
 502   public static boolean isLeapYear(final int yyyy) {
 503 
 504     if ((yyyy % 4) != 0) {
 505       return false;
 506     }
 507     else if ((yyyy % 400) == 0) {
 508       return true;
 509     }
 510     else if ((yyyy % 100) == 0) {
 511       return false;
 512     }
 513     else {
 514       return true;
 515     }
 516 
 517   }
 518 
 519   /**
 520    * Returns the number of leap years from 1900 to the specified year 
 521    * INCLUSIVE.
 522    * <P>
 523    * Note that 1900 is not a leap year.
 524    *
 525    * @param yyyy  the year (in the range 1900 to 9999).
 526    *
 527    * @return the number of leap years from 1900 to the specified year.
 528    */
 529   public static int leapYearCount(final int yyyy) {
 530 
 531     final int leap4 = (yyyy - 1896) / 4;
 532     final int leap100 = (yyyy - 1800) / 100;
 533     final int leap400 = (yyyy - 1600) / 400;
 534     return leap4 - leap100 + leap400;
 535 
 536   }
 537 
 538   /**
 539    * Returns the number of the last day of the month, taking into account 
 540    * leap years.
 541    *
 542    * @param month  the month.
 543    * @param yyyy  the year (in the range 1900 to 9999).
 544    *
 545    * @return the number of the last day of the month.
 546    */
 547   public static int lastDayOfMonth(final int month, final int yyyy) {
 548 
 549     final int result = LAST_DAY_OF_MONTH[month];
 550     if (month != FEBRUARY) {
 551       return result;
 552     }
 553     else if (isLeapYear(yyyy)) {
 554       return result + 1;
 555     }
 556     else {
 557       return result;
 558     }
 559 
 560   }
 561 
 562   /**
 563    * Creates a new date by adding the specified number of days to the base 
 564    * date.
 565    *
 566    * @param days  the number of days to add (can be negative).
 567    * @param base  the base date.
 568    *
 569    * @return a new date.
 570    */
 571   public static SerialDate addDays(final int days, final SerialDate base) {
 572 
 573     final int serialDayNumber = base.toSerial() + days;
 574     return SerialDate.createInstance(serialDayNumber);
 575 
 576   }
 577 
 578   /**
 579    * Creates a new date by adding the specified number of months to the base 
 580    * date.
 581    * <P>
 582    * If the base date is close to the end of the month, the day on the result
 583    * may be adjusted slightly:  31 May + 1 month = 30 June.
 584    *
 585    * @param months  the number of months to add (can be negative).
 586    * @param base  the base date.
 587    *
 588    * @return a new date.
 589    */
 590   public static SerialDate addMonths(final int months, 
 591                                      final SerialDate base) {
 592 
 593     final int yy = (12 * base.getYYYY() + base.getMonth() + months - 1) / 12;
 594                    
 595     final int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1;
 596                    
 597     final int dd = Math.min(
 598         base.getDayOfMonth(), SerialDate.lastDayOfMonth(mm, yy)
 599     );
 600     return SerialDate.createInstance(dd, mm, yy);
 601 
 602   }
 603 
 604   /**
 605    * Creates a new date by adding the specified number of years to the base 
 606    * date.
 607    *
 608    * @param years  the number of years to add (can be negative).
 609    * @param base  the base date.
 610    *
 611    * @return A new date.
 612    */
 613   public static SerialDate addYears(final int years, final SerialDate base) {
 614 
 615     final int baseY = base.getYYYY();
 616     final int baseM = base.getMonth();
 617     final int baseD = base.getDayOfMonth();
 618 
 619     final int targetY = baseY + years;
 620     final int targetD = Math.min(
 621       baseD, SerialDate.lastDayOfMonth(baseM, targetY)
 622     );
 623 
 624     return SerialDate.createInstance(targetD, baseM, targetY);
 625 
 626   }
 627 
 628   /**
 629    * Returns the latest date that falls on the specified day-of-the-week and 
 630    * is BEFORE the base date.
 631    *
 632    * @param targetWeekday  a code for the target day-of-the-week.
 633    * @param base  the base date.
 634    *
 635    * @return the latest date that falls on the specified day-of-the-week and 
 636    *         is BEFORE the base date.
 637    */
 638   public static SerialDate getPreviousDayOfWeek(final int targetWeekday, 
 639                                                 final SerialDate base) {
 640 
 641     // check arguments...
 642     if (!SerialDate.isValidWeekdayCode(targetWeekday)) {
 643       throw new IllegalArgumentException(
 644           "Invalid day-of-the-week code."
 645       );
 646     }
 647 
 648     // find the date...
 649     final int adjust;
 650     final int baseDOW = base.getDayOfWeek();
 651     if (baseDOW > targetWeekday) {
 652       adjust = Math.min(0, targetWeekday - baseDOW);
 653     }
 654     else {
 655       adjust = -7 + Math.max(0, targetWeekday - baseDOW);
 656     }
 657 
 658     return SerialDate.addDays(adjust, base);
 659 
 660   }
 661 
 662   /**
 663    * Returns the earliest date that falls on the specified day-of-the-week
 664    * and is AFTER the base date.
 665    *
 666    * @param targetWeekday  a code for the target day-of-the-week.
 667    * @param base  the base date.
 668    *
 669    * @return the earliest date that falls on the specified day-of-the-week 
 670    *         and is AFTER the base date.
 671    */
 672   public static SerialDate getFollowingDayOfWeek(final int targetWeekday, 
 673                                                  final SerialDate base) {
 674 
 675     // check arguments...
 676     if (!SerialDate.isValidWeekdayCode(targetWeekday)) {
 677       throw new IllegalArgumentException(
 678           "Invalid day-of-the-week code."
 679       );
 680     }
 681 
 682     // find the date...
 683     final int adjust;
 684     final int baseDOW = base.getDayOfWeek();
 685     if (baseDOW > targetWeekday) {
 686       adjust = 7 + Math.min(0, targetWeekday - baseDOW);
 687     }
 688     else {
 689       adjust = Math.max(0, targetWeekday - baseDOW);
 690     }
 691 
 692     return SerialDate.addDays(adjust, base);
 693   }
 694 
 695   /**
 696    * Returns the date that falls on the specified day-of-the-week and is
 697    * CLOSEST to the base date.
 698    *
 699    * @param targetDOW  a code for the target day-of-the-week.
 700    * @param base  the base date.
 701    *
 702    * @return the date that falls on the specified day-of-the-week and is 
 703    *         CLOSEST to the base date.
 704    */
 705   public static SerialDate getNearestDayOfWeek(final int targetDOW,
 706                                                final SerialDate base) {
 707 
 708     // check arguments...
 709     if (!SerialDate.isValidWeekdayCode(targetDOW)) {
 710       throw new IllegalArgumentException(
 711           "Invalid day-of-the-week code."
 712       );
 713     }
 714 
 715     // find the date...
 716     final int baseDOW = base.getDayOfWeek();
 717     int adjust = -Math.abs(targetDOW - baseDOW);
 718     if (adjust >= 4) {
 719       adjust = 7 - adjust;
 720     }
 721     if (adjust <= -4) {
 722       adjust = 7 + adjust;
 723     }
 724     return SerialDate.addDays(adjust, base);
 725 
 726   }
 727 
 728   /**
 729    * Rolls the date forward to the last day of the month.
 730    *
 731    * @param base  the base date.
 732    *
 733    * @return a new serial date.
 734    */
 735   public SerialDate getEndOfCurrentMonth(final SerialDate base) {
 736     final int last = SerialDate.lastDayOfMonth(
 737       base.getMonth(), base.getYYYY()
 738     );
 739     return SerialDate.createInstance(last, base.getMonth(), base.getYYYY());
 740   }
 741 
 742   /**
 743    * Returns a string corresponding to the week-in-the-month code.
 744    * <P>
 745    * Need to find a better approach.
 746    *
 747    * @param count  an integer code representing the week-in-the-month.
 748    *
 749    * @return a string corresponding to the week-in-the-month code.
 750    */
 751   public static String weekInMonthToString(final int count) {
 752 
 753     switch (count) {
 754       case SerialDate.FIRST_WEEK_IN_MONTH : return "First";
 755       case SerialDate.SECOND_WEEK_IN_MONTH : return "Second";
 756       case SerialDate.THIRD_WEEK_IN_MONTH : return "Third";
 757       case SerialDate.FOURTH_WEEK_IN_MONTH : return "Fourth";
 758       case SerialDate.LAST_WEEK_IN_MONTH : return "Last";
 759       default :
 760           return "SerialDate.weekInMonthToString(): invalid code.";
 761     }
 762 
 763   }
 764 
 765   /**
 766    * Returns a string representing the supplied 'relative'.
 767    * <P>
 768    * Need to find a better approach.
 769    *
 770    * @param relative  a constant representing the 'relative'.
 771    *
 772    * @return a string representing the supplied 'relative'.
 773    */
 774   public static String relativeToString(final int relative) {
 775 
 776     switch (relative) {
 777       case SerialDate.PRECEDING : return "Preceding";
 778       case SerialDate.NEAREST : return "Nearest";
 779       case SerialDate.FOLLOWING : return "Following";
 780       default : return "ERROR : Relative To String";
 781     }
 782 
 783   }
 784 
 785   /**
 786    * Factory method that returns an instance of some concrete subclass of 
 787    * {@link SerialDate}.
 788    *
 789    * @param day  the day (1-31).
 790    * @param month  the month (1-12).
 791    * @param yyyy  the year (in the range 1900 to 9999).
 792    *
 793    * @return An instance of {@link SerialDate}.
 794    */
 795   public static SerialDate createInstance(final int day, final int month, 
 796                                           final int yyyy) {
 797     return new SpreadsheetDate(day, month, yyyy);
 798   }
 799 
 800   /**
 801    * Factory method that returns an instance of some concrete subclass of 
 802    * {@link SerialDate}.
 803    *
 804    * @param serial  the serial number for the day (1 January 1900 = 2).
 805    *
 806    * @return a instance of SerialDate.
 807    */
 808   public static SerialDate createInstance(final int serial) {
 809       return new SpreadsheetDate(serial);
 810   }
 811 
 812   /**
 813    * Factory method that returns an instance of a subclass of SerialDate.
 814    *
 815    * @param date  A Java date object.
 816    *
 817    * @return a instance of SerialDate.
 818    */
 819   public static SerialDate createInstance(final java.util.Date date) {
 820 
 821     final GregorianCalendar calendar = new GregorianCalendar();
 822     calendar.setTime(date);
 823     return new SpreadsheetDate(calendar.get(Calendar.DATE),
 824                                calendar.get(Calendar.MONTH) + 1,
 825                                calendar.get(Calendar.YEAR));
 826 
 827   }
 828 
 829   /**
 830    * Returns the serial number for the date, where 1 January 1900 = 2 (this
 831    * corresponds, almost, to the numbering system used in Microsoft Excel for
 832    * Windows and Lotus 1-2-3).
 833    *
 834    * @return the serial number for the date.
 835    */
 836   public abstract int toSerial();
 837 
 838   /**
 839    * Returns a java.util.Date.  Since java.util.Date has more precision than
 840    * SerialDate, we need to define a convention for the 'time of day'.
 841    *
 842    * @return this as <code>java.util.Date</code>.
 843    */
 844   public abstract java.util.Date toDate();
 845 
 846   /**
 847    * Returns a description of the date.
 848    *
 849    * @return a description of the date.
 850    */
 851   public String getDescription() {
 852     return this.description;
 853   }
 854 
 855   /**
 856    * Sets the description for the date.
 857    *
 858    * @param description  the new description for the date.
 859    */
 860   public void setDescription(final String description) {
 861       this.description = description;
 862   }
 863 
 864   /**
 865    * Converts the date to a string.
 866    *
 867    * @return  a string representation of the date.
 868    */
 869   public String toString() {
 870     return getDayOfMonth() + "-" + SerialDate.monthCodeToString(getMonth())
 871                            + "-" + getYYYY();
 872   }
 873 
 874   /**
 875    * Returns the year (assume a valid range of 1900 to 9999).
 876    *
 877    * @return the year.
 878    */
 879   public abstract int getYYYY();
 880 
 881   /**
 882    * Returns the month (January = 1, February = 2, March = 3).
 883    *
 884    * @return the month of the year.
 885    */
 886   public abstract int getMonth();
 887 
 888   /**
 889    * Returns the day of the month.
 890    *
 891    * @return the day of the month.
 892    */
 893   public abstract int getDayOfMonth();
 894 
 895   /**
 896    * Returns the day of the week.
 897    *
 898    * @return the day of the week.
 899    */
 900   public abstract int getDayOfWeek();
 901 
 902   /**
 903    * Returns the difference (in days) between this date and the specified 
 904    * 'other' date.
 905    * <P>
 906    * The result is positive if this date is after the 'other' date and
 907    * negative if it is before the 'other' date.
 908    *
 909    * @param other  the date being compared to.
 910    *
 911    * @return the difference between this and the other date.
 912    */
 913   public abstract int compare(SerialDate other);
 914 
 915   /**
 916    * Returns true if this SerialDate represents the same date as the 
 917    * specified SerialDate.
 918    *
 919    * @param other  the date being compared to.
 920    *
 921    * @return <code>true</code> if this SerialDate represents the same date as 
 922    *         the specified SerialDate.
 923    */
 924   public abstract boolean isOn(SerialDate other);
 925 
 926   /**
 927    * Returns true if this SerialDate represents an earlier date compared to
 928    * the specified SerialDate.
 929    *
 930    * @param other  The date being compared to.
 931    *
 932    * @return <code>true</code> if this SerialDate represents an earlier date 
 933    *         compared to the specified SerialDate.
 934    */
 935   public abstract boolean isBefore(SerialDate other);
 936 
 937   /**
 938    * Returns true if this SerialDate represents the same date as the 
 939    * specified SerialDate.
 940    *
 941    * @param other  the date being compared to.
 942    *
 943    * @return <code>true<code> if this SerialDate represents the same date
 944    *         as the specified SerialDate.
 945    */
 946   public abstract boolean isOnOrBefore(SerialDate other);
 947 
 948   /**
 949    * Returns true if this SerialDate represents the same date as the 
 950    * specified SerialDate.
 951    *
 952    * @param other  the date being compared to.
 953    *
 954    * @return <code>true</code> if this SerialDate represents the same date
 955    *         as the specified SerialDate.
 956    */
 957   public abstract boolean isAfter(SerialDate other);
 958 
 959   /**
 960    * Returns true if this SerialDate represents the same date as the 
 961    * specified SerialDate.
 962    *
 963    * @param other  the date being compared to.
 964    *
 965    * @return <code>true</code> if this SerialDate represents the same date
 966    *         as the specified SerialDate.
 967    */
 968   public abstract boolean isOnOrAfter(SerialDate other);
 969 
 970   /**
 971    * Returns <code>true</code> if this {@link SerialDate} is within the 
 972    * specified range (INCLUSIVE).  The date order of d1 and d2 is not 
 973    * important.
 974    *
 975    * @param d1  a boundary date for the range.
 976    * @param d2  the other boundary date for the range.
 977    *
 978    * @return A boolean.
 979    */
 980   public abstract boolean isInRange(SerialDate d1, SerialDate d2);
 981 
 982   /**
 983    * Returns <code>true</code> if this {@link SerialDate} is within the 
 984    * specified range (caller specifies whether or not the end-points are 
 985    * included).  The date order of d1 and d2 is not important.
 986    *
 987    * @param d1  a boundary date for the range.
 988    * @param d2  the other boundary date for the range.
 989    * @param include  a code that controls whether or not the start and end 
 990    *                 dates are included in the range.
 991    *
 992    * @return A boolean.
 993    */
 994   public abstract boolean isInRange(SerialDate d1, SerialDate d2, 
 995                                     int include);
 996 
 997   /**
 998    * Returns the latest date that falls on the specified day-of-the-week and
 999    * is BEFORE this date.
1000    *
1001    * @param targetDOW  a code for the target day-of-the-week.
1002    *
1003    * @return the latest date that falls on the specified day-of-the-week and
1004    *         is BEFORE this date.
1005    */
1006   public SerialDate getPreviousDayOfWeek(final int targetDOW) {
1007     return getPreviousDayOfWeek(targetDOW, this);
1008   }
1009
1010   /**
1011    * Returns the earliest date that falls on the specified day-of-the-week
1012    * and is AFTER this date.
1013    *
1014    * @param targetDOW  a code for the target day-of-the-week.
1015    *
1016    * @return the earliest date that falls on the specified day-of-the-week
1017    *         and is AFTER this date.
1018    */
1019   public SerialDate getFollowingDayOfWeek(final int targetDOW) {
1020     return getFollowingDayOfWeek(targetDOW, this);
1021   }
1022
1023   /**
1024    * Returns the nearest date that falls on the specified day-of-the-week.
1025    *
1026    * @param targetDOW  a code for the target day-of-the-week.
1027    *
1028    * @return the nearest date that falls on the specified day-of-the-week.
1029    */
1030   public SerialDate getNearestDayOfWeek(final int targetDOW) {
1031     return getNearestDayOfWeek(targetDOW, this);
1032   }
1033
1034 }

代码清单B-2 SerialDateTest.java

  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  * 
  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  * SerialDateTests.java
 29  * --------------------
 30  * (C) Copyright 2001-2005, by Object Refinery Limited.
 31  *
 32  * Original Author:  David Gilbert (for Object Refinery Limited);
 33  * Contributor(s):   -;
 34  *
 35  * $Id: SerialDateTests.java,v 1.6 2005/11/16 15:58:40 taqua Exp $
 36  *
 37  * Changes
 38  * -------
 39  * 15-Nov-2001 : Version 1 (DG);
 40  * 25-Jun-2002 : Removed unnecessary import (DG);
 41  * 24-Oct-2002 : Fixed errors reported by Checkstyle (DG);
 42  * 13-Mar-2003 : Added serialization test (DG);
 43  * 05-Jan-2005 : Added test for bug report 1096282 (DG);
 44  *
 45  */
 46 
 47 package org.jfree.date.junit;
 48 
 49 import java.io.ByteArrayInputStream;
 50 import java.io.ByteArrayOutputStream;
 51 import java.io.ObjectInput;
 52 import java.io.ObjectInputStream;
 53 import java.io.ObjectOutput;
 54 import java.io.ObjectOutputStream;
 55 
 56 import junit.framework.Test;
 57 import junit.framework.TestCase;
 58 import junit.framework.TestSuite;
 59 
 60 import org.jfree.date.MonthConstants;
 61 import org.jfree.date.SerialDate;
 62 
 63 /**
 64  * Some JUnit tests for the {@link SerialDate} class.
 65  */
 66 public class SerialDateTests extends TestCase {
 67 
 68   /** Date representing November 9. */
 69   private SerialDate nov9Y2001;
 70 
 71   /**
 72    * Creates a new test case.
 73    *
 74    * @param name  the name.
 75    */
 76   public SerialDateTests(final String name) {
 77     super(name);
 78   }
 79 
 80   /**
 81    * Returns a test suite for the JUnit test runner.
 82    *
 83    * @return The test suite.
 84    */
 85   public static Test suite() {
 86     return new TestSuite(SerialDateTests.class);
 87   }
 88 
 89   /**
 90    * Problem set up.
 91    */
 92   protected void setUp() {
 93     this.nov9Y2001 = SerialDate.createInstance(9, MonthConstants.NOVEMBER, 2001);
 94   }
 95 
 96   /**
 97    * 9 Nov 2001 plus two months should be 9 Jan 2002.
 98    */
 99   public void testAddMonthsTo9Nov2001() {
100     final SerialDate jan9Y2002 = SerialDate.addMonths(2, this.nov9Y2001);
101     final SerialDate answer = SerialDate.createInstance(9, 1, 2002);
102     assertEquals(answer, jan9Y2002);
103   }
104 
105   /**
106    * A test case for a reported bug, now fixed.
107    */
108   public void testAddMonthsTo5Oct2003() {
109     final SerialDate d1 = SerialDate.createInstance(5, MonthConstants.OCTOBER,2003);
110     final SerialDate d2 = SerialDate.addMonths(2, d1);
111     assertEquals(d2, SerialDate.createInstance(5, MonthConstants.DECEMBER, 2003));
112   }
113 
114   /**
115    * A test case for a reported bug, now fixed.
116    */
117   public void testAddMonthsTo1Jan2003() {
118     final SerialDate d1 = SerialDate.createInstance(1, MonthConstants.JANUARY, 2003);
119     final SerialDate d2 = SerialDate.addMonths(0, d1);
120     assertEquals(d2, d1);
121   }
122 
123   /**
124    * Monday preceding Friday 9 November 2001 should be 5 November.
125    */
126   public void testMondayPrecedingFriday9Nov2001() {
127     SerialDate mondayBefore = SerialDate.getPreviousDayOfWeek(
128          SerialDate.MONDAY, this.nov9Y2001
129     );
130     assertEquals(5, mondayBefore.getDayOfMonth());
131   }
132 
133   /**
134    * Monday following Friday 9 November 2001 should be 12 November.
135    */
136   public void testMondayFollowingFriday9Nov2001() {
137     SerialDate mondayAfter = SerialDate.getFollowingDayOfWeek(
138       SerialDate.MONDAY, this.nov9Y2001
139     );
140     assertEquals(12, mondayAfter.getDayOfMonth());
141   }
142 
143   /**
144    * Monday nearest Friday 9 November 2001 should be 12 November.
145    */
146   public void testMondayNearestFriday9Nov2001() {
147     SerialDate mondayNearest = SerialDate.getNearestDayOfWeek(
148       SerialDate.MONDAY, this.nov9Y2001
149     );
150     assertEquals(12, mondayNearest.getDayOfMonth());
151   }
152 
153   /**
154    * The Monday nearest to 22nd January 1970 falls on the 19th.
155    */
156   public void testMondayNearest22Jan1970() {
157     SerialDate jan22Y1970 = SerialDate.createInstance(22, MonthConstants.JANUARY, 1970);
158     SerialDate mondayNearest=SerialDate.getNearestDayOfWeek(SerialDate.MONDAY,
             jan22Y1970);
159      assertEquals(19, mondayNearest.getDayOfMonth());
160   }
161 
162   /**
163    * Problem that the conversion of days to strings returns the right result. Actually,
164    * this result depends on the Locale so this test needs to be modified.
165    */
166   public void testWeekdayCodeToString() {
167 
168     final String test = SerialDate.weekdayCodeToString(SerialDate.SATURDAY);
169     assertEquals("Saturday", test);
170 
171   }
172 
173   /**
174    * Test the conversion of a string to a weekday. Note that this test will fail if
175    * the default locale doesn't use English weekday names...devise a better test!
176    */
177   public void testStringToWeekday() {
178 
179     int weekday = SerialDate.stringToWeekdayCode("Wednesday");
180     assertEquals(SerialDate.WEDNESDAY, weekday);
181 
182     weekday = SerialDate.stringToWeekdayCode(" Wednesday ");
183     assertEquals(SerialDate.WEDNESDAY, weekday);
184 
185     weekday = SerialDate.stringToWeekdayCode("Wed");
186     assertEquals(SerialDate.WEDNESDAY, weekday);
187 
188   }
189 
190   /**
191    * Test the conversion of a string to a month.  Note that this test will fail if
192    * the default locale doesn't use English month names...devise a better test!
193    */
194   public void testStringToMonthCode() {
195 
196     int m = SerialDate.stringToMonthCode("January");
197     assertEquals(MonthConstants.JANUARY, m);
198 
199     m = SerialDate.stringToMonthCode(" January ");
200     assertEquals(MonthConstants.JANUARY, m);
201 
202     m = SerialDate.stringToMonthCode("Jan");
203     assertEquals(MonthConstants.JANUARY, m);
204 
205   }
206 
207   /**
208    * Tests the conversion of a month code to a string.
209    */
210   public void testMonthCodeToStringCode() {
211 
212     final String test = SerialDate.monthCodeToString(MonthConstants.DECEMBER);
213     assertEquals("December", test);
214 
215   }
216 
217   /**
218    * 1900 is not a leap year.
219    */
220   public void testIsNotLeapYear1900() {
221     assertTrue(!SerialDate.isLeapYear(1900));
222   }
223 
224   /**
225    * 2000 is a leap year.
226    */
227   public void testIsLeapYear2000() {
228     assertTrue(SerialDate.isLeapYear(2000));
229   }
230 
231   /**
232    * The number of leap years from 1900 up-to-and-including 1899 is 0.
233    */
234   public void testLeapYearCount1899() {
235     assertEquals(SerialDate.leapYearCount(1899), 0);
236   }
237 
238   /**
239    * The number of leap years from 1900 up-to-and-including 1903 is 0.
240    */
241   public void testLeapYearCount1903() {
242     assertEquals(SerialDate.leapYearCount(1903), 0);
243   }
244 
245   /**
246    * The number of leap years from 1900 up-to-and-including 1904 is 1.
247    */
248   public void testLeapYearCount1904() {
249     assertEquals(SerialDate.leapYearCount(1904), 1);
250   }
251 
252   /**
253    * The number of leap years from 1900 up-to-and-including 1999 is 24.
254    */
255   public void testLeapYearCount1999() {
256     assertEquals(SerialDate.leapYearCount(1999), 24);
257   }
258 
259   /**
260    * The number of leap years from 1900 up-to-and-including 2000 is 25.
261    */
262   public void testLeapYearCount2000() {
263     assertEquals(SerialDate.leapYearCount(2000), 25);
264   }
265 
266   /**
267    * Serialize an instance, restore it, and check for equality.
268    */
269   public void testSerialization() {
270 
271     SerialDate d1 = SerialDate.createInstance(15, 4, 2000);
272     SerialDate d2 = null;
273 
274     try {
275       ByteArrayOutputStream buffer = new ByteArrayOutputStream();
276       ObjectOutput out = new ObjectOutputStream(buffer);
277       out.writeObject(d1);
278       out.close();
279 
280       ObjectInput in = new ObjectInputStream(
                                  new ByteArrayInputStream(buffer.toByteArray()));
281       d2 = (SerialDate) in.readObject();
282       in.close();
283     }
284     catch (Exception e) {
285       System.out.println(e.toString());
286     }
287     assertEquals(d1, d2);
288 
289   }
290
291   /**
292    * A test for bug report 1096282 (now fixed).
293    */
294   public void test1096282() {
295     SerialDate d = SerialDate.createInstance(29, 2, 2004);
296     d = SerialDate.addYears(1, d);
297     SerialDate expected = SerialDate.createInstance(28, 2, 2005);
298     assertTrue(d.isOn(expected));
299   }
300 
301   /**
302    * Miscellaneous tests for the addMonths() method.
303    */
304   public void testAddMonths() {
305     SerialDate d1 = SerialDate.createInstance(31, 5, 2004);
306
307     SerialDate d2 = SerialDate.addMonths(1, d1);
308     assertEquals(30, d2.getDayOfMonth());
309     assertEquals(6, d2.getMonth());
310     assertEquals(2004, d2.getYYYY());
311
312     SerialDate d3 = SerialDate.addMonths(2, d1);
313     assertEquals(31, d3.getDayOfMonth());
314     assertEquals(7, d3.getMonth());
315     assertEquals(2004, d3.getYYYY());
316
317     SerialDate d4 = SerialDate.addMonths(1, SerialDate.addMonths(1, d1));
318     assertEquals(30, d4.getDayOfMonth());
319     assertEquals(7, d4.getMonth());
320     assertEquals(2004, d4.getYYYY());
321   }
322 }

代码清单B-3 MonthConstants.java

   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  *
   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  * MonthConstants.java
  29  * -------------------
  30  * (C) Copyright 2002, 2003, by Object Refinery Limited.
  31  *
  32  * Original Author:  David Gilbert (for Object Refinery Limited);
  33  * Contributor(s):   -;
  34  *
  35  * $Id: MonthConstants.java,v 1.4 2005/11/16 15:58:40 taqua Exp $
  36  *
  37  * Changes
  38  * -------
  39  * 29-May-2002 : Version 1 (code moved from SerialDate class) (DG);
  40  *
  41  */
  42 
  43 package org.jfree.date;
  44 
  45 /**
  46  * Useful constants for months.  Note that these are NOT equivalent to the
  47  * constants defined by java.util.Calendar (where JANUARY=0 and DECEMBER=11).
  48  * <P>
  49  * Used by the SerialDate and RegularTimePeriod classes.
  50  *
  51  * @author David Gilbert
  52  */
  53 public interface MonthConstants {
  54 
  55   /** Constant for January. */
  56   public static final int JANUARY = 1;
  57 
  58   /** Constant for February. */
  59   public static final int FEBRUARY = 2;
  60 
  61   /** Constant for March. */
  62   public static final int MARCH = 3;
  63 
  64   /** Constant for April. */
  65   public static final int APRIL = 4;
  66 
  67   /** Constant for May. */
  68   public static final int MAY = 5;
  69 
  70   /** Constant for June. */
  71   public static final int JUNE = 6;
  72 
  73   /** Constant for July. */
  74   public static final int JULY = 7;
  75 
  76   /** Constant for August. */
  77   public static final int AUGUST = 8;
  78 
  79   /** Constant for September. */
  80   public static final int SEPTEMBER = 9;
  81 
  82   /** Constant for October. */
  83   public static final int OCTOBER = 10;
  84 
  85   /** Constant for November. */
  86   public static final int NOVEMBER = 11;
  87 
  88   /** Constant for December. */
  89   public static final int DECEMBER = 12;
  90 
  91 }

代码清单B-4 BobsSerialDateTest.java

   1 package org.jfree.date.junit;
   2 
   3 import junit.framework.TestCase;
   4 import org.jfree.date.*;
   5 import static org.jfree.date.SerialDate.*;
   6 
   7 import java.util.*;
   8 
   9 public class BobsSerialDateTest extends TestCase {
  10 
  11   public void testIsValidWeekdayCode() throws Exception {
  12     for (int day = 1; day <= 7; day++)
  13       assertTrue(isValidWeekdayCode(day));
  14     assertFalse(isValidWeekdayCode(0));
  15     assertFalse(isValidWeekdayCode(8));
  16   }
  17 
  18   public void testStringToWeekdayCode() throws Exception {
  19 
  20     assertEquals(-1, stringToWeekdayCode("Hello"));
  21     assertEquals(MONDAY, stringToWeekdayCode("Monday"));
  22     assertEquals(MONDAY, stringToWeekdayCode("Mon"));
  23 //todo    assertEquals(MONDAY,stringToWeekdayCode("monday"));
  24 //    assertEquals(MONDAY,stringToWeekdayCode("MONDAY"));
  25 //    assertEquals(MONDAY, stringToWeekdayCode("mon"));
  26 
  27     assertEquals(TUESDAY, stringToWeekdayCode("Tuesday"));
  28     assertEquals(TUESDAY, stringToWeekdayCode("Tue"));
  29 //    assertEquals(TUESDAY,stringToWeekdayCode("tuesday"));
  30 //    assertEquals(TUESDAY,stringToWeekdayCode("TUESDAY"));
  31 //    assertEquals(TUESDAY, stringToWeekdayCode("tue"));
  32 //    assertEquals(TUESDAY, stringToWeekdayCode("tues"));
  33 
  34     assertEquals(WEDNESDAY, stringToWeekdayCode("Wednesday"));
  35     assertEquals(WEDNESDAY, stringToWeekdayCode("Wed"));
  36 //    assertEquals(WEDNESDAY,stringToWeekdayCode("wednesday"));
  37 //    assertEquals(WEDNESDAY,stringToWeekdayCode("WEDNESDAY"));
  38 //    assertEquals(WEDNESDAY, stringToWeekdayCode("wed"));
  39 
  40     assertEquals(THURSDAY, stringToWeekdayCode("Thursday"));
  41     assertEquals(THURSDAY, stringToWeekdayCode("Thu"));
  42 //    assertEquals(THURSDAY,stringToWeekdayCode("thursday"));
  43 //    assertEquals(THURSDAY,stringToWeekdayCode("THURSDAY"));
  44 //    assertEquals(THURSDAY, stringToWeekdayCode("thu"));
  45 //    assertEquals(THURSDAY, stringToWeekdayCode("thurs"));
  46 
  47     assertEquals(FRIDAY, stringToWeekdayCode("Friday"));
  48     assertEquals(FRIDAY, stringToWeekdayCode("Fri"));
  49 //    assertEquals(FRIDAY,stringToWeekdayCode("friday"));
  50 //    assertEquals(FRIDAY,stringToWeekdayCode("FRIDAY"));
  51 //    assertEquals(FRIDAY, stringToWeekdayCode("fri"));
  52 
  53     assertEquals(SATURDAY, stringToWeekdayCode("Saturday"));
  54     assertEquals(SATURDAY, stringToWeekdayCode("Sat"));
  55 //    assertEquals(SATURDAY,stringToWeekdayCode("saturday"));
  56 //    assertEquals(SATURDAY,stringToWeekdayCode("SATURDAY"));
  57 //    assertEquals(SATURDAY, stringToWeekdayCode("sat"));
  58 
  59     assertEquals(SUNDAY, stringToWeekdayCode("Sunday"));
  60     assertEquals(SUNDAY, stringToWeekdayCode("Sun"));
  61 //    assertEquals(SUNDAY,stringToWeekdayCode("sunday"));
  62 //    assertEquals(SUNDAY,stringToWeekdayCode("SUNDAY"));
  63 //    assertEquals(SUNDAY, stringToWeekdayCode("sun"));
  64   }
  65 
  66   public void testWeekdayCodeToString() throws Exception {
  67     assertEquals("Sunday", weekdayCodeToString(SUNDAY));
  68     assertEquals("Monday", weekdayCodeToString(MONDAY));
  69     assertEquals("Tuesday", weekdayCodeToString(TUESDAY));
  70     assertEquals("Wednesday", weekdayCodeToString(WEDNESDAY));
  71     assertEquals("Thursday", weekdayCodeToString(THURSDAY));
  72     assertEquals("Friday", weekdayCodeToString(FRIDAY));
  73     assertEquals("Saturday", weekdayCodeToString(SATURDAY));
  74   }
  75 
  76   public void testIsValidMonthCode() throws Exception {
  77     for (int i = 1; i <= 12; i++)
  78       assertTrue(isValidMonthCode(i));
  79     assertFalse(isValidMonthCode(0));
  80     assertFalse(isValidMonthCode(13));
  81   }
  82 
  83   public void testMonthToQuarter() throws Exception {
  84     assertEquals(1, monthCodeToQuarter(JANUARY));
  85     assertEquals(1, monthCodeToQuarter(FEBRUARY));
  86     assertEquals(1, monthCodeToQuarter(MARCH));
  87     assertEquals(2, monthCodeToQuarter(APRIL));
  88     assertEquals(2, monthCodeToQuarter(MAY));
  89     assertEquals(2, monthCodeToQuarter(JUNE));
  90     assertEquals(3, monthCodeToQuarter(JULY));
  91     assertEquals(3, monthCodeToQuarter(AUGUST));
  92     assertEquals(3, monthCodeToQuarter(SEPTEMBER));
  93     assertEquals(4, monthCodeToQuarter(OCTOBER));
  94     assertEquals(4, monthCodeToQuarter(NOVEMBER));
  95     assertEquals(4, monthCodeToQuarter(DECEMBER));
  96 
  97     try {
  98       monthCodeToQuarter(-1);
  99       fail("Invalid Month Code should throw exception");
 100     } catch (IllegalArgumentException e) {
 101     }
 102   }
 103 
 104   public void testMonthCodeToString() throws Exception {
 105     assertEquals("January", monthCodeToString(JANUARY));
 106     assertEquals("February", monthCodeToString(FEBRUARY));
 107     assertEquals("March", monthCodeToString(MARCH));
 108     assertEquals("April", monthCodeToString(APRIL));
 109     assertEquals("May", monthCodeToString(MAY));
 110     assertEquals("June", monthCodeToString(JUNE));
 111     assertEquals("July", monthCodeToString(JULY));
 112     assertEquals("August", monthCodeToString(AUGUST));
 113     assertEquals("September", monthCodeToString(SEPTEMBER));
 114     assertEquals("October", monthCodeToString(OCTOBER));
 115     assertEquals("November", monthCodeToString(NOVEMBER));
 116     assertEquals("December", monthCodeToString(DECEMBER));
 117 
 118     assertEquals("Jan", monthCodeToString(JANUARY, true));
 119     assertEquals("Feb", monthCodeToString(FEBRUARY, true));
 120     assertEquals("Mar", monthCodeToString(MARCH, true));
 121     assertEquals("Apr", monthCodeToString(APRIL, true));
 122     assertEquals("May", monthCodeToString(MAY, true));
 123     assertEquals("Jun", monthCodeToString(JUNE, true));
 124     assertEquals("Jul", monthCodeToString(JULY, true));
 125     assertEquals("Aug", monthCodeToString(AUGUST, true));
 126     assertEquals("Sep", monthCodeToString(SEPTEMBER, true));
 127     assertEquals("Oct", monthCodeToString(OCTOBER, true));
 128     assertEquals("Nov", monthCodeToString(NOVEMBER, true));
 129     assertEquals("Dec", monthCodeToString(DECEMBER, true));
 130 
 131     try {
 132       monthCodeToString(-1);
 133       fail("Invalid month code should throw exception");
 134     } catch (IllegalArgumentException e) {
 135     }
 136 
 137   }
 138 
 139   public void testStringToMonthCode() throws Exception {
 140     assertEquals(JANUARY,stringToMonthCode("1"));
 141     assertEquals(FEBRUARY,stringToMonthCode("2"));
 142     assertEquals(MARCH,stringToMonthCode("3"));
 143     assertEquals(APRIL,stringToMonthCode("4"));
 144     assertEquals(MAY,stringToMonthCode("5"));
 145     assertEquals(JUNE,stringToMonthCode("6"));
 146     assertEquals(JULY,stringToMonthCode("7"));
 147     assertEquals(AUGUST,stringToMonthCode("8"));
 148     assertEquals(SEPTEMBER,stringToMonthCode("9"));
 149     assertEquals(OCTOBER,stringToMonthCode("10"));
 150     assertEquals(NOVEMBER, stringToMonthCode("11"));
 151     assertEquals(DECEMBER,stringToMonthCode("12"));
 152 
 153 //todo    assertEquals(-1, stringToMonthCode("0"));
 154 //    assertEquals(-1, stringToMonthCode("13"));
 155 
 156     assertEquals(-1,stringToMonthCode("Hello"));
 157 
 158     for (int m = 1; m <= 12; m++) {
 159       assertEquals(m, stringToMonthCode(monthCodeToString(m, false)));
 160       assertEquals(m, stringToMonthCode(monthCodeToString(m, true)));
 161     }
 162 
 163 //    assertEquals(1,stringToMonthCode("jan"));
 164 //    assertEquals(2,stringToMonthCode("feb"));
 165 //    assertEquals(3,stringToMonthCode("mar"));
 166 //    assertEquals(4,stringToMonthCode("apr"));
 167 //    assertEquals(5,stringToMonthCode("may"));
 168 //    assertEquals(6,stringToMonthCode("jun"));
 169 //    assertEquals(7,stringToMonthCode("jul"));
 170 //    assertEquals(8,stringToMonthCode("aug"));
 171 //    assertEquals(9,stringToMonthCode("sep"));
 172 //    assertEquals(10,stringToMonthCode("oct"));
 173 //    assertEquals(11,stringToMonthCode("nov"));
 174 //    assertEquals(12,stringToMonthCode("dec"));
 175 
 176 //    assertEquals(1,stringToMonthCode("JAN"));
 177 //    assertEquals(2,stringToMonthCode("FEB"));
 178 //    assertEquals(3,stringToMonthCode("MAR"));
 179 //    assertEquals(4,stringToMonthCode("APR"));
 180 //    assertEquals(5,stringToMonthCode("MAY"));
 181 //    assertEquals(6,stringToMonthCode("JUN"));
 182 //    assertEquals(7,stringToMonthCode("JUL"));
 183 //    assertEquals(8,stringToMonthCode("AUG"));
 184 //    assertEquals(9,stringToMonthCode("SEP"));
 185 //    assertEquals(10,stringToMonthCode("OCT"));
 186 //    assertEquals(11,stringToMonthCode("NOV"));
 187 //    assertEquals(12,stringToMonthCode("DEC"));
 188 
 189 //    assertEquals(1,stringToMonthCode("january"));
 190 //    assertEquals(2,stringToMonthCode("february"));
 191 //    assertEquals(3,stringToMonthCode("march"));
 192 //    assertEquals(4,stringToMonthCode("april"));
 193 //    assertEquals(5,stringToMonthCode("may"));
 194 //    assertEquals(6,stringToMonthCode("june"));
 195 //    assertEquals(7,stringToMonthCode("july"));
 196 //    assertEquals(8,stringToMonthCode("august"));
 197 //    assertEquals(9,stringToMonthCode("september"));
 198 //    assertEquals(10,stringToMonthCode("october"));
 199 //    assertEquals(11,stringToMonthCode("november"));
 200 //    assertEquals(12,stringToMonthCode("december"));
 201 
 202 //    assertEquals(1,stringToMonthCode("JANUARY"));
 203 //    assertEquals(2,stringToMonthCode("FEBRUARY"));
 204 //    assertEquals(3,stringToMonthCode("MAR"));
 205 //    assertEquals(4,stringToMonthCode("APRIL"));
 206 //    assertEquals(5,stringToMonthCode("MAY"));
 207 //    assertEquals(6,stringToMonthCode("JUNE"));
 208 //    assertEquals(7,stringToMonthCode("JULY"));
 209 //    assertEquals(8,stringToMonthCode("AUGUST"));
 210 //    assertEquals(9,stringToMonthCode("SEPTEMBER"));
 211 //    assertEquals(10,stringToMonthCode("OCTOBER"));
 212 //    assertEquals(11,stringToMonthCode("NOVEMBER"));
 213 //    assertEquals(12,stringToMonthCode("DECEMBER"));
 214   }
 215 
 216   public void testIsValidWeekInMonthCode() throws Exception {
 217     for (int w = 0; w <= 4; w++) {
 218       assertTrue(isValidWeekInMonthCode(w));
 219     }
 220     assertFalse(isValidWeekInMonthCode(5));
 221   }
 222 
 223   public void testIsLeapYear() throws Exception {
 224     assertFalse(isLeapYear(1900));
 225     assertFalse(isLeapYear(1901));
 226     assertFalse(isLeapYear(1902));
 227     assertFalse(isLeapYear(1903));
 228     assertTrue(isLeapYear(1904));
 229     assertTrue(isLeapYear(1908));
 230     assertFalse(isLeapYear(1955));
 231     assertTrue(isLeapYear(1964));
 232     assertTrue(isLeapYear(1980));
 233     assertTrue(isLeapYear(2000));
 234     assertFalse(isLeapYear(2001));
 235     assertFalse(isLeapYear(2100));
 236   }
 237 
 238   public void testLeapYearCount() throws Exception {
 239     assertEquals(0, leapYearCount(1900));
 240     assertEquals(0, leapYearCount(1901));
 241     assertEquals(0, leapYearCount(1902));
 242     assertEquals(0, leapYearCount(1903));
 243     assertEquals(1, leapYearCount(1904));
 244     assertEquals(1, leapYearCount(1905));
 245     assertEquals(1, leapYearCount(1906));
 246     assertEquals(1, leapYearCount(1907));
 247     assertEquals(2, leapYearCount(1908));
 248     assertEquals(24, leapYearCount(1999));
 249     assertEquals(25, leapYearCount(2001));
 250     assertEquals(49, leapYearCount(2101));
 251     assertEquals(73, leapYearCount(2201));
 252     assertEquals(97, leapYearCount(2301));
 253     assertEquals(122, leapYearCount(2401));
 254   }
 255 
 256   public void testLastDayOfMonth() throws Exception {
 257     assertEquals(31, lastDayOfMonth(JANUARY, 1901));
 258     assertEquals(28, lastDayOfMonth(FEBRUARY, 1901));
 259     assertEquals(31, lastDayOfMonth(MARCH, 1901));
 260     assertEquals(30, lastDayOfMonth(APRIL, 1901));
 261     assertEquals(31, lastDayOfMonth(MAY, 1901));
 262     assertEquals(30, lastDayOfMonth(JUNE, 1901));
 263     assertEquals(31, lastDayOfMonth(JULY, 1901));
 264     assertEquals(31, lastDayOfMonth(AUGUST, 1901));
 265     assertEquals(30, lastDayOfMonth(SEPTEMBER, 1901));
 266     assertEquals(31, lastDayOfMonth(OCTOBER, 1901));
 267     assertEquals(30, lastDayOfMonth(NOVEMBER, 1901));
 268     assertEquals(31, lastDayOfMonth(DECEMBER, 1901));
 269     assertEquals(29, lastDayOfMonth(FEBRUARY, 1904));
 270   }
 271 
 272   public void testAddDays() throws Exception {
 273     SerialDate newYears = d(1, JANUARY, 1900);
 274     assertEquals(d(2, JANUARY, 1900), addDays(1, newYears));
 275     assertEquals(d(1, FEBRUARY, 1900), addDays(31, newYears));
 276     assertEquals(d(1, JANUARY, 1901), addDays(365, newYears));
 277     assertEquals(d(31, DECEMBER, 1904), addDays(5 * 365, newYears));
 278   }
 279 
 280   private static SpreadsheetDate d(int day, int month, int year) {return new 
         SpreadsheetDate(day, month, year);}
 281 
 282   public void testAddMonths() throws Exception {
 283     assertEquals(d(1, FEBRUARY, 1900), addMonths(1, d(1, JANUARY, 1900)));
 284     assertEquals(d(28, FEBRUARY, 1900), addMonths(1, d(31, JANUARY, 1900)));
 285     assertEquals(d(28, FEBRUARY, 1900), addMonths(1, d(30, JANUARY, 1900)));
 286     assertEquals(d(28, FEBRUARY, 1900), addMonths(1, d(29, JANUARY, 1900)));
 287     assertEquals(d(28, FEBRUARY, 1900), addMonths(1, d(28, JANUARY, 1900)));
 288     assertEquals(d(27, FEBRUARY, 1900), addMonths(1, d(27, JANUARY, 1900)));
 289 
 290     assertEquals(d(30, JUNE, 1900), addMonths(5, d(31, JANUARY, 1900)));
 291     assertEquals(d(30, JUNE, 1901), addMonths(17, d(31, JANUARY, 1900)));
 292 
 293     assertEquals(d(29, FEBRUARY, 1904), addMonths(49, d(31, JANUARY, 1900)));
 294 
 295   }
 296 
 297   public void testAddYears() throws Exception {
 298     assertEquals(d(1, JANUARY, 1901), addYears(1, d(1, JANUARY, 1900)));
 299     assertEquals(d(28, FEBRUARY, 1905), addYears(1, d(29, FEBRUARY, 1904)));
 300     assertEquals(d(28, FEBRUARY, 1905), addYears(1, d(28, FEBRUARY, 1904)));
 301     assertEquals(d(28, FEBRUARY, 1904), addYears(1, d(28, FEBRUARY, 1903)));
 302   }
 303 
 304   public void testGetPreviousDayOfWeek() throws Exception {
 305     assertEquals(d(24, FEBRUARY, 2006), getPreviousDayOfWeek(FRIDAY,
          d(1, MARCH, 2006)));
 306     assertEquals(d(22, FEBRUARY, 2006), getPreviousDayOfWeek(WEDNESDAY,
          d(1, MARCH, 2006)));
 307     assertEquals(d(29, FEBRUARY, 2004), getPreviousDayOfWeek(SUNDAY,
          d(3, MARCH, 2004)));
 308     assertEquals(d(29, DECEMBER, 2004), getPreviousDayOfWeek(WEDNESDAY,
          d(5, JANUARY, 2005)));
 309 
 310     try {
 311       getPreviousDayOfWeek(-1, d(1, JANUARY, 2006));
 312       fail("Invalid day of week code should throw exception");
 313     } catch (IllegalArgumentException e) {
 314     }
 315   }
 316 
 317   public void testGetFollowingDayOfWeek() throws Exception {
 318 //    assertEquals(d(1, JANUARY, 2005),getFollowingDayOfWeek(SATURDAY,
            d(25, DECEMBER, 2004)));
 319     assertEquals(d(1, JANUARY, 2005), getFollowingDayOfWeek(SATURDAY,
          d(26, DECEMBER, 2004)));
 320     assertEquals(d(3, MARCH, 2004), getFollowingDayOfWeek(WEDNESDAY,
          d(28, FEBRUARY, 2004)));
 321 
 322     try {
 323       getFollowingDayOfWeek(-1, d(1, JANUARY, 2006));
 324       fail("Invalid day of week code should throw exception");
 325     } catch (IllegalArgumentException e) {
 326     }
 327   }
 328 
 329   public void testGetNearestDayOfWeek() throws Exception {
 330     assertEquals(d(16, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(16, APRIL,
            2006)));
 331     assertEquals(d(16, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(17, APRIL, 
            2006)));
 332     assertEquals(d(16, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(18, APRIL, 
            2006)));
 333     assertEquals(d(16, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(19, APRIL, 
            2006)));
 334     assertEquals(d(23, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(20, APRIL, 
            2006)));
 335     assertEquals(d(23, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(21, APRIL, 
            2006)));
 336     assertEquals(d(23, APRIL, 2006), getNearestDayOfWeek(SUNDAY, d(22, APRIL, 
            2006)));
 337 
 338 //todo    assertEquals(d(17, APRIL, 2006), getNearestDayOfWeek(MONDAY,
                 d(16, APRIL, 2006)));
 339     assertEquals(d(17, APRIL, 2006), getNearestDayOfWeek(MONDAY, d(17, APRIL, 
            2006)));
 340     assertEquals(d(17, APRIL, 2006), getNearestDayOfWeek(MONDAY, d(18, APRIL, 
            2006)));
 341     assertEquals(d(17, APRIL, 2006), getNearestDayOfWeek(MONDAY, d(19, APRIL, 
            2006)));
 342     assertEquals(d(17, APRIL, 2006), getNearestDayOfWeek(MONDAY, d(20, APRIL, 
            2006)));
 343     assertEquals(d(24, APRIL, 2006), getNearestDayOfWeek(MONDAY, d(21, APRIL, 
            2006)));
 344     assertEquals(d(24, APRIL, 2006), getNearestDayOfWeek(MONDAY, d(22, APRIL, 
            2006)));
 345 
 346 //    assertEquals(d(18, APRIL, 2006), getNearestDayOfWeek(TUESDAY,
            d(16, APRIL, 2006)));
 347 //    assertEquals(d(18, APRIL, 2006), getNearestDayOfWeek(TUESDAY,
            d(17, APRIL, 2006)));
 348     assertEquals(d(18, APRIL, 2006), getNearestDayOfWeek(TUESDAY, d(18, APRIL, 
            2006)));
 349     assertEquals(d(18, APRIL, 2006), getNearestDayOfWeek(TUESDAY, d(19, APRIL, 
            2006)));
 350     assertEquals(d(18, APRIL, 2006), getNearestDayOfWeek(TUESDAY, d(20, APRIL, 
            2006)));
 351     assertEquals(d(18, APRIL, 2006), getNearestDayOfWeek(TUESDAY, d(21, APRIL, 
            2006)));
 352     assertEquals(d(25, APRIL, 2006), getNearestDayOfWeek(TUESDAY, d(22, APRIL, 
            2006)));
 353 
 354 //    assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY
            d(16, APRIL, 2006)));
 355 //    assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY,
            d(17, APRIL, 2006)));
 356 //    assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY,
            d(18, APRIL, 2006)));
 357     assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY,
          d(19, APRIL, 2006)));
 358     assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY,
          d(20, APRIL, 2006)));
 359     assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY,
          d(21, APRIL, 2006)));
 360     assertEquals(d(19, APRIL, 2006), getNearestDayOfWeek(WEDNESDAY,
          d(22, APRIL, 2006)));
 361  
 362 //    assertEquals(d(13, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
            d(16, APRIL, 2006)));
 363 //    assertEquals(d(20, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
            d(17, APRIL, 2006)));
 364 //    assertEquals(d(20, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
            d(18, APRIL, 2006)));
 365 //    assertEquals(d(20, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
            d(19, APRIL, 2006)));
 366     assertEquals(d(20, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
          d(20, APRIL, 2006)));
 367     assertEquals(d(20, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
          d(21, APRIL, 2006)));
 368     assertEquals(d(20, APRIL, 2006), getNearestDayOfWeek(THURSDAY,
          d(22, APRIL, 2006)));
 369 
 370 //    assertEquals(d(14, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(16, APRIL, 
            2006)));
 371 //    assertEquals(d(14, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(17, APRIL, 
            2006)));
 372 //    assertEquals(d(21, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(18, APRIL, 
            2006)));
 373 //    assertEquals(d(21, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(19, APRIL, 
            2006)));
 374 //    assertEquals(d(21, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(20, APRIL, 
            2006)));
 375     assertEquals(d(21, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(21, APRIL, 
            2006)));
 376     assertEquals(d(21, APRIL, 2006), getNearestDayOfWeek(FRIDAY, d(22, APRIL, 
            2006)));
 377 
 378 //    assertEquals(d(15, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
            d(16, APRIL, 2006)));
 379 //    assertEquals(d(15, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
            d(17, APRIL, 2006)));
 380 //    assertEquals(d(15, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
            d(18, APRIL, 2006)));
 381 //    assertEquals(d(22, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
            d(19, APRIL, 2006)));
 382 //    assertEquals(d(22, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
            d(20, APRIL, 2006)));
 383 //    assertEquals(d(22, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
            d(21, APRIL, 2006)));
 384     assertEquals(d(22, APRIL, 2006), getNearestDayOfWeek(SATURDAY,
          d(22, APRIL, 2006)));
 385 
 386     try {
 387       getNearestDayOfWeek(-1, d(1, JANUARY, 2006));
 388       fail("Invalid day of week code should throw exception");
 389     } catch (IllegalArgumentException e) {
 390     }
 391   }
 392 
 393   public void testEndOfCurrentMonth() throws Exception {
 394     SerialDate d = SerialDate.createInstance(2);
 395     assertEquals(d(31, JANUARY, 2006), d.getEndOfCurrentMonth(d(1, JANUARY, 2006)));
 396     assertEquals(d(28, FEBRUARY, 2006), d.getEndOfCurrentMonth(d(1, FEBRUARY, 2006)));
 397     assertEquals(d(31, MARCH, 2006), d.getEndOfCurrentMonth(d(1, MARCH, 2006)));
 398     assertEquals(d(30, APRIL, 2006), d.getEndOfCurrentMonth(d(1, APRIL, 2006)));
 399     assertEquals(d(31, MAY, 2006), d.getEndOfCurrentMonth(d(1, MAY, 2006)));
 400     assertEquals(d(30, JUNE, 2006), d.getEndOfCurrentMonth(d(1, JUNE, 2006)));
 401     assertEquals(d(31, JULY, 2006), d.getEndOfCurrentMonth(d(1, JULY, 2006)));
 402     assertEquals(d(31, AUGUST, 2006), d.getEndOfCurrentMonth(d(1, AUGUST, 2006)));
 403     assertEquals(d(30, SEPTEMBER, 2006), d.getEndOfCurrentMonth
         (d(1, SEPTEMBER, 2006)));
 404     assertEquals(d(31, OCTOBER, 2006), d.getEndOfCurrentMonth(d(1, OCTOBER, 2006)));
 405     assertEquals(d(30, NOVEMBER, 2006), d.getEndOfCurrentMonth(d(1, NOVEMBER, 2006)));
 406     assertEquals(d(31, DECEMBER, 2006), d.getEndOfCurrentMonth(d(1, DECEMBER, 2006)));
 407     assertEquals(d(29, FEBRUARY, 2008), d.getEndOfCurrentMonth(d(1, FEBRUARY, 2008)));
 408   }
 409 
 410   public void testWeekInMonthToString() throws Exception {
 411     assertEquals("First",weekInMonthToString(FIRST_WEEK_IN_MONTH));
 412     assertEquals("Second",weekInMonthToString(SECOND_WEEK_IN_MONTH));
 413     assertEquals("Third",weekInMonthToString(THIRD_WEEK_IN_MONTH));
 414     assertEquals("Fourth",weekInMonthToString(FOURTH_WEEK_IN_MONTH));
 415     assertEquals("Last",weekInMonthToString(LAST_WEEK_IN_MONTH));
 416 
 417 //todo    try {
 418 //      weekInMonthToString(-1);
 419 //      fail("Invalid week code should throw exception");
 420 //    } catch (IllegalArgumentException e) {
 421 //    }
 422   }
 423 
 424   public void testRelativeToString() throws Exception {
 425     assertEquals("Preceding",relativeToString(PRECEDING));
 426     assertEquals("Nearest",relativeToString(NEAREST));
 427     assertEquals("Following",relativeToString(FOLLOWING));
 428 
 429 //todo    try {
 430 //      relativeToString(-1000);
 431 //      fail("Invalid relative code should throw exception");
 432 //    } catch (IllegalArgumentException e) {
 433 //    }
 434   }
 435 
 436   public void testCreateInstanceFromDDMMYYY() throws Exception {
 437     SerialDate date = createInstance(1, JANUARY, 1900);
 438     assertEquals(1,date.getDayOfMonth());
 439     assertEquals(JANUARY,date.getMonth());
 440     assertEquals(1900,date.getYYYY());
 441     assertEquals(2,date.toSerial());
 442   }
 443 
 444   public void testCreateInstanceFromSerial() throws Exception {
 445     assertEquals(d(1, JANUARY, 1900),createInstance(2));
 446     assertEquals(d(1, JANUARY, 1901), createInstance(367));
 447   }
 448 
 449   public void testCreateInstanceFromJavaDate() throws Exception {
 450     assertEquals(d(1, JANUARY, 1900), 
                      createInstance(new GregorianCalendar(1900,0,1).getTime()));
 451     assertEquals(d(1, JANUARY, 2006), 
                      createInstance(new GregorianCalendar(2006,0,1).getTime()));
 452   }
 453 
 454   public static void main(String[] args) {
 455     junit.textui.TestRunner.run(BobsSerialDateTest.class);
 456   }
 457 }

代码清单B-5 SpreadsheetDate.java

   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  * 
   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  * SpreadsheetDate.java
  29  * --------------------
  30  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  31  *
  32  * Original Author:  David Gilbert (for Object Refinery Limited);
  33  * Contributor(s):   -;
  34  *
  35  * $Id: SpreadsheetDate.java,v 1.8 2005/11/03 09:25:39 mungady Exp $
  36  *
  37  * Changes
  38  * -------
  39  * 11-Oct-2001 : Version 1 (DG);
  40  * 05-Nov-2001 : Added getDescription() and setDescription() methods (DG);
  41  * 12-Nov-2001 : Changed name from ExcelDate.java to SpreadsheetDate.java (DG);
  42  *               Fixed a bug in calculating day, month and year from serial
  43  *               number (DG);
  44  * 24-Jan-2002 : Fixed a bug in calculating the serial number from the day,
  45  *               month and year.  Thanks to Trevor Hills for the report (DG);
  46  * 29-May-2002 : Added equals(Object) method (SourceForge ID 558850) (DG);
  47  * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  48  * 13-Mar-2003 : Implemented Serializable (DG);
  49  * 04-Sep-2003 : Completed isInRange() methods (DG);
  50  * 05-Sep-2003 : Implemented Comparable (DG);
  51  * 21-Oct-2003 : Added hashCode() method (DG);
  52  *
  53  */
  54 
  55 package org.jfree.date;
  56 
  57 import java.util.Calendar;
  58 import java.util.Date;
  59 
  60 /**
  61  * Represents a date using an integer, in a similar fashion to the
  62  * implementation in Microsoft Excel.  The range of dates supported is
  63  * 1-Jan-1900 to 31-Dec-9999.
  64  * <P>
  65  * Be aware that there is a deliberate bug in Excel that recognises the year
  66  * 1900 as a leap year when in fact it is not a leap year. You can find more
  67  * information on the Microsoft website in article Q181370:
  68  * <P>
  69  * 
  70  * <P>
  71  * Excel uses the convention that 1-Jan-1900 = 1.  This class uses the
  72  * convention 1-Jan-1900 = 2.
  73  * The result is that the day number in this class will be different to the
  74  * Excel figure for January and February 1900...but then Excel adds in an extra
  75  * day (29-Feb-1900 which does not actually exist!) and from that point forward
  76  * the day numbers will match.
  77  *
  78  * @author David Gilbert
  79  */
  80 public class SpreadsheetDate extends SerialDate {
  81 
  82   /** For serialization. */
  83   private static final long serialVersionUID = -2039586705374454461L;
  84 
  85   /**
  86    * The day number (1-Jan-1900 = 2, 2-Jan-1900 = 3, ..., 31-Dec-9999 =
  87    * 2958465).
  88    */
  89   private int serial;
  90 
  91   /** The day of the month (1 to 28, 29, 30 or 31 depending on the month). */
  92   private int day;
  93 
  94   /** The month of the year (1 to 12). */
  95   private int month;
  96 
  97   /** The year (1900 to 9999). */
  98   private int year;
  99 
 100   /** An optional description for the date. */
 101   private String description;
 102 
 103   /**
 104    * Creates a new date instance.
 105    *
 106    * @param day  the day (in the range 1 to 28/29/30/31).
 107    * @param month  the month (in the range 1 to 12).
 108    * @param year  the year (in the range 1900 to 9999).
 109    */
 110   public SpreadsheetDate(final int day, final int month, final int year) {
 111 
 112     if ((year >= 1900) && (year <= 9999)) {
 113       this.year = year;
 114     }
 115     else {
 116       throw new IllegalArgumentException(
 117           "The 'year' argument must be in range 1900 to 9999."
 118       );
 119     }
 120 
 121     if ((month >= MonthConstants.JANUARY)
 122           && (month <= MonthConstants.DECEMBER)) {
 123       this.month = month;
 124     }
 125     else {
 126       throw new IllegalArgumentException(
 127           "The 'month' argument must be in the range 1 to 12."
 128       );
 129     }
 130 
 131     if ((day >= 1) && (day <= SerialDate.lastDayOfMonth(month, year))) {
 132       this.day = day;
 133     }
 134     else {
 135       throw new IllegalArgumentException("Invalid 'day' argument.");
 136     }
 137 
 138     // the serial number needs to be synchronised with the day-month-year...
 139     this.serial = calcSerial(day, month, year);
 140 
 141     this.description = null;
 142 
 143   }
 144 
 145   /**
 146    * Standard constructor - creates a new date object representing the
 147    * specified day number (which should be in the range 2 to 2958465.
 148    *
 149    * @param serial  the serial number for the day (range: 2 to 2958465).
 150    */
 151   public SpreadsheetDate(final int serial) {
 152 
 153     if ((serial >= SERIAL_LOWER_BOUND) && (serial <= SERIAL_UPPER_BOUND)) {
 154       this.serial = serial;
 155     }
 156     else {
 157       throw new IllegalArgumentException(
 158           "SpreadsheetDate: Serial must be in range 2 to 2958465.");
 159     }
 160 
 161     // the day-month-year needs to be synchronised with the serial number...
 162     calcDayMonthYear();
 163 
 164     }
 165 
 166   /**
 167    * Returns the description that is attached to the date.  It is not
 168    * required that a date have a description, but for some applications it
 169    * is useful.
 170    *
 171    * @return The description that is attached to the date.
 172    */
 173   public String getDescription() {
 174     return this.description;
 175   }
 176 
 177   /**
 178    * Sets the description for the date.
 179    *
 180    * @param description the description for this date (<code>null</code>
 181    *                    permitted).
 182    */
 183   public void setDescription(final String description) {
 184     this.description = description;
 185   }
 186 
 187   /**
 188    * Returns the serial number for the date, where 1 January 1900 = 2
 189    * (this corresponds, almost, to the numbering system used in Microsoft
 190    * Excel for Windows and Lotus 1-2-3).
 191    *
 192    * @return The serial number of this date.
 193    */
 194   public int toSerial() {
 195     return this.serial;
 196   }
 197 
 198   /**
 199    * Returns a <code>java.util.Date</code> equivalent to this date.
 200    *
 201    * @return The date.
 202    */
 203   public Date toDate() {
 204     final Calendar calendar = Calendar.getInstance();
 205     calendar.set(getYYYY(), getMonth() - 1, getDayOfMonth(), 0, 0, 0);
 206     return calendar.getTime();
 207   }
 208 
 209   /**
 210    * Returns the year (assume a valid range of 1900 to 9999).
 211    *
 212    * @return The year.
 213    */
 214   public int getYYYY() {
 215     return this.year;
 216   }
 217 
 218   /**
 219    * Returns the month (January = 1, February = 2, March = 3).
 220    *
 221    * @return The month of the year.
 222    */
 223   public int getMonth() {
 224     return this.month;
 225   }
 226 
 227   /**
 228    * Returns the day of the month.
 229    *
 230    * @return The day of the month.
 231    */
 232   public int getDayOfMonth() {
 233     return this.day;
 234   }
 235 
 236   /**
 237    * Returns a code representing the day of the week.
 238    * <P>
 239    * The codes are defined in the {@link SerialDate} class as:
 240    * <code>SUNDAY</code>, <code>MONDAY</code>, <code>TUESDAY</code>,
 241    * <code>WEDNESDAY</code>, <code>THURSDAY</code>, <code>FRIDAY</code>, and
 242    * <code>SATURDAY</code>.
 243    *
 244    * @return A code representing the day of the week.
 245    */
 246   public int getDayOfWeek() {
 247     return (this.serial + 6) % 7 + 1;
 248   }
 249 
 250   /**
 251    * Tests the equality of this date with an arbitrary object.
 252    * <P>
 253    * This method will return true ONLY if the object is an instance of the
 254    * {@link SerialDate} base class, and it represents the same day as this
 255    * {@link SpreadsheetDate}.
 256    *
 257    * @param object  the object to compare (<code>null</code> permitted).
 258    *
 259    * @return A boolean.
 260    */
 261   public boolean equals(final Object object) {
 262 
 263     if (object instanceof SerialDate) {
 264       final SerialDate s = (SerialDate) object;
 265       return (s.toSerial() == this.toSerial());
 266     }

 267   else {
 268     return false;
 269   }
 270 
 271   }
 272 
 273   /**
 274    * Returns a hash code for this object instance.
 275    *
 276    * @return A hash code.
 277    */
 278   public int hashCode() {
 279     return toSerial();
 280   }
 281 
 282   /**
 283    * Returns the difference (in days) between this date and the specified
 284    * 'other' date.
 285    *
 286    * @param other  the date being compared to.
 287    *
 288    * @return The difference (in days) between this date and the specified
 289    *         'other' date.
 290    */
 291   public int compare(final SerialDate other) {
 292     return this.serial - other.toSerial();
 293   }
 294 
 295   /**
 296    * Implements the method required by the Comparable interface.
 297    *
 298    * @param other  the other object (usually another SerialDate).
 299    *
 300    * @return A negative integer, zero, or a positive integer as this object
 301    *         is less than, equal to, or greater than the specified object.
 302    */
 303   public int compareTo(final Object other) {
 304     return compare((SerialDate) other);
 305   }
 306 
 307   /**
 308    * Returns true if this SerialDate represents the same date as the
 309    * specified SerialDate.
 310    *
 311    * @param other  the date being compared to.
 312    *
 313    * @return <code>true</code> if this SerialDate represents the same date as
 314    *         the specified SerialDate.
 315    */
 316   public boolean isOn(final SerialDate other) {
 317     return (this.serial == other.toSerial());
 318   }
 319 
 320   /**
 321    s* Returns true if this SerialDate represents an earlier date compared to
 322    * the specified SerialDate.
 323    *
 324    * @param other  the date being compared to.
 325    *
 326    * @return <code>true</code> if this SerialDate represents an earlier date
 327    *         compared to the specified SerialDate.
 328    */
 329   public boolean isBefore(final SerialDate other) {
 330     return (this.serial < other.toSerial());
 331   }
 332 
 333   /**
 334    * Returns true if this SerialDate represents the same date as the
 335    * specified SerialDate.
 336    *
 337    * @param other  the date being compared to.
 338    *
 339    * @return <code>true</code> if this SerialDate represents the same date
 340    *         as the specified SerialDate.
 341    */
 342   public boolean isOnOrBefore(final SerialDate other) {
 343     return (this.serial <= other.toSerial());
 344   }
 345 
 346   /**
 347    * Returns true if this SerialDate represents the same date as the
 348    * specified SerialDate.
 349    *
 350    * @param other  the date being compared to.
 351    *
 352    * @return <code>true</code> if this SerialDate represents the same date
 353    *         as the specified SerialDate.
 354    */
 355   public boolean isAfter(final SerialDate other) {
 356     return (this.serial > other.toSerial());
 357   }
 358 
 359   /**
 360    * Returns true if this SerialDate represents the same date as the
 361    * specified SerialDate.
 362    *
 363    * @param other  the date being compared to.
 364    *
 365    * @return <code>true</code> if this SerialDate represents the same date as
 366    *         the specified SerialDate.
 367    */
 368   public boolean isOnOrAfter(final SerialDate other) {
 369     return (this.serial >= other.toSerial());
 370   }
 371 
 372   /**
 373    * Returns <code>true</code> if this {@link SerialDate} is within the
 374    * specified range (INCLUSIVE).  The date order of d1 and d2 is not
 375    * important.
 376    *
 377    * @param d1  a boundary date for the range.
 378    * @param d2  the other boundary date for the range.
 379    *
 380    * @return A boolean.
 381    */
 382   public boolean isInRange(final SerialDate d1, final SerialDate d2) {
 383     return isInRange(d1, d2, SerialDate.INCLUDE_BOTH);
 384   }
 385 
 386   /**
 387    * Returns true if this SerialDate is within the specified range (caller
 388    * specifies whether or not the end-points are included).  The order of d1
 389    * and d2 is not important.
 390    *
 391    * @param d1  one boundary date for the range.
 392    * @param d2  a second boundary date for the range.
 393    * @param include  a code that controls whether or not the start and end
 394    *                 dates are included in the range.
 395    *
 396    * @return <code>true</code> if this SerialDate is within the specified
 397    *         range.
 398    */
 399   public boolean isInRange(final SerialDate d1, final SerialDate d2,
 400                            final int include) {
 401     final int s1 = d1.toSerial();
 402     final int s2 = d2.toSerial();
 403     final int start = Math.min(s1, s2);
 404     final int end = Math.max(s1, s2);
 405 
 406     final int s = toSerial();
 407     if (include == SerialDate.INCLUDE_BOTH) {
 408       return (s >= start && s <= end);
 409     }
 410     else if (include == SerialDate.INCLUDE_FIRST) {
 411       return (s >= start && s < end);
 412     }
 413     else if (include == SerialDate.INCLUDE_SECOND) {
 414       return (s > start && s <= end);
 415     }
 416     else {
 417       return (s > start && s < end);
 418     }
 419   }
 420 
 421   /**
 422    * Calculate the serial number from the day, month and year.
 423    * <P>
 424    * 1-Jan-1900 = 2.
 425    *
 426    * @param d  the day.
 427    * @param m  the month.
 428    * @param y  the year.
 429    *
 430    * @return the serial number from the day, month and year.
 431    */
 432   private int calcSerial(final int d, final int m, final int y) {
 433     final int yy = ((y - 1900) * 365) + SerialDate.leapYearCount(y - 1);
 434     int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[m];
 435     if (m > MonthConstants.FEBRUARY) {
 436       if (SerialDate.isLeapYear(y)) {
 437           mm = mm + 1;
 438       }
 439     }
 440     final int dd = d;
 441     return yy + mm + dd + 1;
 442   }
 443 
 444   /**
 445    * Calculate the day, month and year from the serial number.
 446    */
 447   private void calcDayMonthYear() {
 448 
 449     // get the year from the serial date
 450     final int days = this.serial - SERIAL_LOWER_BOUND;
 451     // overestimated because we ignored leap days
 452     final int overestimatedYYYY = 1900 + (days / 365);
 453     final int leaps = SerialDate.leapYearCount(overestimatedYYYY);
 454     final int nonleapdays = days - leaps;
 455     // underestimated because we overestimated years
 456     int underestimatedYYYY = 1900 + (nonleapdays / 365);
 457 
 458     if (underestimatedYYYY == overestimatedYYYY) {
 459       this.year = underestimatedYYYY;
 460     }
 461     else {
 462       int ss1 = calcSerial(1, 1, underestimatedYYYY);
 463       while (ss1 <= this.serial) {
 464           underestimatedYYYY = underestimatedYYYY + 1;
 465           ss1 = calcSerial(1, 1, underestimatedYYYY);
 466       }
 467       this.year = underestimatedYYYY - 1;
 468     }
 469 
 470     final int ss2 = calcSerial(1, 1, this.year);
 471 
 472     int[] daysToEndOfPrecedingMonth
 473       = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
 474 
 475     if (isLeapYear(this.year)) {
 476       daysToEndOfPrecedingMonth
 477           = LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
 478     }
 479 
 480     // get the month from the serial date
 481     int mm = 1;
 482     int sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1;
 483     while (sss < this.serial) {
 484       mm = mm + 1;
 485       sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1;
 486     }
 487     this.month = mm - 1;
 488 
 489     // what's left is d(+1);
 490     this.day = this.serial - ss2
 491                - daysToEndOfPrecedingMonth[this.month] + 1;
 492 
 493   }
 494 
 495 }

代码清单B-6 RelativeDayOfWeekRule.java

   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  * 
   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  * RelativeDayOfWeekRule.java
  29  * --------------------------
  30  * (C) Copyright 2000-2003, by Object Refinery Limited and Contributors.
  31  *
  32  * Original Author:  David Gilbert (for Object Refinery Limited);
  33  * Contributor(s):   -;
  34  *
  35  * $Id: RelativeDayOfWeekRule.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  * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  41  *
  42  */
  43 
  44 package org.jfree.date;
  45 
  46 /**
  47  * An annual date rule that returns a date for each year based on (a) a
  48  * reference rule; (b) a day of the week; and (c) a selection parameter
  49  * (SerialDate.PRECEDING, SerialDate.NEAREST, SerialDate.FOLLOWING).
  50  * <P>
  51  * For example, Good Friday can be specified as 'the Friday PRECEDING Easter
  52  * Sunday'.
  53  *
  54  * @author David Gilbert
  55  */
  56 public class RelativeDayOfWeekRule extends AnnualDateRule {
  57 
  58   /** A reference to the annual date rule on which this rule is based. */
  59   private AnnualDateRule subrule;
  60 
  61   /**
  62    * The day of the week (SerialDate.MONDAY, SerialDate.TUESDAY, and so on).
  63    */
  64   private int dayOfWeek;
  65 
  66   /** Specifies which day of the week (PRECEDING, NEAREST or FOLLOWING). */
  67   private int relative;
  68 
  69   /**
  70    * Default constructor - builds a rule for the Monday following 1 January.
  71    */
  72   public RelativeDayOfWeekRule() {
  73     this(new DayAndMonthRule(), SerialDate.MONDAY, SerialDate.FOLLOWING);
  74   }
  75 
  76   /**
  77    * Standard constructor - builds rule based on the supplied sub-rule.
  78    *
  79    * @param subrule  the rule that determines the reference date.
  80    * @param dayOfWeek  the day-of-the-week relative to the reference date.
  81    * @param relative  indicates *which* day-of-the-week (preceding, nearest
  82    *                  or following).
  83    */
  84   public RelativeDayOfWeekRule(final AnnualDateRule subrule,
  85       final int dayOfWeek, final int relative) {
  86     this.subrule = subrule;
  87     this.dayOfWeek = dayOfWeek;
  88     this.relative = relative;
  89   }
  90 
  91   /**
  92    * Returns the sub-rule (also called the reference rule).
  93    *
  94    * @return The annual date rule that determines the reference date for this
  95    *         rule.
  96    */
  97   public AnnualDateRule getSubrule() {
  98     return this.subrule;
  99   }
 100 
 101   /**
 102    * Sets the sub-rule.
 103    *
 104    * @param subrule  the annual date rule that determines the reference date
 105    *                 for this rule.
 106    */
 107   public void setSubrule(final AnnualDateRule subrule) {
 108     this.subrule = subrule;
 109   }
 110 
 111   /**
 112    * Returns the day-of-the-week for this rule.
 113    *
 114    * @return the day-of-the-week for this rule.
 115    */
 116   public int getDayOfWeek() {
 117     return this.dayOfWeek;
 118   }
 119 
 120   /**
 121    * Sets the day-of-the-week for this rule.
 122    *
 123    * @param dayOfWeek the day-of-the-week (SerialDate.MONDAY,
 124    *                  SerialDate.TUESDAY, and so on).
 125    */
 126   public void setDayOfWeek(final int dayOfWeek) {
 127     this.dayOfWeek = dayOfWeek;
 128   }
 129 
 130   /**
 131    * Returns the 'relative' attribute, that determines *which*
 132    * day-of-the-week we are interested in (SerialDate.PRECEDING,
 133    * SerialDate.NEAREST or SerialDate.FOLLOWING).
 134    *
 135    * @return The 'relative' attribute.
 136    */
 137   public int getRelative() {
 138     return this.relative;
 139   }
 140 
 141   /**
 142    * Sets the 'relative' attribute (SerialDate.PRECEDING, SerialDate.NEAREST,
 143    * SerialDate.FOLLOWING).
 144    *
 145    * @param relative  determines *which* day-of-the-week is selected by this
 146    *                  rule.
 147    */
 148   public void setRelative(final int relative) {
 149     this.relative = relative;
 150   }
 151 
 152   /**
 153    * Creates a clone of this rule.
 154    *
 155    * @return a clone of this rule.
 156    *
 157    * @throws CloneNotSupportedException this should never happen.
 158    */
 159   public Object clone() throws CloneNotSupportedException {
 160     final RelativeDayOfWeekRule duplicate
 161         = (RelativeDayOfWeekRule) super.clone();
 162     duplicate.subrule = (AnnualDateRule) duplicate.getSubrule().clone();
 163     return duplicate;
 164   }
 165 
 166   /**
 167    * Returns the date generated by this rule, for the specified year.
 168    *
 169    * @param year  the year (1900 &lt;= year &lt;= 9999).
 170    *
 171    * @return The date generated by the rule for the given year (possibly
 172    *         <code>null</code>).
 173    */
 174   public SerialDate getDate(final int year) {
 175 
 176     // check argument...
 177     if ((year < SerialDate.MINIMUM_YEAR_SUPPORTED)
 178       || (year > SerialDate.MAXIMUM_YEAR_SUPPORTED)) {
 179       throw new IllegalArgumentException(
 180           "RelativeDayOfWeekRule.getDate(): year outside valid range.");
 181     }
 182 
 183     // calculate the date...
 184     SerialDate result = null;
 185     final SerialDate base = this.subrule.getDate(year);
 186 
 187     if (base != null) {
 188       switch (this.relative) {
 189         case(SerialDate.PRECEDING):
 190           result = SerialDate.getPreviousDayOfWeek(this.dayOfWeek, base);
 191                   
 192           break;
 193         case(SerialDate.NEAREST):
 194           result = SerialDate.getNearestDayOfWeek(this.dayOfWeek, base);
 195                   
 196           break;
 197         case(SerialDate.FOLLOWING):
 198           result = SerialDate.getFollowingDayOfWeek(this.dayOfWeek, base);
 199                 
 200           break;
 201         default:
 202           break;
 203       }
 204     }
 205     return result;
 206 
 207   }
 208 
 209 }

代码清单B-7 DayDate.java(最终版本)

   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.
   ...
  36  */
  37 package org.jfree.date;
  38 
  39 import java.io.Serializable;
  40 import java.util.*;
  41 
  42 /**
  43  * An abstract class that represents immutable dates with a precision of
  44  * one day.  The implementation will map each date to an integer that
  45  * represents an ordinal number of days from some fixed origin.
  46  *
  47  * Why not just use java.util.Date?  We will, when it makes sense.  At times,
  48  * java.util.Date can be *too* precise - it represents an instant in time,
  49  * accurate to 1/1000th of a second (with the date itself depending on the
  50  * time-zone).  Sometimes we just want to represent a particular day (e.g. 21
  51  * January 2015) without concerning ourselves about the time of day, or the
  52  * time-zone, or anything else.  That's what we've defined DayDate for.
  53  *
  54  * Use DayDateFactory.makeDate to create an instance.
  55  *
  56  * @author David Gilbert
  57  * @author Robert C. Martin did a lot of refactoring.
  58  */
  59 
  60 public abstract class DayDate implements Comparable, Serializable {
  61   public abstract int getOrdinalDay();
  62   public abstract int getYear();
  63   public abstract Month getMonth();
  64   public abstract int getDayOfMonth();
  65 
  66   protected abstract Day getDayOfWeekForOrdinalZero();
  67 
  68   public DayDate plusDays(int days) {
  69     return DayDateFactory.makeDate(getOrdinalDay() + days);
  70   }
  71 
  72   public DayDate plusMonths(int months) {
  73     int thisMonthAsOrdinal = getMonth().toInt() - Month.JANUARY.toInt();
  74     int thisMonthAndYearAsOrdinal = 12 * getYear() + thisMonthAsOrdinal;
  75     int resultMonthAndYearAsOrdinal = thisMonthAndYearAsOrdinal + months;
  76     int resultYear = resultMonthAndYearAsOrdinal / 12;
  77     int resultMonthAsOrdinal = resultMonthAndYearAsOrdinal % 12 + Month.JANUARY.toInt();
  78     Month resultMonth = Month.fromInt(resultMonthAsOrdinal);
  79     int resultDay = correctLastDayOfMonth(getDayOfMonth(), resultMonth, resultYear);
  80     return DayDateFactory.makeDate(resultDay, resultMonth, resultYear);
  81   }
  82 
  83   public DayDate plusYears(int years) {
  84     int resultYear = getYear() + years;
  85     int resultDay = correctLastDayOfMonth(getDayOfMonth(), getMonth(), resultYear);
  86     return DayDateFactory.makeDate(resultDay, getMonth(), resultYear);
  87   }
  88 
  89   private int correctLastDayOfMonth(int day, Month month, int year) {
  90     int lastDayOfMonth = DateUtil.lastDayOfMonth(month, year);
  91     if (day > lastDayOfMonth)
  92         day = lastDayOfMonth;
  93     return day;
  94   }
  95 
  96   public DayDate getPreviousDayOfWeek(Day targetDayOfWeek) {
  97     int offsetToTarget = targetDayOfWeek.toInt() - getDayOfWeek().toInt();
  98     if (offsetToTarget >= 0)
  99       offsetToTarget -= 7;
 100     return plusDays(offsetToTarget);
 101   }
 102 
 103   public DayDate getFollowingDayOfWeek(Day targetDayOfWeek) {
 104     int offsetToTarget = targetDayOfWeek.toInt() - getDayOfWeek().toInt();
 105     if (offsetToTarget <= 0)
 106       offsetToTarget += 7;
 107     return plusDays(offsetToTarget);
 108   }
 109 
 110   public DayDate getNearestDayOfWeek(Day targetDayOfWeek) {
 111     int offsetToThisWeeksTarget = targetDayOfWeek.toInt() - getDayOfWeek().toInt();
 112     int offsetToFutureTarget = (offsetToThisWeeksTarget + 7) % 7;
 113     int offsetToPreviousTarget = offsetToFutureTarget - 7;
 114 
 115     if (offsetToFutureTarget > 3)
 116       return plusDays(offsetToPreviousTarget);
 117     else
 118       return plusDays(offsetToFutureTarget);
 119   }
 120 
 121   public DayDate getEndOfMonth() {
 122     Month month = getMonth();
 123     int year = getYear();
 124     int lastDay = DateUtil.lastDayOfMonth(month, year);
 125     return DayDateFactory.makeDate(lastDay, month, year);
 126   }
 127 
 128   public Date toDate() {
 129     final Calendar calendar = Calendar.getInstance();
 130     int ordinalMonth = getMonth().toInt() - Month.JANUARY.toInt();
 131     calendar.set(getYear(), ordinalMonth, getDayOfMonth(), 0, 0, 0);
 132     return calendar.getTime();
 133   }
 134 
 135   public String toString() {
 136     return String.format("%02d-%s-%d", getDayOfMonth(), getMonth(), getYear());
 137   }
 138 
 139   public Day getDayOfWeek() {
 140     Day startingDay = getDayOfWeekForOrdinalZero();
 141     int startingOffset = startingDay.toInt() - Day.SUNDAY.toInt();
 142     int ordinalOfDayOfWeek = (getOrdinalDay() + startingOffset) % 7;
 143     return Day.fromInt(ordinalOfDayOfWeek + Day.SUNDAY.toInt());
 144   }
 145 
 146   public int daysSince(DayDate date) {
 147     return getOrdinalDay() - date.getOrdinalDay();
 148   }
 149 
 150   public boolean isOn(DayDate other) {
 151     return getOrdinalDay() == other.getOrdinalDay();
 152   }
 153 
 154   public boolean isBefore(DayDate other) {
 155     return getOrdinalDay() < other.getOrdinalDay();
 156   }
 157 
 158   public boolean isOnOrBefore(DayDate other) {
 159     return getOrdinalDay() <= other.getOrdinalDay();
 160   }
 161 
 162   public boolean isAfter(DayDate other) {
 163     return getOrdinalDay() > other.getOrdinalDay();
 164   }
 165 
 166   public boolean isOnOrAfter(DayDate other) {
 167     return getOrdinalDay() >= other.getOrdinalDay();
 168   }
 169 
 170   public boolean isInRange(DayDate d1, DayDate d2) {
 171     return isInRange(d1, d2, DateInterval.CLOSED);
 172   }
 173 
 174   public boolean isInRange(DayDate d1, DayDate d2, DateInterval interval) {
 175     int left = Math.min(d1.getOrdinalDay(), d2.getOrdinalDay());
 176     int right = Math.max(d1.getOrdinalDay(), d2.getOrdinalDay());
 177     return interval.isIn(getOrdinalDay(), left, right);
 178   }
 179 }

代码清单B-8 Month.java(最终版本)

   1 package org.jfree.date;
   2 
   3 import java.text.DateFormatSymbols;
   4 
   5 public enum Month {
   6   JANUARY(1), FEBRUARY(2), MARCH(3),
   7   APRIL(4),   MAY(5),      JUNE(6),
   8   JULY(7),    AUGUST(8),   SEPTEMBER(9),
   9   OCTOBER(10),NOVEMBER(11),DECEMBER(12);
  10   private static DateFormatSymbols dateFormatSymbols = new DateFormatSymbols();
  11   private static final int[] LAST_DAY_OF_MONTH =
  12     {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  13 
  14   private int index;
  15 
  16   Month(int index) {
  17     this.index = index;
  18   }
  19 
  20   public static Month fromInt(int monthIndex) {
  21     for (Month m : Month.values()) {
  22       if (m.index == monthIndex)
  23         return m;
  24     }
  25     throw new IllegalArgumentException("Invalid month index " + monthIndex);
  26   }
  27 
  28   public int lastDay() {
  29     return LAST_DAY_OF_MONTH[index];
  30   }
  31 
  32   public int quarter() {
  33     return 1 + (index - 1) / 3;
  34   }
  35 
  36   public String toString() {
  37     return dateFormatSymbols.getMonths()[index - 1];
  38   }
  39 
  40   public String toShortString() {
  41     return dateFormatSymbols.getShortMonths()[index - 1];
  42   }
  43 
  44   public static Month parse(String s) {
  45     s = s.trim();
  46     for (Month m : Month.values())
  47       if (m.matches(s))
  48         return m;
  49 
  50     try {
  51       return fromInt(Integer.parseInt(s));
  52     }
  53     catch (NumberFormatException e) {}
  54     throw new IllegalArgumentException("Invalid month " + s);
  55   }
  56 
  57   private boolean matches(String s) {
  58     return s.equalsIgnoreCase(toString()) ||
  59            s.equalsIgnoreCase(toShortString());
  60   }
  61 
  62   public int toInt() {
  63     return index;
  64   }
  65 }

代码清单B-9 Day.java(最终版本)

   1 package org.jfree.date;
   2 
   3 import java.util.Calendar;
   4 import java.text.DateFormatSymbols;
   5 
   6 public enum Day {
   7   MONDAY(Calendar.MONDAY),
   8   TUESDAY(Calendar.TUESDAY),
   9   WEDNESDAY(Calendar.WEDNESDAY),
  10   THURSDAY(Calendar.THURSDAY),
  11   FRIDAY(Calendar.FRIDAY),
  12   SATURDAY(Calendar.SATURDAY),
  13   SUNDAY(Calendar.SUNDAY);
  14 
  15   private final int index;
  16   private static DateFormatSymbols dateSymbols = new DateFormatSymbols();
  17 
  18   Day(int day) {
  19     index = day;
  20   }
  21 
  22   public static Day fromInt(int index) throws IllegalArgumentException {
  23     for (Day d : Day.values())
  24       if (d.index == index)
  25         return d;
  26     throw new IllegalArgumentException(
  27       String.format("Illegal day index: %d.", index));
  28   }
  29 
  30   public static Day parse(String s) throws IllegalArgumentException {
  31     String[] shortWeekdayNames =
  32       dateSymbols.getShortWeekdays();
  33     String[] weekDayNames =
  34       dateSymbols.getWeekdays();
  35 
  36     s = s.trim();
  37     for (Day day : Day.values()) {
  38       if (s.equalsIgnoreCase(shortWeekdayNames[day.index]) ||
  39           s.equalsIgnoreCase(weekDayNames[day.index])) {
  40         return day;
  41       }
  42     }
  43     throw new IllegalArgumentException(
  44       String.format("%s is not a valid weekday string", s));
  45   }
  46 
  47   public String toString() {
  48     return dateSymbols.getWeekdays()[index];
  49   }
  50 
  51   public int toInt() {
  52     return index;
  53   }
  54 }

代码清单B-10 DateInterval.java(最终版本)

   1 package org.jfree.date;
   2 
   3 public enum DateInterval {
   4   OPEN {
   5     public boolean isIn(int d, int left, int right) {
   6       return d > left && d < right;
   7     }
   8   },
   9   CLOSED_LEFT {
  10     public boolean isIn(int d, int left, int right) {
  11       return d >= left && d < right;
  12     }
  13   },
  14   CLOSED_RIGHT {
  15     public boolean isIn(int d, int left, int right) {
  16       return d > left && d <= right;
  17     }
  18   },
  19   CLOSED {
  20     public boolean isIn(int d, int left, int right) {
  21       return d >= left && d <= right;
  22     }
  23   };
  24 
  25   public abstract boolean isIn(int d, int left, int right);
  26 }

代码清单B-11 WeekInMonth.java(最终版本)

   1 package org.jfree.date;
   2 
   3 public enum WeekInMonth {
   4   FIRST(1), SECOND(2), THIRD(3), FOURTH(4), LAST(0);
   5   private final int index;
   6 
   7   WeekInMonth(int index) {
   8     this.index = index;
   9   }
  10 
  11   public int toInt() {
  12     return index;
  13   }
  14 }

代码清单B-12 WeekdayRange.java(最终版本)

   1 package org.jfree.date;
   2 
   3 public enum WeekdayRange {
   4   LAST, NEAREST, NEXT
   5 }

代码清单B-13 DateUtil.java(最终版本)

   1 package org.jfree.date;
   2 
   3 import java.text.DateFormatSymbols;
   4 
   5 public class DateUtil {
   6   private static DateFormatSymbols dateFormatSymbols = new DateFormatSymbols();
   7 
   8   public static String[] getMonthNames() {
   9     return dateFormatSymbols.getMonths();
  10   }
  11 
  12   public static boolean isLeapYear(int year) {
  13     boolean fourth = year % 4 == 0;
  14     boolean hundredth = year % 100 == 0;
  15     boolean fourHundredth = year % 400 == 0;
  16     return fourth && (!hundredth || fourHundredth);
  17   }
  18 
  19   public static int lastDayOfMonth(Month month, int year) {
  20     if (month == Month.FEBRUARY && isLeapYear(year))
  21       return month.lastDay() + 1;
  22     else
  23       return month.lastDay();
  24   }
  25 
  26   public static int leapYearCount(int year) {
  27     int leap4 = (year - 1896) / 4;
  28     int leap100 = (year - 1800) / 100;
  29     int leap400 = (year - 1600) / 400;
  30     return leap4 - leap100 + leap400;
  31   }
  32 }

代码清单B-14 DayDateFactory.java(最终版本)

   1 package org.jfree.date;
   2 
   3 public abstract class DayDateFactory {
   4   private static DayDateFactory factory = new SpreadsheetDateFactory();
   5   public static void setInstance(DayDateFactory factory) {
   6     DayDateFactory.factory = factory;
   7   }
   8 
   9   protected abstract DayDate _makeDate(int ordinal);
  10   protected abstract DayDate _makeDate(int day, Month month, int year);
  11   protected abstract DayDate _makeDate(int day, int month, int year);
  12   protected abstract DayDate _makeDate(java.util.Date date);
  13   protected abstract int _getMinimumYear();
  14   protected abstract int _getMaximumYear();
  15 
  16   public static DayDate makeDate(int ordinal) {
  17     return factory._makeDate(ordinal);
  18   }
  19 
  20   public static DayDate makeDate(int day, Month month, int year) {
  21     return factory._makeDate(day, month, year);
  22   }
  23 
  24   public static DayDate makeDate(int day, int month, int year) {
  25     return factory._makeDate(day, month, year);
  26   }
  27 
  28   public static DayDate makeDate(java.util.Date date) {
  29     return factory._makeDate(date);
  30   }
  31 
  32   public static int getMinimumYear() {
  33     return factory._getMinimumYear();
  34   }
  35 
  36   public static int getMaximumYear() {
  37     return factory._getMaximumYear();
  38   }
  39 }

代码清单B-15 SpreadsheetDateFactory.java(最终版本)

   1 package org.jfree.date;
   2 
   3 import java.util.*;
   4 
   5 public class SpreadsheetDateFactory extends DayDateFactory {
   6   public DayDate _makeDate(int ordinal) {
   7     return new SpreadsheetDate(ordinal);
   8   }
   9 
  10   public DayDate _makeDate(int day, Month month, int year) {
  11     return new SpreadsheetDate(day, month, year);
  12   }
  13 
  14   public DayDate _makeDate(int day, int month, int year) {
  15     return new SpreadsheetDate(day, month, year);
  16   }
  17 
  18   public DayDate _makeDate(Date date) {
  19     final GregorianCalendar calendar = new GregorianCalendar();
  20     calendar.setTime(date);
  21     return new SpreadsheetDate(
  22       calendar.get(Calendar.DATE),
  23       Month.fromInt(calendar.get(Calendar.MONTH) + 1),
  24       calendar.get(Calendar.YEAR));
  25   }
  26 
  27   protected int _getMinimumYear() {
  28     return SpreadsheetDate.MINIMUM_YEAR_SUPPORTED;
  29   }
  30 
  31   protected int _getMaximumYear() {
  32     return SpreadsheetDate.MAXIMUM_YEAR_SUPPORTED;
  33   }
  34 }

代码清单B-16 SpreadsheetDate.java(最终版本)

   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  * 
...
  52  *
  53  */
  54 
  55 package org.jfree.date;
  56 
  57 import static org.jfree.date.Month.FEBRUARY;
  58 
  59 import java.util.*;
  60 
  61 /**
  62  * Represents a date using an integer, in a similar fashion to the
  63  * implementation in Microsoft Excel.  The range of dates supported is
  64  * 1-Jan-1900 to 31-Dec-9999.
  65  * <p/>
  66  * Be aware that there is a deliberate bug in Excel that recognises the year
  67  * 1900 as a leap year when in fact it is not a leap year. You can find more
  68  * information on the Microsoft website in article Q181370:
  69  * <p/>
  70  * 
  71  * <p/>
  72  * Excel uses the convention that 1-Jan-1900 = 1.  This class uses the
  73  * convention 1-Jan-1900 = 2.
  74  * The result is that the day number in this class will be different to the
  75  * Excel figure for January and February 1900...but then Excel adds in an extra
  76  * day (29-Feb-1900 which does not actually exist!) and from that point forward
  77  * the day numbers will match.
  78  *
  79  * @author David Gilbert
  80  */
  81 public class SpreadsheetDate extends DayDate {
  82   public static final int EARLIEST_DATE_ORDINAL = 2;     // 1/1/1900
  83   public static final int LATEST_DATE_ORDINAL = 2958465; // 12/31/9999
  84   public static final int MINIMUM_YEAR_SUPPORTED = 1900;
  85   public static final int MAXIMUM_YEAR_SUPPORTED = 9999;
  86   static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
  87     {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
  88   static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
  89     {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
  90 
  91   private int ordinalDay;
  92   private int day;
  93   private Month month;
  94   private int year;
  95 
  96   public SpreadsheetDate(int day, Month month, int year) {
  97     if (year < MINIMUM_YEAR_SUPPORTED || year > MAXIMUM_YEAR_SUPPORTED)
  98       throw new IllegalArgumentException(
  99         "The 'year' argument must be in range " +
 100         MINIMUM_YEAR_SUPPORTED + " to " + MAXIMUM_YEAR_SUPPORTED + ".");
 101     if (day < 1 || day > DateUtil.lastDayOfMonth(month, year))
 102       throw new IllegalArgumentException("Invalid 'day' argument.");
 103 
 104     this.year = year;
 105     this.month = month;
 106     this.day = day;
 107     ordinalDay = calcOrdinal(day, month, year);
 108   }
 109 
 110   public SpreadsheetDate(int day, int month, int year) {
 111     this(day, Month.fromInt(month), year);
 112   }
 113 
 114   public SpreadsheetDate(int serial) {
 115     if (serial < EARLIEST_DATE_ORDINAL || serial > LATEST_DATE_ORDINAL)
 116       throw new IllegalArgumentException(
 117         "SpreadsheetDate: Serial must be in range 2 to 2958465.");
 118 
 119     ordinalDay = serial;
 120     calcDayMonthYear();
 121   }
 122 
 123   public int getOrdinalDay() {
 124     return ordinalDay;
 125   }
 126 
 127   public int getYear() {
 128     return year;
 129   }
 130 
 131   public Month getMonth() {
 132     return month;
 133   }
 134 
 135   public int getDayOfMonth() {
 136     return day;
 137   }
 138 
 139   protected Day getDayOfWeekForOrdinalZero() {return Day.SATURDAY;}
 140 
 141   public boolean equals(Object object) {
 142     if (!(object instanceof DayDate))
 143       return false;
 144 
 145     DayDate date = (DayDate) object;
 146     return date.getOrdinalDay() == getOrdinalDay();
 147   }
 148 
 149   public int hashCode() {
 150     return getOrdinalDay();
 151   }
 152 
 153   public int compareTo(Object other) {
 154     return daysSince((DayDate) other);
 155   }
 156 
 157   private int calcOrdinal(int day, Month month, int year) {
 158     int leapDaysForYear = DateUtil.leapYearCount(year - 1);
 159     int daysUpToYear = (year - MINIMUM_YEAR_SUPPORTED) * 365 + leapDaysForYear;
 160     int daysUpToMonth = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[month.toInt()];
 161     if (DateUtil.isLeapYear(year) && month.toInt() > FEBRUARY.toInt())
 162       daysUpToMonth++;
 163     int daysInMonth = day - 1;
 164     return daysUpToYear + daysUpToMonth + daysInMonth + EARLIEST_DATE_ORDINAL;
 165   }
 166 
 167   private void calcDayMonthYear() {
 168     int days = ordinalDay - EARLIEST_DATE_ORDINAL;
 169     int overestimatedYear = MINIMUM_YEAR_SUPPORTED + days / 365;
 170     int nonleapdays = days - DateUtil.leapYearCount(overestimatedYear);
 171     int underestimatedYear = MINIMUM_YEAR_SUPPORTED + nonleapdays / 365;
 172 
 173     year = huntForYearContaining(ordinalDay, underestimatedYear);
 174     int firstOrdinalOfYear = firstOrdinalOfYear(year);
 175     month = huntForMonthContaining(ordinalDay, firstOrdinalOfYear);
 176     day = ordinalDay - firstOrdinalOfYear - daysBeforeThisMonth(month.toInt());
 177   }
 178 
 179   private Month huntForMonthContaining(int anOrdinal, int firstOrdinalOfYear) {
 180     int daysIntoThisYear = anOrdinal - firstOrdinalOfYear;
 181     int aMonth = 1;
 182     while (daysBeforeThisMonth(aMonth) < daysIntoThisYear)
 183       aMonth++;
 184 
 185     return Month.fromInt(aMonth - 1);
 186   }
 187 
 188   private int daysBeforeThisMonth(int aMonth) {
 189     if (DateUtil.isLeapYear(year))
 190       return LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[aMonth] - 1;
 191     else
 192       return AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[aMonth] - 1;
 193   }
 194 
 195   private int huntForYearContaining(int anOrdinalDay, int startingYear) {
 196     int aYear = startingYear;
 197     while (firstOrdinalOfYear(aYear) <= anOrdinalDay)
 198       aYear++;
 199 
 200     return aYear - 1;
 201   }
 202 
 203   private int firstOrdinalOfYear(int year) {
 204     return calcOrdinal(1, Month.JANUARY, year);
 205   }
 206 
 207   public static DayDate createInstance(Date date) {
 208     GregorianCalendar calendar = new GregorianCalendar();
 209     calendar.setTime(date);
 210     return new SpreadsheetDate(calendar.get(Calendar.DATE),
 211                                Month.fromInt(calendar.get(Calendar.MONTH) + 1),
 212                                calendar.get(Calendar.YEAR));
 213 
 214   }
 215 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值