Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4808845

Enahnce java.sql.Timestamp#valueOf() efficiency and robustness

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Duplicate
    • Icon: P4 P4
    • None
    • 1.4.0
    • core-libs
    • x86
    • windows_2000

      Name: jl125535 Date: 01/27/2003


      FULL PRODUCT VERSION :
      java version "1.4.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
      Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)

      A DESCRIPTION OF THE PROBLEM :
      java.sql.Timestamp valueOf() is coded very inefficently and
      also doesn't do very much validation of the strings it is
      being fed for correctness. This suggested enhancement greatly
      reduces object churn by total elimination of
      String.substring() and String.indexing operations. Also is
      not dependent on Interger.parseInt() which was forcing much
      of the substringing. Code also significantly upgrades
      correctness checking. For example, you can feed the
      existing code "2002-01-23 -12:-13:-45.-196" and it will
      happily chew it down and give back an interesting result.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      Current source code:
          public static Timestamp valueOf(String s) {
      String date_s;
      String time_s;
      String nanos_s;
      int year;
      int month;
      int day;
      int hour;
      int minute;
      int second;
      int a_nanos = 0;
      int firstDash;
      int secondDash;
      int dividingSpace;
      int firstColon = 0;
      int secondColon = 0;
      int period = 0;
      String formatError = "Timestamp format must be yyyy-mm-dd
      hh:mm:ss.fffffffff";
      String zeros = "000000000";

      if (s == null) throw new java.lang.IllegalArgumentException("null
      string");

      // Split the string into date and time components
      s = s.trim();
      dividingSpace = s.indexOf(' ');
      if (dividingSpace > 0) {
      date_s = s.substring(0,dividingSpace);
      time_s = s.substring(dividingSpace+1);
      } else {
      throw new java.lang.IllegalArgumentException(formatError);
      }


      // Parse the date
      firstDash = date_s.indexOf('-');
      secondDash = date_s.indexOf('-', firstDash+1);

      // Parse the time
      if (time_s == null)
      throw new java.lang.IllegalArgumentException(formatError);
      firstColon = time_s.indexOf(':');
      secondColon = time_s.indexOf(':', firstColon+1);
      period = time_s.indexOf('.', secondColon+1);

      // Convert the date
      if ((firstDash > 0) & (secondDash > 0) &
      (secondDash < date_s.length()-1)) {
      year = Integer.parseInt(date_s.substring(0, firstDash)) - 1900;
      month =
      Integer.parseInt(date_s.substring
      (firstDash+1, secondDash)) - 1;
      day = Integer.parseInt(date_s.substring(secondDash+1));
      } else {
      throw new java.lang.IllegalArgumentException(formatError);
      }

      // Convert the time; default missing nanos
      if ((firstColon > 0) & (secondColon > 0) &
      (secondColon < time_s.length()-1)) {
      hour = Integer.parseInt(time_s.substring(0, firstColon));
      minute =
      Integer.parseInt(time_s.substring(firstColon+1, secondColon));
      if ((period > 0) & (period < time_s.length()-1)) {
      second =
      Integer.parseInt(time_s.substring(secondColon+1, period));
      nanos_s = time_s.substring(period+1);
      if (nanos_s.length() > 9)
      throw new java.lang.IllegalArgumentException(formatError);
      if (!Character.isDigit(nanos_s.charAt(0)))
      throw new java.lang.IllegalArgumentException(formatError);
      nanos_s = nanos_s + zeros.substring(0,9-nanos_s.length());
      a_nanos = Integer.parseInt(nanos_s);
      } else if (period > 0) {
      throw new java.lang.IllegalArgumentException(formatError);
      } else {
      second = Integer.parseInt(time_s.substring(secondColon+1));
      }
      } else {
      throw new java.lang.IllegalArgumentException();
      }

      return new Timestamp(year, month, day, hour, minute, second, a_nanos);
          }

        Suggested enhancement:
          public static java.sql.Timestamp valueOf(String s) {
      if (s == null) {
      throw new java.lang.IllegalArgumentException(s);
      }

      String formatException = "Timestamp format must be yyyy-mm-dd
      hh:mm:ss.fffffffff: ";

      char buf[] = s.toCharArray();
      // 0-1-2 3:4:5.6
      int results[] = {0,0,0,0,0,0,0};
      int r = 0; // results index to 0
      int i=0; // buf index to 0
      while (i < buf.length) {
      char c = buf[i++];
      if (c == '-') {
      if (r++ > 1) {
      // '-' only valid at 0 and 1
      throw new java.lang.IllegalArgumentException
      (formatException + s);
      }
      } else if (c == ' ') {
      if (r++ != 2) {
      // ' ' can only be used at 2
      throw new java.lang.IllegalArgumentException
      (formatException + s);
      }
      } else if (c == ':') {
      if ((r != 3) && (r != 4)) {
      // ':' only valid at 3 and 4
      throw new java.lang.IllegalArgumentException
      (formatException + s);
      }
      r++;
      } else if (c == '.') {
      if (r++ != 5) {
      // '.' only valid at 5
      throw new java.lang.IllegalArgumentException
      (formatException + s);
      }
      } else {
      int digit = Character.digit(c, 10);
      if (digit < 0) {
      throw new java.lang.IllegalArgumentException
      (formatException + s);
      }
      if (results[r] >= 100000000) {
      // None of the fields should exceed 999999999
      // Checked here to catch before it hits MAX_INTEGER
      throw new java.lang.IllegalArgumentException
      (formatException + s);
      }
      results[r] = (results[r] * 10) + digit;
      }
      }

      // Validate results
      if (
      // Diddn't reach end of buffer
      (i != buf.length) ||
      // Didn't parse all of our int's, but we do allow for missing nanos
      (r < (results.length - 2)) ||
      // Year is too large
      (results[0] > 9999) ||
      // Month is too large
      ((results[1] == 0) || (results[1] > 12)) ||
      // Day out of range
      ((results[2] == 0) || (results[2] > 31)) ||
      // Hours too large
      (results[3] > 23) ||
      // Minutes too large
      (results[4] > 59) ||
      // Seconds too large
      (results[5] > 59)
      // No need to check nanos, done above
      ) {
      throw new java.lang.IllegalArgumentException(formatException + s);
      }

      // Now scale the nanos up, if they aren't 0
      if (results[6] != 0) {
      while (results[6] <= 99999999) {
      results[6] *= 10;
      }
      }

      // Create and return new EITimestamp
      return new Timestamp(results[0] - 1900, results[1] - 1, results[2],
      results[3], results[4], results[5],
      results[6]);
          }

      ---------- END SOURCE ----------
      (Review ID: 153596)
      ======================================================================

            Unassigned Unassigned
            jleesunw Jon Lee (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: