2011-05-12 19 views
5

Tengo un cuadro de entrada de fecha en mi aplicación de elevación, y quiero verificar que la fecha introducida por el usuario esté en el formato correcto: dd/mm/aaaa.Comprobación de Scala/Lift si la fecha está formateada correctamente

¿Cómo puedo escribir una verificación de expresiones regulares para esto en scala? He visto ejemplos de patrones de coincidencia, pero parece demasiado complicado.

PD: ¡No tengo que usar regex, cualquier otra alternativa es bienvenida!

Respuesta

10

SimpleDateFormat es feo y (más inquietante) no es seguro para subprocesos. Si intenta utilizar simultáneamente la misma instancia en 2 o más hilos, entonces espere que las cosas exploten de la manera más desagradable.

JodaTime es mucho mejor:

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

Si se lanza una IllegalArgumentException, entonces la fecha no era válida.

Como sospecho que querrá saber la fecha real si fuera válida, puede devolver un Option[DateTime], con None si no fuera válido.

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

otra alternativa es utilizar un Either para capturar la excepción real, si el formato no era posible:

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

ACTUALIZACIÓN

Para utilizar entonces el Either, tiene dos tácticas principales:

asignar uno de los dos lados:

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

doblarla:

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

Por supuesto, los dos pueden estar compuestos:

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

Solo una nota que si se va a utilizar en Lift, es mejor que Box versus Option en términos de un contenedor. Box le permitirá almacenar el error. – andyczerwonka

+1

@arcticpenguin - Personalmente tengo una gran aversión por el constructo 'Box 'de Lift, y los convertiré a' Option's lo antes posible cuando los API lo obliguen a aceptar las cosas. Si también necesito rastrear las excepciones de forma monótona, utilizaré un 'Oither', sobre todo porque puedo asignar fácilmente la excepción a 'String' u otro tipo para el registro o la presentación al usuario final. –

+0

No estoy en absoluto en desacuerdo. Gran punto – andyczerwonka

1

No utilizaría una expresión regular, pero SimpleDateFormat (que no es tan simple, como veremos).

Una expresión regular que se maneja para permitir 28 y 30 como días, pero no como 38, diferentes meses y años bisiestos, podría ser un desafío interesante, pero no para el código del mundo real.

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

(supongo que M como en el Gran Mes, no m en minutos pequeños).

Ahora, vamos a empezar con un error:

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

hoppla - es muy tolerante, y se envuelve alrededor de meses al año siguiente. Pero podemos conseguirlo con un segundo proceso de formateo:

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 

espero que no están obligados a expresiones regulares, y puedo usarlo.

+0

Oh, no, puedo usar cualquier cosa que me guste. Enmendé la pregunta. Esto se ve interesante, gracias! – drozzy

+0

Entonces, ¿estás diciendo que para verificar si está mal, debería hacer coincidirlo y convertirlo a la cadena? – drozzy

+0

Además, ¿esto verifica que la fecha sea una fecha "real" o que solo cumpla con la sintaxis general? Como puedo tener el 31 de abril de 2011? – drozzy

1

Como desconocida usuario ha escrito que debe utilizar alguna biblioteca que sabe cómo manejar correctamente las fechas incluyendo días por mes específico y años bisiestos.

SimpleDateFormat no es muy intuitivo con respecto a las vueltas de campos y acepta inicialmente las fechas incorrectas simplemente al pasar sobre los otros campos. Para evitar que lo haga, debe invocar setLenient(false) en él. También hay que tener en cuenta que SimpleDateFormat no es seguro para subprocesos por lo que necesita para crear una nueva instancia cada vez que desee utilizarlo:

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

alternativa, también se puede utilizar Joda Time que es un poco más intuitivo que la fecha de Java APIs y ofrece formatos de fecha compatibles con el proceso:

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

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

de esto podemos validar la fecha en la cadena, así como se obtiene la respuesta esperada fecha mediante el análisis en el formato como "dd/mm/aaaa".

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

que es una buena práctica definir la instancia DateTimeFormatter en un objeto, ya que es seguro para subprocesos e inmutable.

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 
    } 
Cuestiones relacionadas