Scala/Lift check if date is correctly formatted

前端 未结 5 1140
半阙折子戏
半阙折子戏 2020-12-31 17:20

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 reg

相关标签:
5条回答
  • 2020-12-31 17:32

    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
      }
    
    0 讨论(0)
  • 2020-12-31 17:45

    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)
    )
    
    0 讨论(0)
  • 2020-12-31 17:52

    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.

    0 讨论(0)
  • 2020-12-31 17:53

    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
      }
    
    0 讨论(0)
  • 2020-12-31 17:56

    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 讨论(0)
提交回复
热议问题