1 /*
   2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.sql;
  27 
  28 import java.time.Instant;
  29 import java.time.LocalDate;
  30 import sun.misc.SharedSecrets;
  31 import sun.misc.JavaLangAccess;
  32 
  33 /**
  34  * <P>A thin wrapper around a millisecond value that allows
  35  * JDBC to identify this as an SQL <code>DATE</code> value.  A
  36  * milliseconds value represents the number of milliseconds that
  37  * have passed since January 1, 1970 00:00:00.000 GMT.
  38  * <p>
  39  * To conform with the definition of SQL <code>DATE</code>, the
  40  * millisecond values wrapped by a <code>java.sql.Date</code> instance
  41  * must be 'normalized' by setting the
  42  * hours, minutes, seconds, and milliseconds to zero in the particular
  43  * time zone with which the instance is associated.
  44  */
  45 public class Date extends java.util.Date {
  46 
  47     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
  48 
  49     /**
  50      * Constructs a <code>Date</code> object initialized with the given
  51      * year, month, and day.
  52      * <P>
  53      * The result is undefined if a given argument is out of bounds.
  54      *
  55      * @param year the year minus 1900; must be 0 to 8099. (Note that
  56      *        8099 is 9999 minus 1900.)
  57      * @param month 0 to 11
  58      * @param day 1 to 31
  59      * @deprecated instead use the constructor <code>Date(long date)</code>
  60      */
  61     @Deprecated
  62     public Date(int year, int month, int day) {
  63         super(year, month, day);
  64     }
  65 
  66     /**
  67      * Constructs a <code>Date</code> object using the given milliseconds
  68      * time value.  If the given milliseconds value contains time
  69      * information, the driver will set the time components to the
  70      * time in the default time zone (the time zone of the Java virtual
  71      * machine running the application) that corresponds to zero GMT.
  72      *
  73      * @param date milliseconds since January 1, 1970, 00:00:00 GMT not
  74      *        to exceed the milliseconds representation for the year 8099.
  75      *        A negative number indicates the number of milliseconds
  76      *        before January 1, 1970, 00:00:00 GMT.
  77      */
  78     public Date(long date) {
  79         // If the millisecond date value contains time info, mask it out.
  80         super(date);
  81 
  82     }
  83 
  84     /**
  85      * Sets an existing <code>Date</code> object
  86      * using the given milliseconds time value.
  87      * If the given milliseconds value contains time information,
  88      * the driver will set the time components to the
  89      * time in the default time zone (the time zone of the Java virtual
  90      * machine running the application) that corresponds to zero GMT.
  91      *
  92      * @param date milliseconds since January 1, 1970, 00:00:00 GMT not
  93      *        to exceed the milliseconds representation for the year 8099.
  94      *        A negative number indicates the number of milliseconds
  95      *        before January 1, 1970, 00:00:00 GMT.
  96      */
  97     public void setTime(long date) {
  98         // If the millisecond date value contains time info, mask it out.
  99         super.setTime(date);
 100     }
 101 
 102     /**
 103      * Converts a string in JDBC date escape format to
 104      * a <code>Date</code> value.
 105      *
 106      * @param s a <code>String</code> object representing a date in
 107      *        in the format "yyyy-[m]m-[d]d". The leading zero for <code>mm</code>
 108      * and <code>dd</code> may also be omitted.
 109      * @return a <code>java.sql.Date</code> object representing the
 110      *         given date
 111      * @throws IllegalArgumentException if the date given is not in the
 112      *         JDBC date escape format (yyyy-[m]m-[d]d)
 113      */
 114     public static Date valueOf(String s) {
 115         if (s == null) {
 116             throw new java.lang.IllegalArgumentException();
 117         }
 118         final int YEAR_LENGTH = 4;
 119         final int MONTH_LENGTH = 2;
 120         final int DAY_LENGTH = 2;
 121         final int MAX_MONTH = 12;
 122         final int MAX_DAY = 31;
 123         Date d = null;
 124 
 125         int firstDash = s.indexOf('-');
 126         int secondDash = s.indexOf('-', firstDash + 1);
 127         int len = s.length();
 128 
 129         if ((firstDash > 0) && (secondDash > 0) && (secondDash < len - 1)) {
 130             if (firstDash == YEAR_LENGTH &&
 131                     (secondDash - firstDash > 1 && secondDash - firstDash <= MONTH_LENGTH + 1) &&
 132                     (len - secondDash > 1 && len - secondDash <= DAY_LENGTH + 1)) {
 133                 int year = Integer.parseInt(s, 0, firstDash, 10);
 134                 int month = Integer.parseInt(s, firstDash + 1, secondDash, 10);
 135                 int day = Integer.parseInt(s, secondDash + 1, len, 10);
 136 
 137                 if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) {
 138                     d = new Date(year - 1900, month - 1, day);
 139                 }
 140             }
 141         }
 142         if (d == null) {
 143             throw new java.lang.IllegalArgumentException();
 144         }
 145 
 146         return d;
 147 
 148     }
 149 
 150 
 151     /**
 152      * Formats a date in the date escape format yyyy-mm-dd.
 153      *
 154      * @return a String in yyyy-mm-dd format
 155      */
 156     @SuppressWarnings("deprecation")
 157     public String toString () {
 158         int year = super.getYear() + 1900;
 159         int month = super.getMonth() + 1;
 160         int day = super.getDate();
 161 
 162         char buf[] = new char[10];
 163         formatDecimalInt(year, buf, 0, 4);
 164         buf[4] = '-';
 165         Date.formatDecimalInt(month, buf, 5, 2);
 166         buf[7] = '-';
 167         Date.formatDecimalInt(day, buf, 8, 2);
 168 
 169         return jla.newStringUnsafe(buf);
 170     }
 171 
 172     /**
 173      * Formats an unsigned integer into a char array in decimal output format.
 174      * Numbers will be zero-padded or truncated if the string representation
 175      * of the integer is smaller than or exceeds len, respectively.
 176      *
 177      * Should consider moving this to Integer and expose it through
 178      * JavaLangAccess similar to Integer::formatUnsignedInt
 179      * @param val  Value to convert
 180      * @param buf  Array containing converted value
 181      * @param offset Starting pos in buf
 182      * @param len  length of output value
 183      */
 184     static void formatDecimalInt(int val, char[] buf, int offset, int len) {
 185         int charPos = offset + len;
 186         do {
 187             buf[--charPos] = (char)('0' + (val % 10));
 188             val /= 10;
 189         } while (charPos > offset);
 190     }
 191 
 192     // Override all the time operations inherited from java.util.Date;
 193 
 194    /**
 195     * This method is deprecated and should not be used because SQL Date
 196     * values do not have a time component.
 197     *
 198     * @deprecated
 199     * @exception java.lang.IllegalArgumentException if this method is invoked
 200     * @see #setHours
 201     */
 202     @Deprecated
 203     public int getHours() {
 204         throw new java.lang.IllegalArgumentException();
 205     }
 206 
 207    /**
 208     * This method is deprecated and should not be used because SQL Date
 209     * values do not have a time component.
 210     *
 211     * @deprecated
 212     * @exception java.lang.IllegalArgumentException if this method is invoked
 213     * @see #setMinutes
 214     */
 215     @Deprecated
 216     public int getMinutes() {
 217         throw new java.lang.IllegalArgumentException();
 218     }
 219 
 220    /**
 221     * This method is deprecated and should not be used because SQL Date
 222     * values do not have a time component.
 223     *
 224     * @deprecated
 225     * @exception java.lang.IllegalArgumentException if this method is invoked
 226     * @see #setSeconds
 227     */
 228     @Deprecated
 229     public int getSeconds() {
 230         throw new java.lang.IllegalArgumentException();
 231     }
 232 
 233    /**
 234     * This method is deprecated and should not be used because SQL Date
 235     * values do not have a time component.
 236     *
 237     * @deprecated
 238     * @exception java.lang.IllegalArgumentException if this method is invoked
 239     * @see #getHours
 240     */
 241     @Deprecated
 242     public void setHours(int i) {
 243         throw new java.lang.IllegalArgumentException();
 244     }
 245 
 246    /**
 247     * This method is deprecated and should not be used because SQL Date
 248     * values do not have a time component.
 249     *
 250     * @deprecated
 251     * @exception java.lang.IllegalArgumentException if this method is invoked
 252     * @see #getMinutes
 253     */
 254     @Deprecated
 255     public void setMinutes(int i) {
 256         throw new java.lang.IllegalArgumentException();
 257     }
 258 
 259    /**
 260     * This method is deprecated and should not be used because SQL Date
 261     * values do not have a time component.
 262     *
 263     * @deprecated
 264     * @exception java.lang.IllegalArgumentException if this method is invoked
 265     * @see #getSeconds
 266     */
 267     @Deprecated
 268     public void setSeconds(int i) {
 269         throw new java.lang.IllegalArgumentException();
 270     }
 271 
 272    /**
 273     * Private serial version unique ID to ensure serialization
 274     * compatibility.
 275     */
 276     static final long serialVersionUID = 1511598038487230103L;
 277 
 278     /**
 279      * Obtains an instance of {@code Date} from a {@link LocalDate} object
 280      * with the same year, month and day of month value as the given
 281      * {@code LocalDate}.
 282      * <p>
 283      * The provided {@code LocalDate} is interpreted as the local date
 284      * in the local time zone.
 285      *
 286      * @param date a {@code LocalDate} to convert
 287      * @return a {@code Date} object
 288      * @exception NullPointerException if {@code date} is null
 289      * @since 1.8
 290      */
 291     @SuppressWarnings("deprecation")
 292     public static Date valueOf(LocalDate date) {
 293         return new Date(date.getYear() - 1900, date.getMonthValue() -1,
 294                         date.getDayOfMonth());
 295     }
 296 
 297     /**
 298      * Creates a {@code LocalDate} instance using the year, month and day
 299      * from this {@code Date} object.
 300      * @return a {@code LocalDate} object representing the same date value
 301      *
 302      * @since 1.8
 303      */
 304     @SuppressWarnings("deprecation")
 305     public LocalDate toLocalDate() {
 306         return LocalDate.of(getYear() + 1900, getMonth() + 1, getDate());
 307     }
 308 
 309    /**
 310     * This method always throws an UnsupportedOperationException and should
 311     * not be used because SQL {@code Date} values do not have a time
 312     * component.
 313     *
 314     * @exception java.lang.UnsupportedOperationException if this method is invoked
 315     */
 316     @Override
 317     public Instant toInstant() {
 318         throw new java.lang.UnsupportedOperationException();
 319     }
 320 }