Scala / Lift проверяет, правильно ли отформатирована дата

#scala #lift

#scala #лифт

Вопрос:

У меня есть поле ввода даты в моем приложении lift, и я хочу проверить, что введенная пользователем дата находится в правильном формате: дд / мм / гггг.

Как я могу написать проверку регулярных выражений для этого в scala? Я просмотрел примеры сопоставления с шаблоном, но это кажется чрезмерно сложным.

PS: Мне не нужно использовать regex, любые другие альтернативы приветствуются!

Ответ №1:

SimpleDateFormat является уродливым и (что более тревожно) не потокобезопасным. Если вы попытаетесь одновременно использовать один и тот же экземпляр в 2 или более потоках, то ожидайте, что все произойдет самым неприятным образом.

JodaTime намного приятнее:

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

Если он выдает IllegalArgumentException , значит, дата была недействительной.

Поскольку я подозреваю, что вы захотите узнать фактическую дату, если она была действительной, вы можете вернуть Option[DateTime] , с None если она была недействительной.

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

В качестве альтернативы используйте Either для захвата фактического исключения, если форматирование было невозможно:

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

Обновить

Чтобы затем использовать Either , у вас есть две основные тактики:

сопоставьте одну из двух сторон:

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

сверните ее:

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

Конечно, эти два параметра могут быть составлены:

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

Комментарии:

1. Просто обратите внимание, что если она будет использоваться в Lift, вам лучше выбрать Box, а не Option с точки зрения оболочки. Поле позволит вам сохранить ошибку.

2. @arcticpenguin — Лично мне очень не нравится Box конструкция Lift, и я при первой же возможности преобразую их в Option s, когда API будет вынужден принять эти вещи. Если мне нужно также отслеживать исключения монадически, я буду использовать Either в качестве предпочтения, не в последнюю очередь потому, что я могу легко сопоставить исключение с String или каким-либо другим типом для ведения журнала или представления конечному пользователю.

3. Спасибо! Мне это нравится. Сейчас у меня это работает как «validateDate (дата рождения) соответствует { case Some (_) => // success case None =>S.ошибка («дата рождения», «Недопустимая дата. Пожалуйста, введите дату в форме дд / мм /гггг.») }»

4. Я все еще не знаю, для чего нужно «Либо» — хе-хе, но я знаю вариант — поэтому я выбрал это.

5. fmt = ГГГГ / ММ / ДД, input = 2015/02/34 Исключение не выдается

Ответ №2:

Как написал неизвестный пользователь, вам следует использовать какую-нибудь библиотеку, которая знает, как правильно обрабатывать даты, включая дни в определенном месяце и високосные годы.

SimpleDateFormat не очень интуитивно понятен в отношении переноса полей и изначально принимает неправильные даты, просто перенося другие поля. Чтобы предотвратить это, вы должны вызвать setLenient(false) для этого. Также имейте в виду, что SimpleDateFormat это не потокобезопасно, поэтому вам нужно создавать новый экземпляр каждый раз, когда вы хотите его использовать:

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

В качестве альтернативы вы также можете использовать Joda Time, который немного более интуитивно понятен, чем Java Date API, и предлагает потокобезопасные форматы даты:

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

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

Ответ №3:

Хорошей практикой является определение DateTimeFormatter экземпляра в объекте, поскольку он потокобезопасен и неизменяем.

   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
  }
  

Ответ №4:

Я бы использовал не регулярное выражение, а SimpleDateFormat (что не так просто, как мы увидим).

Регулярное выражение, которое разрешает использовать 28 и 30 в качестве дня, но не 38, разные длины месяцев и високосные годы, может быть интересной задачей, но не для кода реального мира.

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

(Я предполагаю, что M как в большом месяце, а не m как в маленькой минуте).

Теперь давайте начнем с ошибки:

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

hoppla — он очень терпимый и переносит месяцы на следующий год. Но мы можем получить ее с помощью второго процесса форматирования:

 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
  

Я надеюсь, что вы не привязаны к регулярному выражению и можете его использовать.

Комментарии:

1. О нет, я могу использовать все, что мне нравится. Я исправил вопрос. Это выглядит интересно, спасибо!

2. Итак, вы говорите, что для проверки, если это неправильно — я должен сопоставить это — и преобразовать это обратно в строку?

3. Также — проверяет ли это, что дата является «реальной» датой, или только то, что она соответствует общему синтаксису. Например, могу ли я получить 31 апреля 2011?

Ответ №5:

исходя из этого, мы можем подтвердить дату в строке, а также получить ожидаемый ответ о дате путем синтаксического анализа в формате, подобном «дд / ММ / гггг».

   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
    }