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)} instead.
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("unimplemented");
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("unimplemented");
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("unimplemented");
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("unimplemented");
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("unimplemented");
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("unimplemented");
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("dateString == null");
216        }
217        if (dateString.length() > 10) {
218            // early fail to avoid parsing huge invalid strings
219            throw new IllegalArgumentException();
220        }
221
222        String[] parts = dateString.split("-");
223        if (parts.length != 3) {
224            throw new IllegalArgumentException();
225        }
226
227        int year = Integer.parsePositiveInt(parts[0]);
228        int month = Integer.parsePositiveInt(parts[1]);
229        int day = Integer.parsePositiveInt(parts[2]);
230        return new Date(year - 1900, month - 1, day);
231    }
232
233    /*
234     * Private method which normalizes a Time value, removing all low
235     * significance digits corresponding to milliseconds, seconds, minutes and
236     * hours, so that the returned Time value corresponds to 00:00:00 GMT on a
237     * particular day.
238     */
239    private static long normalizeTime(long theTime) {
240        return theTime;
241    }
242}
243