开发者

Scala/Lift check if date is correctly formatted

I have a date input box in my lift application, and I want to check that a user-entered date is in correct format: dd/mm/yyyy.

How can I write a regex check for this in scala? I've looked at pattern matching examples - but that seems over-complicated.

PS: I don't have开发者_如何学C to use regex, any other alternatives are welcome!


SimpleDateFormat is ugly and (more disturbingly) non-thread-safe. If you try to simultaneously use the same instance in 2 or more threads then expect things to blow up in a most unpleasant fashion.

JodaTime is far nicer:

import org.joda.time.format._
val fmt = DateTimeFormat forPattern "dd/MM/yyyy"
val input = "12/05/2009"
val output = fmt parseDateTime input

If it throws an IllegalArgumentException, then the date wasn't valid.

As I suspect you'll want to know the actual date if it was valid, you may want to return an Option[DateTime], with None if it was invalid.

def parseDate(input: String) = try {
  Some(fmt parseDateTime input)
} catch {
  case e: IllegalArgumentException => None
}

Alternatively, use an Either to capture the actual exception if formatting wasn't possible:

def parseDate(input: String) = try {
  Right(fmt parseDateTime input)
} catch {
  case e: IllegalArgumentException => Left(e)
}

UPDATE

To then use the Either, you have two main tactics:

map one of the two sides:

parseDate(input).left map (_.getMessage)
//will convert the Either[IllegalArgumentException, DateTime]
//to an Either[String, DateTime]

fold it:

parseDate(input) fold (
  _ => S.error(
    "birthdate",
    "Invalid date. Please enter date in the form dd/mm/yyyy."),
  dt => successFunc(dt)
)

Of course, the two can be composed:

parseDate(input).left map (_.getMessage) fold (
  errMsg => S.error("birthdate", errMsg), //if failure (Left by convention)
  dt => successFunc(dt) //if success (Right by convention)
)


As user unknown has written you should use some library that knows how to handle dates correctly including days per specific month and leap years.

SimpleDateFormat is not very intuitive regarding rollovers of fields and initially accepts wrong dates by just rolling over the other fields. To prevent it from doing so you have to invoke setLenient(false) on it. Also keep in mind that SimpleDateFormat is not thread-safe so you need to create a new instance every time you want to use it:

def validate(date: String) = try {
    val df = new SimpleDateFormat("dd/MM/yyyy")
    df.setLenient(false)
    df.parse(date)
    true
  } catch {
    case e: ParseException => false
  }

Alternatively you may also use Joda Time which is a bit more intuitive than the Java Date APIs and offers thread-safe date formats:

val format = DateTimeFormat.forPattern("dd/MM/yyyy")

def validate(date: String) = try {
    format.parseMillis(date)
    true
  }
  catch {
    case e: IllegalArgumentException => false
  }


It's a good practice to define the DateTimeFormatter instance in an object since it's thread-safe and immutable.

  object DateOfBirth{
    import org.joda.time.format.DateTimeFormat
    import scala.util.Try
    val fmt = DateTimeFormat forPattern "MM/dd/yyyy"
    def validate(date: String) = Try(fmt.parseDateTime(date)).isSuccess
  }


I wouldn't use a regex, but SimpleDateFormat (which isn't that simple, as we will see).

A regular expression which handles to allow 28 and 30 as day, but not 38, different month-lengths and leap years, might be an interesting challenge, but not for real world code.

val df = new java.text.SimpleDateFormat ("dd/MM/yyyy")

(I assume M as in big Month, not m as in small minute).

Now, let's start with an error:

scala> df.parse ("09/13/2001")                                
res255: java.util.Date = Wed Jan 09 00:00:00 CET 2002

hoppla - it is very tolerant, and wraps months around to the next year. But we can get it with a second formatting process:

scala> val sInput = "13/09/2001"
sInput: java.lang.String = 13/09/2001

scala> sInput.equals (df.format (df.parse (sInput))) 
res259: Boolean = true

scala> val sInput = "09/13/2001"                     
sInput: java.lang.String = 09/13/2001

scala> sInput.equals (df.format (df.parse (sInput))) 
res260: Boolean = false

I hope you aren't bound to regex, and can use it.


from this we can validate date in string as well as we get the expected date response by parsing in the format like "dd/MM/yyyy".

  try {  val format = DateTimeFormat.forPattern("dd/MM/yyyy")
  format.parseMillis(dateInString)
  val df = new SimpleDateFormat("dd/MM/yyyy")
  val newDate = df.parse(dateInString)
  true
    } catch {
      case e: ParseException => false

      case e: IllegalArgumentException => false
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜