59
60 /**
61 * Представляет дату с использованием целого числа, по аналогии с реализацией
62 * в Microsoft Excel. Поддерживаемый диапазон дат:
63 * с 1 января 1900 по 31 декабря 9999.
64 *
65 * Учтите, что в Excel существует намеренная ошибка, вследствие которой год
66 * 1900 считается високосным, тогда как в действительности он таковым не является.
67 * Дополнительная информация приведена на сайте Microsoft в статье Q181370:
68 *
69 * http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
70 *
71 * Excel считает, что 1 января 1900 = 1. Этот класс считает, что
72 * 1 января 1900 = 2.
73 * В результате номер дня этого класса будет отличаться от номера Excel
74 * в январе и феврале 1900...но затем Excel прибавляет лишний день
75 * (29 февраля 1900, который в действительности не существует!), и с этого
76 * момента нумерация дней совпадает.
77 *
78 * @author Дэвид Гилберт
79 */
80 public class SpreadsheetDate extends SerialDate {
81
82 /** Для сериализации. */
83 private static final long serialVersionUID = -2039586705374454461L;
84
85 /**
Листинг Б.5 (продолжение)
86 * Номер дня (1.01.1900 = 2, 2.01.1900 = 3, ..., 31.12.9999 =
87 * 2958465).
88 */
89 private int serial;
90
91 /** День месяца (от 1 до 28, 29, 30 или 31 в зависимости от месяца). */
92 private int day;
93
94 /** Месяц года (от 1 по 12). */
95 private int month;
96
97 /** Год (от 1900 до 9999). */
98 private int year;
99
100 /** Необязательное описание даты. */
101 private String description;
102
103 /**
104 * Создает новый экземпляр даты.
105 *
106 * @param day день (в диапазоне от 1 до 28/29/30/31).
107 * @param month месяц (в диапазоне от 1 до 12).
108 * @param year год (в диапазоне от 1900 до 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 // Порядковый номер должен синхронизироваться с днем-месяцем-годом...
139 this.serial = calcSerial(day, month, year);
140
141 this.description = null;
142
143 }
144
145 /**
146 * Стандартный конструктор - создает новый объект даты, представляющий
147 * день с заданным номером (в диапазоне от 2 до 2958465).
148 *
149 * @param serial порядковый номер дня (диапазон: от 2 до 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 // День-месяц-год должен синхронизироваться с порядковым номером...
162 calcDayMonthYear();
163
164 }
165
166 /**
167 * Возвращает описание, присоединенное к дате.
168 * Дата не обязана иметь описание, но в некоторых приложениях
169 * оно может оказаться полезным.
170 *
171 * @return описание, присоединенное к дате.
172 */
173 public String getDescription() {
174 return this.description;
175 }
176
177 /**
178 * Задает описание для даты.
179 *
180 * @param description описание даты (разрешается
181 * null
).
182 */
183 public void setDescription(final String description) {
184 this.description = description;
185 }
186
Листинг Б.5 (продолжение)
187 /**
188 * Возвращает порядковый номер даты, где 1 января 1900 = 2
189 * (что почти соответствует системе нумерации, используемой в Microsoft
190 * Excel for Windows и Lotus 1-2-3).