1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package java.sql;
19
20/**
21 * A class which can consume and produce dates in SQL {@code Date} format.
22 * <p>
23 * Dates are represented in SQL as {@code yyyy-MM-dd}. Note that this date
24 * format only deals with year, month and day values. There are no values for
25 * hours, minutes, seconds.
26 * <p>
27 * This is unlike the familiar {@code java.util.Date} object, which also includes
28 * values for hours, minutes, seconds, and milliseconds.
29 * <p>
30 * Time points are handled as millisecond values - milliseconds since the Epoch,
31 * January 1st 1970, 00:00:00.000 GMT. Time values passed to the {@code
32 * java.sql.Date} class are "normalized" to the time 00:00:00.000 GMT on the
33 * date implied by the time value.
34 */
35@FindBugsSuppressWarnings("NM_SAME_SIMPLE_NAME_AS_SUPERCLASS")
36public class Date extends java.util.Date {
37
38    private static final long serialVersionUID = 1511598038487230103L;
39
40    /**
41     * Constructs a {@code Date} object corresponding to the supplied year,
42     * month and day.
43     *
44     * @deprecated Use the constructor {@link #Date(long)}.
45     * @param theYear
46     *            the year, specified as the year minus 1900. Must be in the
47     *            range {@code [0,8099]}.
48     * @param theMonth
49     *            the month, specified as a number with 0 = January. Must be in
50     *            the range {@code [0,11]}.
51     * @param theDay
52     *            the day in the month. Must be in the range {@code [1,31]}.
53     */
54    @Deprecated
55    public Date(int theYear, int theMonth, int theDay) {
56        super(theYear, theMonth, theDay);
57    }
58
59    /**
60     * Creates a date which corresponds to the day determined by the supplied
61     * milliseconds time value {@code theDate}.
62     *
63     * @param theDate
64     *            a time value in milliseconds since the epoch - January 1 1970
65     *            00:00:00 GMT. The time value (hours, minutes, seconds,
66     *            milliseconds) stored in the {@code Date} object is adjusted to
67     *            correspond to 00:00:00 GMT on the day determined by the supplied
68     *            time value.
69     */
70    public Date(long theDate) {
71        super(normalizeTime(theDate));
72    }
73
74    /**
75     * @deprecated This method is deprecated and must not be used. SQL {@code
76     *             Date} values do not have an hours component.
77     * @return does not return anything.
78     * @throws IllegalArgumentException
79     *             if this method is called.
80     */
81    @Deprecated
82    @Override
83    public int getHours() {
84        throw new IllegalArgumentException();
85    }
86
87    /**
88     * @deprecated This method is deprecated and must not be used. SQL {@code
89     *             Date} values do not have a minutes component.
90     * @return does not return anything.
91     * @throws IllegalArgumentException
92     *             if this method is called.
93     */
94    @Deprecated
95    @Override
96    public int getMinutes() {
97        throw new IllegalArgumentException();
98    }
99
100    /**
101     * @deprecated This method is deprecated and must not be used. SQL {@code
102     *             Date} values do not have a seconds component.
103     * @return does not return anything.
104     * @throws IllegalArgumentException
105     *             if this method is called.
106     */
107    @Deprecated
108    @Override
109    public int getSeconds() {
110        throw new IllegalArgumentException();
111    }
112
113    /**
114     * @deprecated This method is deprecated and must not be used. SQL {@code
115     *             Date} values do not have an hours component.
116     * @param theHours
117     *            the number of hours to set.
118     * @throws IllegalArgumentException
119     *             if this method is called.
120     */
121    @Deprecated
122    @Override
123    public void setHours(int theHours) {
124        throw new IllegalArgumentException();
125    }
126
127    /**
128     * @deprecated This method is deprecated and must not be used. SQL {@code
129     *             Date} values do not have a minutes component.
130     * @param theMinutes
131     *            the number of minutes to set.
132     * @throws IllegalArgumentException
133     *             if this method is called.
134     */
135    @Deprecated
136    @Override
137    public void setMinutes(int theMinutes) {
138        throw new IllegalArgumentException();
139    }
140
141    /**
142     * @deprecated This method is deprecated and must not be used. SQL {@code
143     *             Date} values do not have a seconds component.
144     * @param theSeconds
145     *            the number of seconds to set.
146     * @throws IllegalArgumentException
147     *             if this method is called.
148     */
149    @Deprecated
150    @Override
151    public void setSeconds(int theSeconds) {
152        throw new IllegalArgumentException();
153    }
154
155    /**
156     * Sets this date to a date supplied as a milliseconds value. The date is
157     * set based on the supplied time value and rounded to zero GMT for that day.
158     *
159     * @param theTime
160     *            the time in milliseconds since the Epoch.
161     */
162    @Override
163    public void setTime(long theTime) {
164        /*
165         * Store the Date based on the supplied time after removing any time
166         * elements finer than the day based on zero GMT
167         */
168        super.setTime(normalizeTime(theTime));
169    }
170
171    /**
172     * Produces a string representation of the date in SQL format
173     *
174     * @return a string representation of the date in SQL format - {@code "yyyy-MM-dd"}.
175     */
176    @Override
177    public String toString() {
178        StringBuilder sb = new StringBuilder(10);
179
180        format((getYear() + 1900), 4, sb);
181        sb.append('-');
182        format((getMonth() + 1), 2, sb);
183        sb.append('-');
184        format(getDate(), 2, sb);
185
186        return sb.toString();
187    }
188
189    private static final String PADDING = "0000";
190
191    /*
192    * Private method to format the time
193    */
194    private void format(int date, int digits, StringBuilder sb) {
195        String str = String.valueOf(date);
196        if (digits - str.length() > 0) {
197            sb.append(PADDING.substring(0, digits - str.length()));
198        }
199        sb.append(str);
200    }
201
202    /**
203     * Creates a {@code Date} from a string representation of a date in SQL
204     * format.
205     *
206     * @param dateString
207     *            the string representation of a date in SQL format - " {@code yyyy-MM-dd}".
208     * @return the {@code Date} object.
209     * @throws IllegalArgumentException
210     *             if the format of the supplied string does not match the SQL
211     *             format.
212     */
213    public static Date valueOf(String dateString) {
214        if (dateString == null) {
215            throw new IllegalArgumentException();
216        }
217        int firstIndex = dateString.indexOf('-');
218        int secondIndex = dateString.indexOf('-', firstIndex + 1);
219        // secondIndex == -1 means none or only one separator '-' has been
220        // found.
221        // The string is separated into three parts by two separator characters,
222        // if the first or the third part is null string, we should throw
223        // IllegalArgumentException to follow RI
224        if (secondIndex == -1 || firstIndex == 0
225                || secondIndex + 1 == dateString.length()) {
226            throw new IllegalArgumentException();
227        }
228        // parse each part of the string
229        int year = Integer.parseInt(dateString.substring(0, firstIndex));
230        int month = Integer.parseInt(dateString.substring(firstIndex + 1,
231                secondIndex));
232        int day = Integer.parseInt(dateString.substring(secondIndex + 1,
233                dateString.length()));
234        return new Date(year - 1900, month - 1, day);
235    }
236
237    /*
238     * Private method which normalizes a Time value, removing all low
239     * significance digits corresponding to milliseconds, seconds, minutes and
240     * hours, so that the returned Time value corresponds to 00:00:00 GMT on a
241     * particular day.
242     */
243    private static long normalizeTime(long theTime) {
244        return theTime;
245    }
246}
247