4

I have a string which can contain a date(yyyy-MM-dd) or date and time (yyyy-MM-dd HH:mm:ss) in respective formats.

I want to know which strings contains only date.

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(dateFormat.parse("2015-02-02"));
System.out.println(dateFormat.parse("2015-02-02 23:23:23"));

In above code, both the strings are parsed successfully, whereas the format is same for only first.

Sahil Jain
  • 177
  • 2
  • 11
  • use regular expression. – Shriram Feb 02 '15 at 07:26
  • 3
    For that particular pattern, you could just check that the length of the input is 10. For variable-length patterns, it's slightly harder - there are alternative parse methods which allow you to see how much of the string has been parsed. (See `parse(String, ParsePosition)`.) – Jon Skeet Feb 02 '15 at 07:29
  • @JonSkeet I also thought of that solution, but it seems more like an alternative. – Sahil Jain Feb 02 '15 at 07:34
  • @Jon, slighly better IMO is checking if the string contains a ':', ( in case a valid time somewhere strange is 10 long) but both are likely fine. – user949300 Feb 02 '15 at 07:35
  • @SahilJain does regex help you – Ankur Singhal Feb 02 '15 at 07:35
  • @ankur-singhal: Why use a regex when you're already parsing? – Jon Skeet Feb 02 '15 at 08:41
  • Thinking more about this question, I really want to know this: **WHY?** Could you elaborate on the requirement? If all you need is the date, why do you care if the String also contains a time, my pet's name, whatever afterwards, so long as you can parse a date? If this input is coming from a file, your XML parser or tokenizer should care, but that should be done **well before** you pass the token to the Date parser. – user949300 Feb 04 '15 at 23:46
  • 1
    @user949300 I am pointing to different database columns based on the incoming string. – Sahil Jain Feb 05 '15 at 08:24
  • I recommend you don’t use `SimpleDateFormat` and `Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Instead use `LocalDate` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). See the answer by Arvind Kumar Avinash below. – Ole V.V. Jul 06 '21 at 02:55

4 Answers4

3
public static void main(String[] args) {
        String dateOnly = "2015-02-02";
        String dateAndTimeOnly = "2015-02-02 23:23:23";
        System.out.println("Date Only = " + validateDateFormat(dateOnly));
        System.out.println("Date And time Only = " + validateDateFormat(dateAndTimeOnly));
    }

    public static boolean validateDateFormat(String input) {

        return input.matches("([0-9]{4})-([0-9]{2})-([0-9]{2})");
    }

output

Date Only = true
Date And time Only = false

Regex is self explanatory - Input will be separated by -, ist part([0-9]{4}) can contain 4 digit , 2nd part can contain 2 digit [0-9]{2}, so as 3rd.

Ankur Singhal
  • 26,012
  • 16
  • 82
  • 116
  • Seems like overkill when checking length 10 or for presence of. ':' also work. What would this regex catch where those fail? – user949300 Feb 02 '15 at 07:38
  • @user949300 eg for input – Ankur Singhal Feb 02 '15 at 07:39
  • O.k., yes, in general case, or if user we're typing, you'd want a regex. However, OP specified that the Strings are already highly limited and structured. – user949300 Feb 02 '15 at 07:41
  • @user949300 i agree with your points, reading OP question, his solution did not work, why it did not worked explained by `San Krish`, he also wanted to have alternate solutions, which can be many considering your input and considering OP input is very well structured. – Ankur Singhal Feb 02 '15 at 07:43
  • Also this allows invalid dates. If you're not really going to validate that it's a date, why not just check the length? – Jon Skeet Feb 02 '15 at 08:53
  • @JonSkeet yes agree, as per OP, just wanted to differentiate between two dates format with regex. Yes Regex can be further modified.. – Ankur Singhal Feb 02 '15 at 08:55
  • But I don't see why a regex is a good idea at all? It's more complicated than a length check, and less rigorous than using a different overload of `DateFormat.parse` (as per my answer). Where is the benefit in using it? – Jon Skeet Feb 02 '15 at 08:57
3

I would use the overload of parse which takes a ParsePosition - you can then check the position afterwards:

import java.util.*;
import java.text.*;

public class Test {

    public static void main(String[] args) throws Exception {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        System.out.println(parseFully(dateFormat, "2015-02-02"));
        System.out.println(parseFully(dateFormat, "2015-02-02 23:23:23"));
    }

    private static Date parseFully(DateFormat format, String text) 
          throws ParseException {
        ParsePosition position = new ParsePosition(0);
        Date date = format.parse(text, position);
        if (position.getIndex() == text.length()) {
            return date;
        }
        if (date == null) {
            throw new ParseException("Date could not be parsed: " + text,
                                     position.getErrorIndex());
        }
        throw new ParseException("Date was parsed incompletely: " + text,
                                 position.getIndex());
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks jon for the idea. I think that comparing pattern length and date string length is the best possible solution so far. – Sahil Jain Feb 02 '15 at 09:57
  • @SahilJain: That's fine *if* it's a fixed-length pattern. If you've got a pattern like `"EEE dd MMM yyyy"` then it won't work. – Jon Skeet Feb 02 '15 at 09:59
  • I might quibble that this fails (I think???) for an input with a space at the end, which may be common, and you may wish to consider "legal". If so, be sure to call `trim()` first. – user949300 Feb 04 '15 at 19:16
  • @user949300: I would *want* it to fail in that case, personally - it's not "just a date" at that point. But yes, you can always trim it... – Jon Skeet Feb 04 '15 at 19:19
1

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Solution using java.time, the modern Date-Time API:

Let's first try to do it the way you have done:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String[] arr = { "2015-02-02", "2015-02-02 23:23:23" };
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.ENGLISH);

        for (String s : arr) {
            System.out.println("Attempting to parse '" + s + "':");
            LocalDate date = LocalDate.parse(s, dtf);
            System.out.println("Parsed successfully: " + date);
        }
    }
}

Output:

Attempting to parse '2015-02-02':
Parsed successfully: 2015-02-02
Attempting to parse '2015-02-02 23:23:23':
Exception in thread "main" java.time.format.DateTimeParseException: Text
'2015-02-02 23:23:23' could not be parsed, unparsed text found at index 10

As you can see, the java.time API correctly throws an exception informing you about the problem. SimpleDateFormat, on the other hand, parses the input string silently which has caused the problem that you have posted.

Thus, with the modern date-time API, you have two easy options:

  1. Simply catch the exception and say that the second input (i.e. 2015-02-02 23:23:23) is not a date string as per the specified date pattern.
  2. Use the function, DateTimeFormatter#parse(CharSequence, ParsePosition) with the ParsePosition index set to 0.

Given below is a demo of the second option:

import java.text.ParsePosition;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String[] arr = { "2015-02-02", "2015-02-02 23:23:23" };
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.ENGLISH);

        for (String s : arr) {
            ParsePosition pp = new ParsePosition(0);
            LocalDate.from(dtf.parse(s, pp));
            if (pp.getIndex() < s.length()) {
                System.out.println("'" + s + "' is not a date string as per the specified date pattern.");
            }
        }
    }
}

Output:

'2015-02-02 23:23:23' is not a date string as per the specified date pattern.

ONLINE DEMO

Note: Never use SimpleDateFormat or DateTimeFormatter without a Locale.

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

Once the desired format is reached , SimpleDateFormat doesnt format the rest of String . It is the reason why your second string is parsed.

This post SimpleDateFormat parse(string str) doesn't throw an exception when str = 2011/12/12aaaaaaaaa? may help you .

Also check the DateFormat#parse method in java docs

Community
  • 1
  • 1
Santhosh
  • 8,181
  • 4
  • 29
  • 56
  • Yes, i am understanding what java is doing there. But i am looking for a good solution for my problem and also want to understand why does java does not check for whole string. I tried to set lenient to false also, still not able to find the desired output – Sahil Jain Feb 02 '15 at 07:30
  • Have you read the answers in the post linked , it will make you clear . comment if you dont understand :) – Santhosh Feb 02 '15 at 07:33