2009-07-19 15 views
5

Tengo que validar numerosas fechas con mi proyecto actual. Desafortunadamente, estas fechas pueden variar ampliamente. Los ejemplos incluyen:Advanced Date-Validation con PHP

  1. 1983-07-10 (Después de 1970)
  2. 1492-10-11 (Antes de 1970, año de Unix marcas de tiempo - esto elimina strtotime() en algunos sistemas)
  3. 200 aC (Realmente viejo ...)

Las fechas no excederán 9999 b.c., ni serán futuras (más allá de "hoy"). ¿Cuál sería la mejor manera de validar que los valores enviados son, en verdad, fechas y fechas adecuadas?

actualizaciones ...

Todas las fechas deben ser clasificables dentro de su lista global. Las fechas de significado 1 y 3 anteriores deben ser comparables entre sí, y clasificadas ASC o DESC.

Soy plenamente consciente de los cambios de calendario que han tenido lugar en el pasado y la confusión en torno a estos cambios. Mi proyecto supone que el usuario ya realizó la calibración adecuada para averiguar la fecha de acuerdo con nuestro sistema de calendario moderno. No realizaré esta calibración por ellos.

+0

¿Cuál será el formato de entrada de fecha? La respuesta dependería mucho de eso. :) – Jon

+0

Estoy abierto para recibir sugerencias. No estoy seguro de si tendré una serie de menús desplegables con un adicional que tenga "B.C./A.D". o un simple cuadro de texto. – Sampson

+0

Vea mi larga respuesta a continuación; para resumir, los botones de radio para BC/AD suenan como los más apropiados para mí. – Jon

Respuesta

5

¿Qué tal una serie de expresiones regulares cuidadosamente escritas que reconocen cada formato posible. Una vez que conozca el formato, puede validarlo y quizás ponerlo en una representación uniforme (por ejemplo, time_t de 64 bits).

por ejemplo,

/(\d{4})-(\d{2})-(\d{2})/ 
/(\d+)(bc|b.c.|bce|b.c.e)/i 
etc. 

ya que suena como cada forma tiene sus propias reglas de validación, y no estás implementar cualquier norma ampliamente disponible, creo que le pegan validar cada caso por separado.

Actualización:

Todas las fechas deben ser clasificables dentro de su lista global.

Me parece que para poder ordenar las fechas que aparecen en diferentes formatos, necesitaría una representación uniforme para cada uno internamente, como mencioné anteriormente. Por ejemplo, use un diccionario de varias teclas (std :: multimap en C++, no está seguro acerca de PHP) para almacenar (representación uniforme) -> (representación de entrada). Dependiendo de la implementación del contenedor, puede obtener búsquedas inversas u ordenar claves de forma gratuita.

-2

¿qué pasa con strtotime()?

+6

No creo que hayas leído toda la pregunta. –

+0

lo hice. strtotime puede validar una parte de los formatos de fecha que espera. usa eso en conexión con algunas expresiones regulares y estás configurado. – dusoft

+0

Sí, sigue, declárame, tu ego no puede aceptar múltiples opiniones. – dusoft

0

Lo más importante, creo, es enumerar todas las posibillidades (o agruparlas de algún modo) y preparar expresiones regulares para cada opción, y sobre esa base identificarlo y manejarlo.

1

Puede considerar implementar su propia clase personalizada de tipo DateTime. No estoy seguro de cuáles son sus requisitos, pero podría ver que tiene propiedades para BC/AD, formateo, etc. Con un poco de reflexión, no debería ser mucho más difícil que implementar una clase de tipo Money si eso le resulta familiar. .

La razón por la que sugiero esto es que 200 aC y 1492-10-07 son muy diferentes, incluso en cuanto al formato.Hablando mal, si tratas BC AD, es posible que también puedas obtener los cálculos que necesitas.

2

¿Qué pasa con el uso de Zend_Date. Zend's date library es una muy buena biblioteca de utilidad de fecha. Puede funcionar de forma independiente o con otras bibliotecas Zend y puede funcionar con date_default_timezone_set() para que las fechas se analicen automáticamente para la zona horaria configurada y funcionará para fechas fuera del rango de marcas de tiempo de Unix. Puede ser un poco largo escribir algunas veces, pero sus puntos fuertes superan en gran medida sus debilidades.

Puede que tenga que implementar su propio análisis personalizado para BC/AD ya que no estoy seguro de que funcione para eso, pero podría valer la pena intentarlo.

Pear also has a date library que valdría la pena mirar, sin embargo, no lo he usado y he escuchado de muchas personas que prefieren el paquete Zend_Date al de Pear's Date.

Siempre puedes escribir la tuya, pero ¿por qué reinventar la rueda? Si no se enrolla como desea, tómelo y mejore;)

1

Como tiene el control de la interfaz de entrada, sin pérdida de generalidad podemos suponer que habrá un año/mes/día por separado enteros (validar correctamente para ... ser entero :). Digamos que ese año será negativo para indicar BC.

Ante todo ... la respuesta obvia (parcial): checkdate(). Esto está bien para años> = 1, como dice la documentación de la función.

que estés atascado por lo tanto, el problema de qué hacer si el año < = 0.

Hagamos un side-trek aquí y ver por qué puede ser un gran problema ...

De acuerdo con la Enlace de Wikipedia arriba, el calendario juliano entró en vigor en el año 45 a. Este calendario es, para todos los propósitos prácticos, idéntico al calendario gregoriano que usamos hoy. La diferencia es que hay una compensación de diez días entre ellos; el último día del calendario juliano fue el jueves 4 de octubre de 1582, y fue seguido por el primer día del calendario gregoriano, el viernes 15 de octubre de 1582 (el ciclo de los días de la semana no se vio afectado).

Esto ya significa que las fechas en el rango del 5 de octubre de 1582 al 14 de octubre de 1582 (inclusive) no son válidas si está siguiendo el calendario gregoriano; ellos nunca han existido

Yendo hacia atrás desde allí, estás bien hasta el 45 aC. Desde el 46 aC hacia atrás, se usó el Roman calendar en lugar del Julian.

No voy a entrar en ese lío aquí, pero simplemente menciono que dado que ese calendario era bastante diferente al gregoriano, sus usuarios no estarán preparados para ver un "formulario de entrada de fecha del calendario romano". Mi sugerencia es, mejor hacer que su aplicación sea utilizable que técnicamente correcta.

Si se puede suponer que nadie en su sano juicio conocería realmente una fecha de BC al día, o sabe cómo especificarla adecuadamente incluso si lo hicieran, puede suponer arbitrariamente que todas las fechas BC son de la forma 1 /1 AÑO. Por lo tanto, su interfaz puede desactivar los controles de mes/día si se marcó una casilla de verificación "BC", tiene cuadros de grupo separados para BC y AD, o cualquier otra cosa apropiada.

El único problema restante después de todo esto, según lo veo, es verificar fechas para años bisiestos. Esos fueron introduced with the Julian calendar, but not actually implemented correctly until 8 AD.

El último enlace arriba documenta que durante 45 BC - 4 años bis (inclusive) años bisiestos no se calcularon correctamente. A es año bisiesto función que da cuenta de que la inconsistencia, más el/interruptor gregoriano Julian sería:

define('YEAR_JULIAN_CALENDAR_INTRODUCED', -45); 
define('YEAR_JULIAN_CALENDAR_LEAP_IMPLEMENTED_CORRECTLY', 8); 
define('YEAR_GREGORIAN_CALENDAR_INTRODUCED', 1582); 

function is_leap_year($year) { 
    if($year < YEAR_JULIAN_CALENDAR_INTRODUCED) { 
     return false; // or good luck :) 
    } 
    if($year < YEAR_JULIAN_CALENDAR_LEAP_IMPLEMENTED_CORRECTLY) { 
     return $year <= -9 && $year % 3 == 0; 
    } 
    if($year < YEAR_GREGORIAN_CALENDAR_INTRODUCED) { 
     return $year % 4 == 0; 
    } 
    // Otherwise, Gregorian is in effect 
    return $year % 4 == 0 && ($year % 100 != 0 || $year % 400 == 0); 
} 

Armado con esto, se podía entonces escribe una función que le indica correctamente el número de días que hay en cada año. La substracción/adición de fecha podría entonces construirse sobre eso.

Después de toda esta discusión (Admiro el coraje de cualquiera que haya leído hasta aquí :) Tengo que preguntar:

¿Cuánto exactitud lo que realmente necesita?

Si decide que debe analizar los "detalles técnicos", implementaría personalmente las funciones mencionadas anteriormente, y luego: a) Utilícelas como mi biblioteca de fechas artesanal, o b) Utilícelas para verificar que cualquier biblioteca de terceros que me interese se haya implementado correctamente.

Si no tiene que hacer eso, pretenda que nunca ha leído todo esto. :)

+0

Este es esencialmente el contenido de una larga discusión que tuve con otra persona involucrada en el desarrollo de esta aplicación. Al final, decidimos dejar que los usuarios calibren sus propias fechas :) – Sampson

0

segunda respuesta, después de actualizar la pregunta de Jonathan:

Por comparación de fechas sencillo, que 'd tiene que usar algo similar a número entero, o una biblioteca de clases que soporta data de 9999 aC (no lo hago saber de uno).

Podrías simplemente especificar las veces como el número de segundos desde 1/1/10000 aC (tira tu propia época); 64 bits serían más que suficientes para eso. Para hacer eso, necesitas resolver uno o dos problemas.

A. Cómo hacer entradas de 64 bits en PHP.

PHP garantiza 31 bits para enteros. Por lo tanto, usted podría hacer uno de los siguientes:

  1. Escribe tu propia clase 62-bit número entero, que almacena los bits en dos miembros enteros privados. 62 bits son también más que suficientes.

    Esto sería doloroso, y probablemente rápido. Mayor ventaja: no dependería de ninguna extensión de PHP.

  2. Utilice BCMath o GMP para hacer números enteros de precisión arbitraria.

    Voy a intentar esto primero, si la portabilidad no es imprescindible. Aunque podría ser más lento que aceptable. Mayor ventaja: no se arriesga a equivocar el código de manipulación de bits.

Con la clase 60-o-tan-bit número entero en la mano (con soporte de suma/resta/comparación través de métodos o funciones de ayuda correspondiente), a continuación, puede escribir una clase CustomDateTime que apoya toda su lógica requerida . Esta clase incluiría todo el código "fecha-a-int" y viceversa (por ejemplo, construcción); todas las operaciones que tienen-puramente-hacer-la-int-implementación (por ejemplo, la comparación) simplemente se reenviarían a su clase entera.

B. Cómo hacer entradas de 64 bits en la base de datos.

Todas las bases de datos lo hacen sin problemas. Sin embargo, seguramente necesita seguir esta ruta porque, por ejemplo, MySQL no admite fechas anteriores al 1000 AD. No sé sobre otros proveedores.