Чистый код. Создание, анализ и рефакторинг — страница 84 из 94

  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).