0

I formatted a regex for HHMMSS.FFFFF as such: ^([0-1]\d|2[0-3])([0-5]\d)([0-5]\d).(\d{5})

and used to to create a SimpleDateFormat as such

 String format = "HHMMSS.FFFFF";
 SimpleDateFormat sdf = new SimpleDateFormat(format);

Then tried to create a time stamp with the following data

new Timestamp(sdf.parse("1-1212.00000).getTime());

Using

121212.00000

it was good as expected. Then tried to do it with such

1-1212.00000

And it still does not throw an Exception. When testing the regex on regex101, the dash is invalid.

Why does Timestamp accept that dash with that regex?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
user2568374
  • 1,164
  • 4
  • 11
  • 21
  • 1
    Format `"HHMMSS.FFFFF"`?? `HH` = Hour in day (0-23), `MM` = Month in year, `SS` = Millisecond, and `FFFFF` = Day of week in month. Really?? – Andreas Jan 31 '18 at 19:35
  • `DateFormat` is not `Pattern`. – Izruo Jan 31 '18 at 19:36
  • `SimpleDateFormat` probably interprets the `-` as a leading (unary) minus; but exactly what goes, I don’t know. – Ole V.V. Jan 31 '18 at 19:48
  • Beware that uppercase `M` is for month (not minute), uppercase `S` is millisecond (not second) and uppercase `F` is day of week in month (not fraction of second). – Ole V.V. Jan 31 '18 at 19:50
  • I got the caps from a Digital Imaging and Communications in Medicine (DICOM) reference for a TIME field (TM) but even if HHMMSS.FFF is not what I want to use for a date format. the HH is correct for hour in day, so why does it accept a dash in the first 2 digits? – user2568374 Jan 31 '18 at 19:50
  • I recommend you throw away the long outmoded and notoriously troublesome `SimpleDateFormat` and friends, and use [`java.time`, the modern Java date and time API,](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`, instead. The modern API is so much nicer to work with. – Ole V.V. Jan 31 '18 at 19:51
  • “why does it accept a dash in the first 2 digits?” I can only say that it’s because `SimpleDateFormat` is poorly designed and notoriously troublesome. With default settings it tends to accept to parse all kinds of input that you would never expect it to accept, and just tacitly give you nonsense results. Throw that class away and forget everything about it. Its `setLenient` method may sometimes make the situation a bit better, but I stand by my first recommendation. – Ole V.V. Jan 31 '18 at 19:56
  • Thanks for the suggestion of updating but JavaSE 1.6 is what I have and I can't make the decision. – user2568374 Jan 31 '18 at 20:04
  • 1
    Much of the *java.time* functionality is back-ported to Java 6 & Java 7 in [*ThreeTen-Backport*](http://www.threeten.org/threetenbp/) project. – Basil Bourque Feb 01 '18 at 03:34
  • Thanks, @user2568374, for the information that you are using Java 1.6. I have edited my answer to reflect this. – Ole V.V. Feb 01 '18 at 05:36
  • @OleV.V. Which answer? Is there something else I can use If i have JavaSE 1.6? – user2568374 Feb 02 '18 at 02:30
  • Please add the closing quote `"` in your `new Timestamp`… – Matt Sep 06 '22 at 08:51

2 Answers2

3

Format "HHMMSS.FFFFF" is:
HH = Hour in day (0-23)
MM = Month in year
SS = Millisecond
FFFFF = Day of week in month (DoWiM)

I don't think that's what you meant to do. But let's go with it.

Remember, all other fields are their default, i.e.
Year = 1970
DayOfMonth = 1
DayOfWeek = Sunday
Minute = 0
Second = 0

Input 000100.00001 parses to 1970-01-04 00:00:00.0, because 1/4/1970 is the first (DoWiM=1) Sunday (default DayOfWeek) in January (Month=1) at midnight (Hour=0, Milli=0).
This is your baseline.

Input 121212.00000 parses to 1970-11-29 12:00:00.012, because Month=12 makes 1970-12-06 the first Sunday in December of 1970, and FFFFF=0 is then one week before that.

Input 1-1212.00000 parses to 1969-10-26 01:00:00.212 since it parses as H=1, M=-1, S=212, F=0, i.e. M=-1 is 2 months before January, and 1969-11-02 is first Sunday in November of 1969, and 1969-10-26 is one week before that.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I know I didn't ask this but is HHmmss.SSSSS what I should be using? – user2568374 Jan 31 '18 at 20:12
  • `SSS` is *milliseconds*. Always use exactly 3 letters, i.e. `SSS`, not `S`, `SS`, `SSSS`, `SSSSS`, or any other length. `HHmmss.SSS00` might do, if you know last two are always zero. – Andreas Jan 31 '18 at 20:16
1

I know I didn't ask this but is HHmmss.SSSSS what I should be using?

You asked now, and thanks for asking.

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HHmmss.SSSSS");
    String timeString = "121212.00000";
    LocalTime time = LocalTime.parse(timeString, dtf);
    System.out.println(time);

This prints

12:12:12

LocalTime from java.time has precision of nanoseconds (up to 9 decimals on the seconds) but its toString method doesn’t print the fraction if it is zero. 121212.34567, for example, is parsed into 12:12:12.345670. Let’s try your string with the dash too, 1-1212.00000. It results in a java.time.format.DateTimeParseException: Text '1-1212.00000' could not be parsed at index 0. As you had expected, the formatter refuses to parse the hours when the second digit is a dash. This also means that you have good validation in the parsing, so you no longer need any regular expression (regex).

The Timestamp and SimpleDateFormat classes are both long outdated, though the latter is the more troublesome of the two. Also SimpleDateFormat supports only millisecond precision, there is no way it could parse seconds with 5 decimals correctly. Find the modern replacements in the java.time package. Use DateTimeFormatter for parsing. I assume you wanted to save a time-of-day without a date into an SQL database. If so, use an SQL datatype of time and a LocalTime in Java. PreparedStatement.setObject() will accept the LocalTime for saving. If your database column has type timestamp and cannot be changed, use LocalDateTime instead.

And while Andreas in a comment has explained why your suggested format pattern string of HHmmss.SSSSS would not work with SimpleDateFormat, it does work with DateTimeFormatter. Here uppercase S means fraction of second, so SSSSS means five decimals (otherwise many of the format pattern letters have the same meaning to both formatters, only not all).

Question: can I use java.time in Java 6?

Yes, most of the above will work with your JavaSE 1.6, only not PreparedStatement.setObject(). You will need to add ThreeTen Backport to your project. This is the backport of java.time to Java 6 and 7 (“ThreeTen” becuase java.time was first described in JSR-310). See the link below.

For saving to your SQL database, you will need to convert to an old-fashioned java.sql.Timestamp or java.sql.Time. For a time stamp you also need a date. For example:

    LocalDate epochDate = LocalDate.ofEpochDay(0);
    Timestamp timeToSave 
            = DateTimeUtils.toSqlTimestamp(LocalDateTime.of(epochDate, time));

I took the epoch date of January 1, 1970, since this is also what a proper parsing of your time string into a Timestamp would have given you. I figure your DICOM can probably give you a date that you want to use instead. DateTimeUtils also has a toSqlTime method for converting LocalTime to java.sql.Time. Link to the documentation below.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • As I mentioned above, my workplace is using JavaSE 1.6 and I can't change this. Is what you said possible with this? – user2568374 Feb 02 '18 at 02:31
  • Yes, that was exactly what I was trying to say. As long as you can decide on a dependency on ThreeTen Backport (and I’m thinking, only temporarily until eventually you move on to Java 8 or 9). – Ole V.V. Feb 02 '18 at 08:09