Zzrot
Zzrot

Reputation: 324

String getting incorrectly parsed to datetime format in Scala

I have a method as follows:

def checkDateFormat(dateStr: String): Any = {
val dateFormats = List(
  "yyyy/MM/dd",
  "MM/dd/yyyy",
  "MMM dd, yyyy",
  "dd MMM yyyy",
  "yyyy-MM-dd"
)

dateFormats.foreach(format => {
  try {
      val date = new SimpleDateFormat(format).parse(dateStr)
      return format
  }
  catch {
    case e: Exception => {
      println("Exception")
      /* do nothing */
    }
  }
})
return
}

Here, I am trying to identify the date format of an input string. The way I go about it is to iterate over all dateFormats and try to parse the string using SimpleDateFormat. If it is correctly parsed, then I return the format, else I let the catch block handle it. This method works great for most cases except for if I try to parse:

 val inputStr = "02/02/2017"
 val dateFormat = checkDateFormat(inputStr)

In this case, for some reason inputStr is parsed by yyyy/MM/dd instead of getting handled by catch (I want it to be parsed by MM/dd/yyyy instead). Is there a different way I should be parsing these strings (eg. only through regex pattern matching) or is there another way I can get the example correctly parsed?

Edit: I would prefer not to use regex as I would like to add more formats into dateFormats and it would be tedious, but if there is no other option, I'm open to it.

Upvotes: 1

Views: 565

Answers (3)

Tim
Tim

Reputation: 27356

Here is a simpler way to write this function:

def checkDateFormat(dateStr: String): Option[String] = {
  val dateFormats = List(
    "yyyy/MM/dd",
    "MM/dd/yyyy",
    "MMM dd, yyyy",
    "dd MMM yyyy",
    "yyyy-MM-dd"
  )

  def validFormat(df: String) =
    Try{new SimpleDateFormat(df).parse(dateStr)}.isSuccess

  dateFormats.find(validFormat)
}

Using find means that this will return as soon as a working format is found.

If you want the actual date, try this:

def parseDate(dateStr: String): Option[Date] = {
  dateFormats
    .view
    .map(df => Try{new SimpleDateFormat(df).parse(dateStr)})
    .find(_.isSuccess)
    .flatMap(_.toOption)
}

In this case, using view ensures that parsing stops as soon as a valid format is found.

Upvotes: 1

Leo C
Leo C

Reputation: 22449

I would recommend using the more reliable java.time API and replacing try-catch with Try as follows:

def checkDateFormat(dateStr: String): String = {
  import java.time.LocalDate
  import java.time.format.DateTimeFormatter
  import scala.util.Try

  val dateFormats = List(
    "yyyy/MM/dd",
    "MM/dd/yyyy",
    "MMM dd, yyyy",
    "dd MMM yyyy",
    "yyyy-MM-dd"
  )

  dateFormats.dropWhile( format =>
    Try( LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(format)) ).isFailure
  ).
  headOption match {
    case Some(fmt) => fmt
    case None => "No Match!"
  }
}

checkDateFormat("2018/05/21")
// res1: String = yyyy/MM/dd

checkDateFormat("05/21/2018")
// res2: String = MM/dd/yyyy

checkDateFormat("May 21, 2018")
// res3: String = MMM dd, yyyy

checkDateFormat("2018 05 21")
// res4: String = No Match!

Upvotes: 0

Andy Hayden
Andy Hayden

Reputation: 375495

From the java docs:

For parsing, if the number of pattern letters is more than 2, the year is interpreted literally, regardless of the number of digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.

In this case it's intepreted as year 2AD:

@ new SimpleDateFormat("YYYY/MM/dd").parse(inputStr)
res8: java.util.Date = Sun Jan 01 00:00:00 PST 2

One option is to regex pattern match before doing these tests.

Upvotes: 0

Related Questions