001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------------- 028 * SerialDate.java 029 * --------------- 030 * (C) Copyright 2006-present, by David Gilbert and Contributors. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.chart.date; 038 039import java.io.Serializable; 040import java.text.DateFormatSymbols; 041import java.text.SimpleDateFormat; 042import java.util.Calendar; 043import java.util.GregorianCalendar; 044 045/** 046 * An abstract class that defines our requirements for manipulating dates, 047 * without tying down a particular implementation. 048 * <P> 049 * Requirement 1 : match at least what Excel does for dates; 050 * Requirement 2 : the date represented by the class is immutable; 051 * <P> 052 * Why not just use java.util.Date? We will, when it makes sense. At times, 053 * java.util.Date can be *too* precise - it represents an instant in time, 054 * accurate to 1/1000th of a second (with the date itself depending on the 055 * time-zone). Sometimes we just want to represent a particular day (e.g. 21 056 * January 2015) without concerning ourselves about the time of day, or the 057 * time-zone, or anything else. That's what we've defined SerialDate for. 058 * <P> 059 * You can call getInstance() to get a concrete subclass of SerialDate, 060 * without worrying about the exact implementation. 061 */ 062public abstract class SerialDate implements Comparable, Serializable, 063 MonthConstants { 064 065 /** For serialization. */ 066 private static final long serialVersionUID = -293716040467423637L; 067 068 /** Date format symbols. */ 069 public static final DateFormatSymbols 070 DATE_FORMAT_SYMBOLS = new SimpleDateFormat().getDateFormatSymbols(); 071 072 /** The serial number for 1 January 1900. */ 073 public static final int SERIAL_LOWER_BOUND = 2; 074 075 /** The serial number for 31 December 9999. */ 076 public static final int SERIAL_UPPER_BOUND = 2958465; 077 078 /** The lowest year value supported by this date format. */ 079 public static final int MINIMUM_YEAR_SUPPORTED = 1900; 080 081 /** The highest year value supported by this date format. */ 082 public static final int MAXIMUM_YEAR_SUPPORTED = 9999; 083 084 /** Useful constant for Monday. Equivalent to java.util.Calendar.MONDAY. */ 085 public static final int MONDAY = Calendar.MONDAY; 086 087 /** 088 * Useful constant for Tuesday. Equivalent to java.util.Calendar.TUESDAY. 089 */ 090 public static final int TUESDAY = Calendar.TUESDAY; 091 092 /** 093 * Useful constant for Wednesday. Equivalent to 094 * java.util.Calendar.WEDNESDAY. 095 */ 096 public static final int WEDNESDAY = Calendar.WEDNESDAY; 097 098 /** 099 * Useful constant for Thrusday. Equivalent to java.util.Calendar.THURSDAY. 100 */ 101 public static final int THURSDAY = Calendar.THURSDAY; 102 103 /** Useful constant for Friday. Equivalent to java.util.Calendar.FRIDAY. */ 104 public static final int FRIDAY = Calendar.FRIDAY; 105 106 /** 107 * Useful constant for Saturday. Equivalent to java.util.Calendar.SATURDAY. 108 */ 109 public static final int SATURDAY = Calendar.SATURDAY; 110 111 /** Useful constant for Sunday. Equivalent to java.util.Calendar.SUNDAY. */ 112 public static final int SUNDAY = Calendar.SUNDAY; 113 114 /** The number of days in each month in non leap years. */ 115 static final int[] LAST_DAY_OF_MONTH = 116 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 117 118 /** The number of days in a (non-leap) year up to the end of each month. */ 119 static final int[] AGGREGATE_DAYS_TO_END_OF_MONTH = 120 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; 121 122 /** The number of days in a year up to the end of the preceding month. */ 123 static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = 124 {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; 125 126 /** The number of days in a leap year up to the end of each month. */ 127 static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH = 128 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; 129 130 /** 131 * The number of days in a leap year up to the end of the preceding month. 132 */ 133 static final int[] 134 LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = 135 {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; 136 137 /** A useful constant for referring to the first week in a month. */ 138 public static final int FIRST_WEEK_IN_MONTH = 1; 139 140 /** A useful constant for referring to the second week in a month. */ 141 public static final int SECOND_WEEK_IN_MONTH = 2; 142 143 /** A useful constant for referring to the third week in a month. */ 144 public static final int THIRD_WEEK_IN_MONTH = 3; 145 146 /** A useful constant for referring to the fourth week in a month. */ 147 public static final int FOURTH_WEEK_IN_MONTH = 4; 148 149 /** A useful constant for referring to the last week in a month. */ 150 public static final int LAST_WEEK_IN_MONTH = 0; 151 152 /** Useful range constant. */ 153 public static final int INCLUDE_NONE = 0; 154 155 /** Useful range constant. */ 156 public static final int INCLUDE_FIRST = 1; 157 158 /** Useful range constant. */ 159 public static final int INCLUDE_SECOND = 2; 160 161 /** Useful range constant. */ 162 public static final int INCLUDE_BOTH = 3; 163 164 /** 165 * Useful constant for specifying a day of the week relative to a fixed 166 * date. 167 */ 168 public static final int PRECEDING = -1; 169 170 /** 171 * Useful constant for specifying a day of the week relative to a fixed 172 * date. 173 */ 174 public static final int NEAREST = 0; 175 176 /** 177 * Useful constant for specifying a day of the week relative to a fixed 178 * date. 179 */ 180 public static final int FOLLOWING = 1; 181 182 /** A description for the date. */ 183 private String description; 184 185 /** 186 * Default constructor. 187 */ 188 protected SerialDate() { 189 } 190 191 /** 192 * Returns {@code true} if the supplied integer code represents a 193 * valid day-of-the-week, and {@code false} otherwise. 194 * 195 * @param code the code being checked for validity. 196 * 197 * @return {@code true} if the supplied integer code represents a 198 * valid day-of-the-week, and {@code false} otherwise. 199 */ 200 public static boolean isValidWeekdayCode(int code) { 201 202 switch(code) { 203 case SUNDAY: 204 case MONDAY: 205 case TUESDAY: 206 case WEDNESDAY: 207 case THURSDAY: 208 case FRIDAY: 209 case SATURDAY: 210 return true; 211 default: 212 return false; 213 } 214 215 } 216 217 /** 218 * Converts the supplied string to a day of the week. 219 * 220 * @param s a string representing the day of the week. 221 * 222 * @return {@code -1} if the string is not convertable, the day of 223 * the week otherwise. 224 */ 225 public static int stringToWeekdayCode(String s) { 226 227 final String[] shortWeekdayNames 228 = DATE_FORMAT_SYMBOLS.getShortWeekdays(); 229 final String[] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays(); 230 231 int result = -1; 232 s = s.trim(); 233 for (int i = 0; i < weekDayNames.length; i++) { 234 if (s.equals(shortWeekdayNames[i])) { 235 result = i; 236 break; 237 } 238 if (s.equals(weekDayNames[i])) { 239 result = i; 240 break; 241 } 242 } 243 return result; 244 245 } 246 247 /** 248 * Returns a string representing the supplied day-of-the-week. 249 * <P> 250 * Need to find a better approach. 251 * 252 * @param weekday the day of the week. 253 * 254 * @return a string representing the supplied day-of-the-week. 255 */ 256 public static String weekdayCodeToString(int weekday) { 257 final String[] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays(); 258 return weekdays[weekday]; 259 } 260 261 /** 262 * Returns an array of month names. 263 * 264 * @return an array of month names. 265 */ 266 public static String[] getMonths() { 267 268 return getMonths(false); 269 270 } 271 272 /** 273 * Returns an array of month names. 274 * 275 * @param shortened a flag indicating that shortened month names should 276 * be returned. 277 * 278 * @return an array of month names. 279 */ 280 public static String[] getMonths(boolean shortened) { 281 if (shortened) { 282 return DATE_FORMAT_SYMBOLS.getShortMonths(); 283 } 284 else { 285 return DATE_FORMAT_SYMBOLS.getMonths(); 286 } 287 } 288 289 /** 290 * Returns true if the supplied integer code represents a valid month. 291 * 292 * @param code the code being checked for validity. 293 * 294 * @return {@code true} if the supplied integer code represents a 295 * valid month. 296 */ 297 public static boolean isValidMonthCode(int code) { 298 299 switch(code) { 300 case JANUARY: 301 case FEBRUARY: 302 case MARCH: 303 case APRIL: 304 case MAY: 305 case JUNE: 306 case JULY: 307 case AUGUST: 308 case SEPTEMBER: 309 case OCTOBER: 310 case NOVEMBER: 311 case DECEMBER: 312 return true; 313 default: 314 return false; 315 } 316 317 } 318 319 /** 320 * Returns the quarter for the specified month. 321 * 322 * @param code the month code (1-12). 323 * 324 * @return the quarter that the month belongs to. 325 */ 326 public static int monthCodeToQuarter(int code) { 327 328 switch(code) { 329 case JANUARY: 330 case FEBRUARY: 331 case MARCH: return 1; 332 case APRIL: 333 case MAY: 334 case JUNE: return 2; 335 case JULY: 336 case AUGUST: 337 case SEPTEMBER: return 3; 338 case OCTOBER: 339 case NOVEMBER: 340 case DECEMBER: return 4; 341 default: throw new IllegalArgumentException( 342 "SerialDate.monthCodeToQuarter: invalid month code."); 343 } 344 345 } 346 347 /** 348 * Returns a string representing the supplied month. 349 * <P> 350 * The string returned is the long form of the month name taken from the 351 * default locale. 352 * 353 * @param month the month. 354 * 355 * @return a string representing the supplied month. 356 */ 357 public static String monthCodeToString(int month) { 358 return monthCodeToString(month, false); 359 } 360 361 /** 362 * Returns a string representing the supplied month. 363 * <P> 364 * The string returned is the long or short form of the month name taken 365 * from the default locale. 366 * 367 * @param month the month. 368 * @param shortened if {@code true} return the abbreviation of the month. 369 * 370 * @return a string representing the supplied month. 371 */ 372 public static String monthCodeToString(int month, boolean shortened) { 373 374 // check arguments... 375 if (!isValidMonthCode(month)) { 376 throw new IllegalArgumentException( 377 "SerialDate.monthCodeToString: month outside valid range."); 378 } 379 380 final String[] months; 381 382 if (shortened) { 383 months = DATE_FORMAT_SYMBOLS.getShortMonths(); 384 } 385 else { 386 months = DATE_FORMAT_SYMBOLS.getMonths(); 387 } 388 389 return months[month - 1]; 390 391 } 392 393 /** 394 * Converts a string to a month code. 395 * <P> 396 * This method will return one of the constants JANUARY, FEBRUARY, ..., 397 * DECEMBER that corresponds to the string. If the string is not 398 * recognised, this method returns -1. 399 * 400 * @param s the string to parse. 401 * 402 * @return {@code -1} if the string is not parseable, the month of the 403 * year otherwise. 404 */ 405 public static int stringToMonthCode(String s) { 406 407 final String[] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths(); 408 final String[] monthNames = DATE_FORMAT_SYMBOLS.getMonths(); 409 410 int result = -1; 411 s = s.trim(); 412 413 // first try parsing the string as an integer (1-12)... 414 try { 415 result = Integer.parseInt(s); 416 } 417 catch (NumberFormatException e) { 418 // suppress 419 } 420 421 // now search through the month names... 422 if ((result < 1) || (result > 12)) { 423 for (int i = 0; i < monthNames.length; i++) { 424 if (s.equals(shortMonthNames[i])) { 425 result = i + 1; 426 break; 427 } 428 if (s.equals(monthNames[i])) { 429 result = i + 1; 430 break; 431 } 432 } 433 } 434 435 return result; 436 437 } 438 439 /** 440 * Returns true if the supplied integer code represents a valid 441 * week-in-the-month, and false otherwise. 442 * 443 * @param code the code being checked for validity. 444 * @return {@code true} if the supplied integer code represents a 445 * valid week-in-the-month. 446 */ 447 public static boolean isValidWeekInMonthCode(int code) { 448 switch(code) { 449 case FIRST_WEEK_IN_MONTH: 450 case SECOND_WEEK_IN_MONTH: 451 case THIRD_WEEK_IN_MONTH: 452 case FOURTH_WEEK_IN_MONTH: 453 case LAST_WEEK_IN_MONTH: return true; 454 default: return false; 455 } 456 } 457 458 /** 459 * Determines whether or not the specified year is a leap year. 460 * 461 * @param yyyy the year (in the range 1900 to 9999). 462 * 463 * @return {@code true} if the specified year is a leap year. 464 */ 465 public static boolean isLeapYear(int yyyy) { 466 467 if ((yyyy % 4) != 0) { 468 return false; 469 } 470 else if ((yyyy % 400) == 0) { 471 return true; 472 } 473 else if ((yyyy % 100) == 0) { 474 return false; 475 } 476 else { 477 return true; 478 } 479 480 } 481 482 /** 483 * Returns the number of leap years from 1900 to the specified year 484 * INCLUSIVE. 485 * <P> 486 * Note that 1900 is not a leap year. 487 * 488 * @param yyyy the year (in the range 1900 to 9999). 489 * 490 * @return the number of leap years from 1900 to the specified year. 491 */ 492 public static int leapYearCount(int yyyy) { 493 int leap4 = (yyyy - 1896) / 4; 494 int leap100 = (yyyy - 1800) / 100; 495 int leap400 = (yyyy - 1600) / 400; 496 return leap4 - leap100 + leap400; 497 } 498 499 /** 500 * Returns the number of the last day of the month, taking into account 501 * leap years. 502 * 503 * @param month the month. 504 * @param yyyy the year (in the range 1900 to 9999). 505 * 506 * @return the number of the last day of the month. 507 */ 508 public static int lastDayOfMonth(int month, int yyyy) { 509 510 final int result = LAST_DAY_OF_MONTH[month]; 511 if (month != FEBRUARY) { 512 return result; 513 } 514 else if (isLeapYear(yyyy)) { 515 return result + 1; 516 } 517 else { 518 return result; 519 } 520 521 } 522 523 /** 524 * Creates a new date by adding the specified number of days to the base 525 * date. 526 * 527 * @param days the number of days to add (can be negative). 528 * @param base the base date. 529 * 530 * @return a new date. 531 */ 532 public static SerialDate addDays(int days, SerialDate base) { 533 int serialDayNumber = base.toSerial() + days; 534 return SerialDate.createInstance(serialDayNumber); 535 } 536 537 /** 538 * Creates a new date by adding the specified number of months to the base 539 * date. 540 * <P> 541 * If the base date is close to the end of the month, the day on the result 542 * may be adjusted slightly: 31 May + 1 month = 30 June. 543 * 544 * @param months the number of months to add (can be negative). 545 * @param base the base date. 546 * 547 * @return a new date. 548 */ 549 public static SerialDate addMonths(int months, SerialDate base) { 550 int yy = (12 * base.getYYYY() + base.getMonth() + months - 1) / 12; 551 int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1; 552 int dd = Math.min(base.getDayOfMonth(), 553 SerialDate.lastDayOfMonth(mm, yy)); 554 return SerialDate.createInstance(dd, mm, yy); 555 } 556 557 /** 558 * Creates a new date by adding the specified number of years to the base 559 * date. 560 * 561 * @param years the number of years to add (can be negative). 562 * @param base the base date. 563 * 564 * @return A new date. 565 */ 566 public static SerialDate addYears(int years, SerialDate base) { 567 int baseY = base.getYYYY(); 568 int baseM = base.getMonth(); 569 int baseD = base.getDayOfMonth(); 570 571 int targetY = baseY + years; 572 int targetD = Math.min(baseD, SerialDate.lastDayOfMonth(baseM, targetY)); 573 return SerialDate.createInstance(targetD, baseM, targetY); 574 } 575 576 /** 577 * Returns the latest date that falls on the specified day-of-the-week and 578 * is BEFORE the base date. 579 * 580 * @param targetWeekday a code for the target day-of-the-week. 581 * @param base the base date. 582 * 583 * @return the latest date that falls on the specified day-of-the-week and 584 * is BEFORE the base date. 585 */ 586 public static SerialDate getPreviousDayOfWeek(int targetWeekday, 587 SerialDate base) { 588 589 // check arguments... 590 if (!SerialDate.isValidWeekdayCode(targetWeekday)) { 591 throw new IllegalArgumentException("Invalid day-of-the-week code."); 592 } 593 594 // find the date... 595 int adjust; 596 int baseDOW = base.getDayOfWeek(); 597 if (baseDOW > targetWeekday) { 598 adjust = Math.min(0, targetWeekday - baseDOW); 599 } else { 600 adjust = -7 + Math.max(0, targetWeekday - baseDOW); 601 } 602 603 return SerialDate.addDays(adjust, base); 604 605 } 606 607 /** 608 * Returns the earliest date that falls on the specified day-of-the-week 609 * and is AFTER the base date. 610 * 611 * @param targetWeekday a code for the target day-of-the-week. 612 * @param base the base date. 613 * 614 * @return the earliest date that falls on the specified day-of-the-week 615 * and is AFTER the base date. 616 */ 617 public static SerialDate getFollowingDayOfWeek(int targetWeekday, 618 SerialDate base) { 619 620 // check arguments... 621 if (!SerialDate.isValidWeekdayCode(targetWeekday)) { 622 throw new IllegalArgumentException( 623 "Invalid day-of-the-week code." 624 ); 625 } 626 627 // find the date... 628 int adjust; 629 int baseDOW = base.getDayOfWeek(); 630 if (baseDOW > targetWeekday) { 631 adjust = 7 + Math.min(0, targetWeekday - baseDOW); 632 } else { 633 adjust = Math.max(0, targetWeekday - baseDOW); 634 } 635 636 return SerialDate.addDays(adjust, base); 637 } 638 639 /** 640 * Returns the date that falls on the specified day-of-the-week and is 641 * CLOSEST to the base date. 642 * 643 * @param targetDOW a code for the target day-of-the-week. 644 * @param base the base date. 645 * 646 * @return the date that falls on the specified day-of-the-week and is 647 * CLOSEST to the base date. 648 */ 649 public static SerialDate getNearestDayOfWeek(int targetDOW, SerialDate base) { 650 651 // check arguments... 652 if (!SerialDate.isValidWeekdayCode(targetDOW)) { 653 throw new IllegalArgumentException("Invalid day-of-the-week code."); 654 } 655 656 // find the date... 657 final int baseDOW = base.getDayOfWeek(); 658 int adjust = -Math.abs(targetDOW - baseDOW); 659 if (adjust >= 4) { 660 adjust = 7 - adjust; 661 } 662 if (adjust <= -4) { 663 adjust = 7 + adjust; 664 } 665 return SerialDate.addDays(adjust, base); 666 667 } 668 669 /** 670 * Rolls the date forward to the last day of the month. 671 * 672 * @param base the base date. 673 * 674 * @return a new serial date. 675 */ 676 public SerialDate getEndOfCurrentMonth(SerialDate base) { 677 int last = SerialDate.lastDayOfMonth(base.getMonth(), base.getYYYY()); 678 return SerialDate.createInstance(last, base.getMonth(), base.getYYYY()); 679 } 680 681 /** 682 * Returns a string corresponding to the week-in-the-month code. 683 * <P> 684 * Need to find a better approach. 685 * 686 * @param count an integer code representing the week-in-the-month. 687 * 688 * @return a string corresponding to the week-in-the-month code. 689 */ 690 public static String weekInMonthToString(int count) { 691 692 switch (count) { 693 case SerialDate.FIRST_WEEK_IN_MONTH : return "First"; 694 case SerialDate.SECOND_WEEK_IN_MONTH : return "Second"; 695 case SerialDate.THIRD_WEEK_IN_MONTH : return "Third"; 696 case SerialDate.FOURTH_WEEK_IN_MONTH : return "Fourth"; 697 case SerialDate.LAST_WEEK_IN_MONTH : return "Last"; 698 default : 699 return "SerialDate.weekInMonthToString(): invalid code."; 700 } 701 702 } 703 704 /** 705 * Returns a string representing the supplied 'relative'. 706 * <P> 707 * Need to find a better approach. 708 * 709 * @param relative a constant representing the 'relative'. 710 * 711 * @return a string representing the supplied 'relative'. 712 */ 713 public static String relativeToString(int relative) { 714 715 switch (relative) { 716 case SerialDate.PRECEDING : return "Preceding"; 717 case SerialDate.NEAREST : return "Nearest"; 718 case SerialDate.FOLLOWING : return "Following"; 719 default : return "ERROR : Relative To String"; 720 } 721 722 } 723 724 /** 725 * Factory method that returns an instance of some concrete subclass of 726 * {@link SerialDate}. 727 * 728 * @param day the day (1-31). 729 * @param month the month (1-12). 730 * @param yyyy the year (in the range 1900 to 9999). 731 * 732 * @return An instance of {@link SerialDate}. 733 */ 734 public static SerialDate createInstance(int day, int month, int yyyy) { 735 return new SpreadsheetDate(day, month, yyyy); 736 } 737 738 /** 739 * Factory method that returns an instance of some concrete subclass of 740 * {@link SerialDate}. 741 * 742 * @param serial the serial number for the day (1 January 1900 = 2). 743 * 744 * @return a instance of SerialDate. 745 */ 746 public static SerialDate createInstance(int serial) { 747 return new SpreadsheetDate(serial); 748 } 749 750 /** 751 * Factory method that returns an instance of a subclass of SerialDate. 752 * 753 * @param date A Java date object. 754 * 755 * @return a instance of SerialDate. 756 */ 757 public static SerialDate createInstance(java.util.Date date) { 758 759 GregorianCalendar calendar = new GregorianCalendar(); 760 calendar.setTime(date); 761 return new SpreadsheetDate(calendar.get(Calendar.DATE), 762 calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.YEAR)); 763 764 } 765 766 /** 767 * Returns the serial number for the date, where 1 January 1900 = 2 (this 768 * corresponds, almost, to the numbering system used in Microsoft Excel for 769 * Windows and Lotus 1-2-3). 770 * 771 * @return the serial number for the date. 772 */ 773 public abstract int toSerial(); 774 775 /** 776 * Returns a java.util.Date. Since java.util.Date has more precision than 777 * SerialDate, we need to define a convention for the 'time of day'. 778 * 779 * @return this as {@code java.util.Date}. 780 */ 781 public abstract java.util.Date toDate(); 782 783 /** 784 * Returns the description that is attached to the date. It is not 785 * required that a date have a description, but for some applications it 786 * is useful. 787 * 788 * @return The description (possibly {@code null}). 789 */ 790 public String getDescription() { 791 return this.description; 792 } 793 794 /** 795 * Sets the description for the date. 796 * 797 * @param description the description for this date ({@code null} 798 * permitted). 799 */ 800 public void setDescription(String description) { 801 this.description = description; 802 } 803 804 /** 805 * Converts the date to a string. 806 * 807 * @return a string representation of the date. 808 */ 809 @Override 810 public String toString() { 811 return getDayOfMonth() + "-" + SerialDate.monthCodeToString(getMonth()) 812 + "-" + getYYYY(); 813 } 814 815 /** 816 * Returns the year (assume a valid range of 1900 to 9999). 817 * 818 * @return the year. 819 */ 820 public abstract int getYYYY(); 821 822 /** 823 * Returns the month (January = 1, February = 2, March = 3). 824 * 825 * @return the month of the year. 826 */ 827 public abstract int getMonth(); 828 829 /** 830 * Returns the day of the month. 831 * 832 * @return the day of the month. 833 */ 834 public abstract int getDayOfMonth(); 835 836 /** 837 * Returns the day of the week. 838 * 839 * @return the day of the week. 840 */ 841 public abstract int getDayOfWeek(); 842 843 /** 844 * Returns the difference (in days) between this date and the specified 845 * 'other' date. 846 * <P> 847 * The result is positive if this date is after the 'other' date and 848 * negative if it is before the 'other' date. 849 * 850 * @param other the date being compared to. 851 * 852 * @return the difference between this and the other date. 853 */ 854 public abstract int compare(SerialDate other); 855 856 /** 857 * Returns true if this SerialDate represents the same date as the 858 * specified SerialDate. 859 * 860 * @param other the date being compared to. 861 * 862 * @return {@code true} if this SerialDate represents the same date as 863 * the specified SerialDate. 864 */ 865 public abstract boolean isOn(SerialDate other); 866 867 /** 868 * Returns true if this SerialDate represents an earlier date compared to 869 * the specified SerialDate. 870 * 871 * @param other The date being compared to. 872 * 873 * @return {@code true} if this SerialDate represents an earlier date 874 * compared to the specified SerialDate. 875 */ 876 public abstract boolean isBefore(SerialDate other); 877 878 /** 879 * Returns true if this SerialDate represents the same date as the 880 * specified SerialDate. 881 * 882 * @param other the date being compared to. 883 * 884 * @return {@code true} if this SerialDate represents the same date 885 * as the specified SerialDate. 886 */ 887 public abstract boolean isOnOrBefore(SerialDate other); 888 889 /** 890 * Returns true if this SerialDate represents the same date as the 891 * specified SerialDate. 892 * 893 * @param other the date being compared to. 894 * 895 * @return {@code true} if this SerialDate represents the same date 896 * as the specified SerialDate. 897 */ 898 public abstract boolean isAfter(SerialDate other); 899 900 /** 901 * Returns true if this SerialDate represents the same date as the 902 * specified SerialDate. 903 * 904 * @param other the date being compared to. 905 * 906 * @return {@code true} if this SerialDate represents the same date 907 * as the specified SerialDate. 908 */ 909 public abstract boolean isOnOrAfter(SerialDate other); 910 911 /** 912 * Returns {@code true} if this {@link SerialDate} is within the 913 * specified range (INCLUSIVE). The date order of d1 and d2 is not 914 * important. 915 * 916 * @param d1 a boundary date for the range. 917 * @param d2 the other boundary date for the range. 918 * 919 * @return A boolean. 920 */ 921 public abstract boolean isInRange(SerialDate d1, SerialDate d2); 922 923 /** 924 * Returns {@code true} if this {@link SerialDate} is within the 925 * specified range (caller specifies whether or not the end-points are 926 * included). The date order of d1 and d2 is not important. 927 * 928 * @param d1 a boundary date for the range. 929 * @param d2 the other boundary date for the range. 930 * @param include a code that controls whether or not the start and end 931 * dates are included in the range. 932 * 933 * @return A boolean. 934 */ 935 public abstract boolean isInRange(SerialDate d1, SerialDate d2, 936 int include); 937 938 /** 939 * Returns the latest date that falls on the specified day-of-the-week and 940 * is BEFORE this date. 941 * 942 * @param targetDOW a code for the target day-of-the-week. 943 * 944 * @return the latest date that falls on the specified day-of-the-week and 945 * is BEFORE this date. 946 */ 947 public SerialDate getPreviousDayOfWeek(int targetDOW) { 948 return getPreviousDayOfWeek(targetDOW, this); 949 } 950 951 /** 952 * Returns the earliest date that falls on the specified day-of-the-week 953 * and is AFTER this date. 954 * 955 * @param targetDOW a code for the target day-of-the-week. 956 * 957 * @return the earliest date that falls on the specified day-of-the-week 958 * and is AFTER this date. 959 */ 960 public SerialDate getFollowingDayOfWeek(int targetDOW) { 961 return getFollowingDayOfWeek(targetDOW, this); 962 } 963 964 /** 965 * Returns the nearest date that falls on the specified day-of-the-week. 966 * 967 * @param targetDOW a code for the target day-of-the-week. 968 * 969 * @return the nearest date that falls on the specified day-of-the-week. 970 */ 971 public SerialDate getNearestDayOfWeek(int targetDOW) { 972 return getNearestDayOfWeek(targetDOW, this); 973 } 974 975} 976