jueves, 27 de marzo de 2008

Haciendo un mirror de subversion

Subversion -al igual que CVS- es un software de control de versiones, todo desarrollador de software debe contar con una herramienta de este tipo y el no hacerlo simplemente es una gran irresponsabilidad (o estupidez).

Una (pero no la principal) de las ventajas de utilizar subversion es que sirve como una herramienta de respaldo de código. Cuando uno mantiene respaldos, es importante que dichos respaldos sean almacenados en instalaciones diferentes a la fuente de información original, esto permite afrontar situaciones extremas como incendios o terremotos.

Esta condición básica de seguridad no se da en mi caso ya que en mi empresa nuestro servidor subversion está en las mismas instalaciones donde trabajamos, por lo tanto nos vimos enfrentados al problema de respaldar en servidores remotos para lo cual implementé un mecanismo de mirroring de subversion mediante el comando svnsync.

Modelo de operación
El mecanismo que escogí para respaldar subversion es un mirroring que se actualiza cada vez que se hace COMMIT en el repositorio maestro, replicando los cambios en el servidor de respaldo. Este modelo de respaldo tiene las siguientes características:
  • El mirror está siempre al día.
  • El mirror es actualizado sólo con los deltas enviados en el COMMIT por lo que la transferencia de datos es mínima.
  • En caso de pérdida del servidor principal, basta con sacar una copia del mirror para poder restaurar el servidor principal.
  • El mirror presta las mismas funcionalidades del servidor principal con la excepción que es de sólo lectura.

En términos resumidos la implementación del mirroring se realiza sacando una copia del repositorio, instalarla en un servidor de respaldo y finalmente se configura el servidor principal para que luego de cada COMMIT se ejecute el comando svnsync que replicará todos los cambios en el servidor de respaldo (mirror).


Prerrequisitos
Se asume que tienes conocimientos básicos de LINUX y de cómo administrar un servidor subversion, en caso que no seas muy experimentado puedes consultar el manual de subversion.

IMPORTANTE: El procedimiento aquí descrito se enfoca en el proceso de creación y configuración del mirror. Debes asegurarte de realizar las configuraciones de control de acceso y seguridad necesarias.

Debemos contar con dos servidores (principal y respaldo) que tengan instalado subversion 1.4 o superior, es deseable que ambos servidores tengan la misma versión.

El repositorio a respaldar lo vamos a denominar proyecto. En los ejemplos, el servidor de mirror usará el nombre de dominio mirror.com.


Procedimiento de configuración
El primer paso es crear el repositorio principal (si ya tienes un repositorio que quieres respaldar, puedes omitir este paso).
svnadmin create proyecto
Luego, hacemos una copia del repositorio recién creado, lo comprimimos y eliminamos la copia temporal. (Utilizamos hotcopy para evitar eventuales errores si justo al momento de hacer la copia alguien realizó un COMMIT en el repositorio):
svnadmin hotcopy proyecto proyectobackup
tar cfz proyectobackup.tgz proyectobackup
rm -rf proyectobackup
A continuación transferimos el archivo comprimido al servidor de respaldo, donde lo descomprimimos y restauramos el nombre del repositorio a proyecto:
tar xfz proyectobackup.tgz
mv proyectobackup proyecto
cd proyecto
Ahora configuramos el repositorio mirror para no permitir que los usuarios hagan COMMIT, solamente se permitirá que haga COMMIT una cuenta de usuario especial que llamaremos syncuser, con contraseña secret (debes definir escoger otra contraseña) y le damos permisos de escritura completos:
echo "syncuser = secret" >> conf/passwd
echo "[/]
syncuser = rw" >> conf/authz
(las ultimas 2 lineas son un solo comando)

Para evitar que el resto de usuarios pueda hacer COMMIT creamos el archivo (con permisos de ejecución) hooks/start-commit dentro del repositorio con el siguiente contenido:

#!/bin/sh

USER="$2"
if [ "$USER" = "syncuser" ]; then exit 0; fi

echo "No se permite hacer commit en el repositorio de respaldo" >&2
exit 1

Luego debemos crear el script (con permisos de ejecución) hooks/pre-revprop-change el cual es requerido por el comando svnsync:

#!/bin/sh

USER="$3"

if [ "$USER" = "syncuser" ]; then exit 0; fi

echo "No se permite hacer commit en el repositorio de respaldo" >&2
exit 1
Con estos pasos hemos creado una copia del repositorio proyecto, hemos creado el usuario syncuser y configuramos el repositorio para que nadie más pueda hacer COMMIT. A continuación volvemos al servidor principal.



En el servidor principal vamos a configurar el repositorio para indicarle que tiene un mirror (referenciado en el siguiente ejemplo con el nombre de dominio mirror.com):
svnsync init svn://mirror.com/proyecto svn://localhost/proyecto --username syncuser --password secret
Finalmente ejecutamos el proceso de sincronización, el cual copiará los datos hacia el servidor de respaldo:
svnsync sync svn://mirror.com/proyecto --username syncuser --password secret
Con estos pasos hemos configurado el servidor principal para que apunte al mirror previamente creado. El paso final es automatizar el proceso de respaldo.

Hay dos estrategias para sincronizar el mirror. La primera es realizar la sincronización en un proceso aparte, por ejemplo todas las noches. La segunda estrategia es que la sincronización se realice inmediatamente después de cada COMMIT.

Alternativa 1: Respaldo asincrónico
Esta estrategia es útil cuando la velocidad del enlace entre el servidor principal y el mirror es muy lenta o cuando los COMMITs que se realizan son muy pesados.

Basta con agregar una linea al cron, a continuación se muestra un ejemplo que realiza la sincronización todas las noches a las 04:15 hrs.:
15 04 * * * svnsync sync svn://mirror.com/proyecto  --username syncuser --password secret

Alternativa 2: Respaldo
en tiempo real
Esta estrategia tiene la ventaja que el mirror permanecerá siempre actualizado cada vez que hagamos un COMMIT.

Implica editar (o crear) el script ejecutable hooks/post-commit y agregarle el siguiente código:

# Determino el nombre del repositorio
NOMBREREPO=$(echo $REPOS | cut -d"/" -f4)

svnsync sync svn://mirror.com/$NOMBREREPO --username syncuser --password secret

Luego de esto tendremos nuestro mirror de subversion configurado.

Usando el lenguaje HTML de manera correcta

El lenguaje HTML es el que le da vida a la web; el requisito fundamental de todo desarrollador web es conocer como mínimo el lenguaje HTML, ya que sobre este se agregan otras tecnologías como CSS y Javascript.

Actualmente la especificación de HTML mayormente recomendada es la XHTML 1.0, la cual en términos simples es HTML pero con las reglas del lenguaje XML. Otras versiones de HTML recomendadas por la W3C son XHTML 1.1, y la más antigua HTML 4.01.

Podríamos resumir las principales reglas de XHTML en las siguientes:
  • Todos los elementos no vacíos deben tener su tag correspondiente de cierre (<p>hola</p>)
  • Los tags que originalmente no consideraban tag de cierre deben utilizar la versión reducida de XML, es decir poniendo un slash al final del tag. (<br/>)
  • Los valores de los atributos deben estar entre comillas simples. (<a href="index.html">)
  • Los nombres de los tags y atributos HTML deben estar en minúsculas.
  • Todos los atributos deben tener un valor. (<option selected="selected">)
Adicionalmente XHTML 1.0 incluye 3 variaciones:
  • XHTML Strict: cumplimiento estricto de la especificación.
  • XHTML Transitional: permite algunas excepciones de la especificación, es la más comúnmente utilizada.
  • XHTML Frameset: soporte para frames.
(HTML 4.01 también permite especificar strict, transitional y frameset)



¿Cómo le decimos al browser la versión de HTML a utilizar?
Para indicarle al navegador la versión de HTML, se utiliza una declaración al comienzo del archivo (en la primera línea) denominada DOCTYPE (document type declaration) la cual indica las "reglas" del documento XML.

Para el caso de XHTML 1.0 tenemos las siguientes alternativas [w3c]:


XHTML 1.0 Strict
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

XHTML 1.0 Transitional
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

XHTML 1.0 Frameset
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

Para la mayoría de los casos recomiendo utilizar XHTML 1.0 Transitional.


Quirksmode
Antiguamente no existía esta técnica para poder decirle al navegador la versión de HTML que se está utilizando, por lo que en los casos de las páginas HTML que no incluyan un DOCTYPE, los navegadores utilizan un modo de compatibilidad denominado quirksmode en el cual asumen que la página está construida con técnicas obsoletas y errores comunes del siglo pasado por lo que interpreta la página con reglas diferentes.

lunes, 24 de marzo de 2008

WebKit alcanza un 95/100 en el Acid3 test

WebKit (que es el core de Safari y que está basado en el motor KHTML de Konqueror) acaba de anunciar que su más reciente versión en desarrollo obtuvo un 95% de cumplimiento del test Acid3.

Esto demuestra que Internet Explorer 8 está a años luz del cumplimiento de los estándares ya que recién su versión beta 1 logró cumplir con el test Acid2. Del Acid3 aún ni hablan.

El puntaje para otros motores de navegadores (en su versión de desarrollo) es el siguiente:
  • Presto (Opera): 77/100
  • Gecko (Mozilla Firefox): 71/100
  • KHTML (Konqueror): 62/100
  • Trident (Internet Explorer): 17/100
La imagen de referencia de Acid3 es la siguiente, que corresponde a un conjunto de pruebas de CSS3, DOM2 y SVG entre otras hierbas.


Además debo decir que la versión Windows de Safari 3.1 recién liberada (con polémica incluida) me dejó gratamente impresionado por su velocidad para renderizar las páginas, tal como profesa su publicidad.

sábado, 22 de marzo de 2008

El dilema de Internet Explorer 8

Desde el momento en que se liberó Internet Explorer 7 -el cual fue un gran avance con respecto a su viejo antecesor IE6- ya se estaba trabajando en la versión 8 del popular navegador.

Una de las principales cualidades que promete esta nueva versión será el gran salto en el cumplimiento de los estándares W3C, específicamente en lo que respecta a XHTML y CSS2.1. La versión beta 1 ya fue liberada y cumple con el acid test 2. Sin embargo, el equipo de desarrollo se está topando con un grave dilema al respecto, digamos que el 99.9% de los sitios de internet no son fieles cumplidores de los estándares por lo que podrían (y van a) ser mal interpretados por Internet Explorer 8.

Esta situación era de esperar ya que el ciclo de desarrollo de un sitio web realizado por desarrolladores "responsables" consiste en desarrollar basado en los estándares para luego probar, corregir y parchar el diseño en base a pruebas en Internet Explorer, Firefox y ocasionalmente Opera y Safari. Y en el caso de los desarrolladores irresponsables "no-responsables" simplemente basta con que se vea bien en IE.

El equipo de desarrollo de Redmond inicialmente tomó la decisión de que su nuevo producto por defecto utilice el modo "IE7 standard-compliant mode", es decir, interpretará el código de un modo que los sitios que actulamente se visualizan correctamente en IE7 sigan haciéndolo y los sitios que cumplan fielmente los estándares deberían poner un tag <meta> para indicar que sean interpretados en "Full standard-compliant mode".

Esta postura causó indignación en los miembros más fanáticos de la comunidad de los estándares web, pero finalmente Microsoft cambió su postura y el nuevo IE8 por defecto interpretará las páginas en "Full standard-compliant mode":
We’ve decided that IE8 will, by default, interpret web content in the most standards compliant way it can. This decision is a change from what we’ve posted previously.

Es una postura que francamente no me esperaba de Microsoft:

We think that acting in accordance with principles is important, and IE8’s default is a demonstration of the interoperability principles in action. While we do not believe any current legal requirements would dictate which rendering mode a browser must use, this step clearly removes this question as a potential legal and regulatory issue. As stated above, we think it’s the better choice.

Ahora bien, este cambio que se vendrá con IE8 augura un gran problema para los actuales sitios ya que deberán realizar correcciones a su código HTML para poder ser correctamente visualizados en IE8. La siguiente es una captura de Google Maps en IE8:


La solución más sencilla es indicarle a IE8 que la página debe ser interpretada como IE7 mediante el siguiente tag <meta> descrito en A List Apart:
<meta equiv="X-UA-Compatible" content="IE=7"/>
Les recuerdo que aún falta mucho tiempo para que IE8 sea liberado por lo que aún pueden aparecer muevas noticias al respecto.

Como artículo relacionado les recomiendo un excelente artículo de Joel on Software (algo que no es novedad en él) que describe de manera magistral el problema de la compatibilidad en la web: Martian Headsets.

lunes, 17 de marzo de 2008

Zend Framework 1.5 ya está aquí

ZEND acaba de liberar la versión 1.5 de Zend Framework, el cual es uno de los mejores frameworks disponibles para PHP.

Esta noticia se suma a la de hace algunas semanas publicada por Andi Gutmans que confirma que Zend Framework formará parte de Ubuntu 8.04 vía el repositorio Ubuntu Universe.

Entre las ventajas de Zend Framework se cuenta con que es un código eficiente, implementa el modelo MVC y posee una gran cantidad de módulos extremadamente útiles.

En mi caso particular no utilizo ningún framework ya que muchas veces agregan un overhead adicional a los scripts y además me obliga a adaptar mi diseño de la aplicación a la de esas herramientas. Por otro lado, este último punto es una de las ventajas de los frameworks, el hecho de obligar al usuario a ser ordenado permite desarrollar código que es más mantenible en el tiempo. Otra de las ventajas evidentes de un framework es que evita tener que reinventar la rueda creando rutinas básicas como por ejemplo control de sesión y manejo de templates lo cual nos permite construir aplicaciones web contando con una base preexistente.

sábado, 15 de marzo de 2008

Lo nuevo de PHP 5.3

La versión actual de PHP es la 5.2.5 y la siguiente versión próxima a ser liberada es la 5.2.6 que principalmente incluye parches que resuelven bugs y problemas seguridad.

Sin embargo, desde hace varios meses se está trabajando en la versión 5.3 que incorporará nuevas funcionalidades que desde hace algún tiempo estaban siendo pedidas por parte de la comunidad. Esta nueva versión debiera ser publicada a mediados de 2008.


Namespaces (¡por fin!)
Sin duda el mayor cambio en esta nueva versión es el soporte a namespaces. Hasta ahora la técnica que se utilizaba para evitar la colisión de nombres era simplemente agregarle un prefijo al nombre de cada clase obteniendo nombres tan largos como: MDB2_Driver_Datatype_Common.

Algunas de las características de la implementación de namespaces son las siguientes:
  • Namespaces para clases, funciones y constantes.
  • Múltiples namespaces en un mismo archivo.
  • Se incorpora la constante __NAMESPACE__.
A continuación incluyo un ejemplo básico de cómo será el uso de namespaces en PHP 5.3:
<?php

namespace DB;
class MyDate {}
$clase1 = new MyDate();

namespace TYPES::BASIC;
class MyDate {}
$clase2 = new MyDate();

var_dump(
get_class($clase1),
get_class($clase2),
);

?>
La salida de este script es la siguiente:

string(10) "DB::MyDate"
string(20) "TYPES::BASIC::MyDate"
En el repositorio CVS de PHP podrán encontrar un README con más detalles de la implementación de namespaces.



Operador "?:"
Esta es otra funcionalidad que llevaba bastante tiempo siendo solicitada por algunos desarrolladores. Su utilidad es similar a la que presta el operador || en javascript, tal como muestro en el siguiente ejemplo donde se le asigna un valor por defecto a las variables width y height:
<script type="text/javascript">

function foo(width, height) {
width = width || 640;
height = height || 480;
// ...
}

</script>
En PHP podríamos hacer las siguientes validaciones:
<?php

$a = true ?: false; // true
$a = false ?: true; // true
$a = "" ?: 1; // 1
$a = 0 ?: 2; // 2
$a = array() ?: array(1); // array(1)
$a = strlen("") ?: strlen("a"); // 1

?>
Este ejemplo fue obtenido de http://ilia.ws

Como vemos no es una super-hiper-killer-funcionalidad, pero para los usuarios habituados a javascript es un placer contar con ella.



Otras mejoras
Otras mejoras -además de las habilituales correcciones de bugs, mejoras en seguridad y optimización de código- incluidas en PHP 5.3 son las siguientes:
  • Mejora en performance de md5().
  • Optimización de manejo de excepciones.
  • Nueva constante __DIR__. (esto nos evitará hacer dirname(__FILE__))
  • Nueva función mágina __callStatic() que complementa a __call().
  • El procesamiento de métodos estáticos se movió desde el tiempo de compilación a tiempo de ejecución (Late Static Binding).
  • Nuevo driver nativo de MySQL (MySQLnd) diseñado especialmente para PHP.
  • Soporte para archivos .ini (al estilo de .htaccess).
  • Nuevas funcionalidades de OpenSSL destinadas a simplificar las implementaciones de OpenID en PHP.
  • Profiling de XSLT. ($xsltprocessor->setProfiling("/tmp/profile.txt"));
  • Nueva constante de error E_DEPRECATED.
  • Garbage collector (gc_enable()).
  • NOWDOC, similar a los HEREDOC pero que no interpreta las variables como $myvar.

viernes, 14 de marzo de 2008

Manteniendo PHP al día

Uno de los dolores de cabeza con que tuve que lidiar hasta hace algún tiempo fue que las diversas distribuciones de linux incluían versiones antiguas de PHP por lo que tenía que compilar el código fuente para tener versiones más recientes.

Tengo dos motivos principales para querer instalar versiones de PHP actualizadas:
  1. En cada release se corrigen innumerables bugs detectados en las versiones anteriores.
  2. Nuevas funcionalidades que se agregan a PHP, como por ejemplo en la versión 5.2 se incorporó soporte para canonicalización [W3C] (C14N) las cuales son imprescindibles para la firma electrónica en XML [W3C] (XMLDsig).
Comúnmente para instalar una versión reciente de PHP compilaba el código fuente, lo cual toma bastante tiempo y puede ser bastante engorroso en caso de que el sistema operativo no tenga todas las dependencias para compilar, la desinstalación de código compilado siempre ha sido difícil y para qué hablar del proceso de actualización de versión en ambientes productivos, siempre era una incógnita cuánto iba a demorar la actualización (y mientras el sistema está detenido sin dar servicio a los clientes). Siempre he preferido administrar los paquetes mediante RPM ya que facilita mucho la instalación y desinstalación de software, la actualización de versión es bastante rápida, por lo general uno no se topa con sorpresas durante la instalación y el procedimiento de vuelta atrás es bastante sencillo ya que consiste sólo en volver a instalar los RPM de la versión previa.

Un día, todas estas preocupaciones quedaron en el olvido gracias a Remi Collet quien matiene un repositorio de paquetes RedHat que incluye las versiones más recientes de PHP. Usualmente el repositorio se actualiza en no más de una semana después de que PHP libera una actualización. Además, incluye módulos PEAR y PECL para PHP y actualizaciones para MySQL, Firefox y Thundebird.

Una vez configurado correctamente el repositorio, basta con ejecutar el siguiente comando para actualizar PHP a la versión más reciente:

yum update php

Al ser orientado a RedHat, el repositorio cuenta con releases para las versiones más comunes de Fedora Core, CentOS y RedHat. Además publica los SRPM, es decir, los RPM que contienen los códigos fuentes lo cual permite que uno recompile y genere sus propios RPM. Provee de instrucciones de instalación para yum, apt y smart.


Como nota final, tengo que aclarar que no es recomendable estar actualizando el software con demasiada frecuencia ya que cada proceso de actualización requiere de pruebas exhaustivas y eventualmente podría comenzar a fallar código que antes funcionaba correctamente.

jueves, 13 de marzo de 2008

Motivación

Cada vez que participo en un proyecto de software aprendo una enormidad de nuevos temas que de alguna manera las quiero ir dejando registradas. Usualmente son temas que han requerido de bastante tiempo de investigación o son producto de la experiencia.

Hace más de un año que tomé la decisión de crear un blog para ir registrando información útil e interesante relacionada con mi trabajo en desarrollo web, sin embargo había pospuesto su inicio debido a que no había decidido la orientación que le iba a dar.

Finalmente tomé la decisión de que si no comenzaba a publicar ahora mismo, no lo iba a hacer nunca por lo que aproveché la contingencia del retraso del cambio de hora en Chile para bautizar mi blog y comenzar a publicar algunos artículos.

Existen cientos de sitios que hablan sobre PHP, Apache, javascript, XML, firma electrónica y otros temas. Sin embargo, casi todos hablan de lo mismo y son pocos los que profundizan en temas específicos. En base a esto, la orientación de este blog no será hablar de temas básicos -y ser uno más de esos cientos de sitios- sino que trataré temas más específicos pero que han sido extremadamente útiles en mi trabajo.

Hay varios temas que me interesan y no tengo claro por cuál voy a comenzar, algunos de los artículos que estoy preparando son:
  • Usando HTML y XHTML correctamente
  • Firma electrónica usando OpenSSL y PHP
  • Encriptación en PHP
  • Recomendaciones para almacenamiento de password
  • Trabajar con XML en PHP
  • Buenas prácticas en PHP
  • Compilación y generación de RPMs de PHP
  • Compilación de módulos PECL para PHP
  • Cración de módulos PECL para PHP (programar en lenguaje C)
  • Memcache
  • Optimización de apache: Configuración de cache
  • Optimización de apache: Compresión de datos
  • Redescubriendo javascript
  • Biblioteca YUI para javascript
  • Manipulación de DOM mediante javascript
  • Convivieno con UTF-8
  • Desarrollando con Firebug
  • OpenID
  • WebServices y SOAP

sábado, 8 de marzo de 2008

PHP y las zonas horarias

Abstract
Debido a la modificación del cambio de horario en Chile para este año 2008 que traslada el término de horario de verano desde la noche del 8 de marzo hacia la noche del 29 de marzo, nos hemos topado con la necesidad de corregir nuestros sistemas para que se adapten a este cambio.

En LINUX, la solución más apropiada es la actualización manual de la definición de la zona horaria America/Santiago o (idealmente) la actualización del paquete tzdata (una vez que hayan publicado la actualización correspondiente).

Sin embargo, algunos software como por ejemplo PHP y JAVA mantienen su propia información de zona horaria (zoneinfo) por lo que ingnorarán las reglas que tenga definido el sistema operativo. Esto significa que -aunque yamos parchado nuestro sistema operativo- mantendrán el problema del cambio de hora.


Solución para PHP
PHP desde la versión 5.1 mantiene su propia base de datos de zona horaria por lo que ignorará las reglas de zona horaria definidas en el Sistema Operativo. La solución a este problema es la actualización del módulo timezonedb incluido con PHP. Lamentablemente al 8 de marzo de 2008 la versión más reciente de timezonedb no incluye la actualización a la zona horaria America/Santiago por lo tanto tendremos que parchar el módulo antes de instalarlo.

Gestión de módulos en PHP mediante PECL
PHP está compuesto por un core llamado Zend Engine y un subconjunto de módulos que le agregan funcionalidades como manejo de strings, manejo de fechas, soporte a sesiones, etc. El conjunto de módulos adicionales de PHP se llama PECL.

Es posible agregar módulos PECL adicionales que no vienen incluidos en la distribución original mediante el comando pecl el cual viene en el paquete php-devel.

En las distribuciones RedHat y sus derivados (Fedora Core, CentOS, etc.) la instalación de paquetes se puede realizar con el comando yum, el cual utilizaré como ejemplo en este artículo. En otras distribuciones existen otros gestores de paquetes y los nombres de los paquetes podrían variar.

En caso que no tengamos el paquete php-devel, lo instalamos con el comando:
yum install php-devel
Ahora que contamos con el comando phpize, el procedimiento para instalar cualquier módulo PECL es el siguiente:
pecl instal nombre_paquete
Por lo tanto, para instalar el módulo timezonedb se ejecutaría el siguiente comando
pecl install timezonedb
Sin embargo, tal como indiqué al principio, esto no resolverá el problema porque en este momento (8 de marzo de 2008), este módulo aún no incorpora la actualización para America/Santiago.

Actualizado: Hace algunos días publiqué un bug en el sitio de PHP y se acaba de liberar la versión de timezonedb que actualiza la zona horaria para America/Santiago, por lo que desde ahora ya no es necesario realizar los pasos que indico en "Instalar módulo timezonedb parchado" sino que simplemente se debe ejecutar el comando indicado a continuación y luego seguir con los pasos indicados más abajo en "Habilitando extensión":
pecl install timezonedb


Instalar módulo timezonedb parchado
El procedimiento a seguir es descargar los fuentes de timezonedb (que están en lenguaje C), modificar la definición de la zona America/Santiago y luego agregar el módulo a PHP. Este procedimiento requiere de conocimientos básicos del lenguaje C.

En primer lugar descargamos los fuentes mediante:
pecl download timezonedb
Esto descargará un archivo con la versión más reciente del módulo que en mi caso fue timezonedb-2007.11.tgz. Lo descomprimimos y entramos al directorio recien creado:
tar xfz timezonedb-2007.11.tgz
cd timezonedb-2007.11
El archivo timezonedb.h es el que contiene la información de las zonas horarias. He publicado una versión parchada que contiene la información actualizada para America/Santiago en base a la versión timezonedb-2007.11. A contnuación descargamos y descompriminos el archivo.
wget http://www.joserodriguez.cl/blog/2008/03/timezonedb.h.gz
gunzip timezonedb.h.gz
Luego, estamos en condiciones de compilar el módulo ejecutando los siguientes comandos:
phpize
./configure
make
Luego, como usuario root, instalamos el módulo.
make install


Habilitando extensión

Habilitamos la extensión en el archivo php.ini el cual usualmente se encuentra en /etc/php.ini. En algunas distribuciones tales como UBUNTU existen dos archivos php.ini, ambos deben ser actualizados.
echo "extension=timezonedb.so" >> /etc/php.ini


Validando instalación
Para verificar que la actualización funcione correctamente podemos ejecutar el siguiente script:
<?php

f(1204988400);
f(1205074800);
f(1206802800);
f(1206889200);

function f($time) {
echo date("Y-m-d G:i:s O e I\n", $time);
}

?>
Cuya salida debería ser la siguiente:
2008-03-08 12:00:00 -0300 America/Santiago 1
2008-03-09 12:00:00 -0300 America/Santiago 1
2008-03-29 12:00:00 -0300 America/Santiago 1
2008-03-30 11:00:00 -0400 America/Santiago 0
En caso que ocurra algún error fatal al ejecutar PHP, como por ejemplo segmentation fault, se puede deshabilitar la extensión eliminando la línea recien agregada a php.ini.

Actualizado el 16 de marzo de 2008.

viernes, 7 de marzo de 2008

¿Cómo corrijo la zona horaria?

En vista del retraso de este año en el cambio de la zona horaria para Chile intentaré describir el manejo de la hora en los sistemas computacionales y dar algunas luces de cómo es el procedimiento adecuado para actualizar el cambio de hora. He omitido algunos conceptos y generalizado en algunos aspectos con el fin de no hacer tan compleja la explicación.

Si no han vivido en una caverna durante toda su vida, debieran saber que el planeta tierra se divide en 24 husos horarios donde cada uno posee una diferencia de una hora con respecto al contiguo. Por efectos prácticos cada país hace coincidir el huso horario con sus limites geográficos y utiliza el término de zona horaria.

Todos los husos horarios o zonas horarias se definen en función del Tiempo universal coordinado (UTC) el cual está fijado en el meridiano de Greenwich (GMT-0), de esta manera nuestro país normalmente está en GMT-4.

Ahora bien, cada país realiza algunos ajustes en su horario, por ejemplo Chile en la época estival utiliza el horario de verano el cual nos mueve desde GMT-4 a GMT-3. Debido a que es complejo saber todas las reglas de cada país, se definen nuevas zonas horarias que para el caso de Chile son America/Santiago en el territorio continental y Pacific/Easter para la isla de Pascua.

Como vivimos en un mundo globalizado, podríamos asumir que la "hora oficial del mundo" es la correspondiente a GMT-0 (simplemente GMT o UTC), de hecho nuestros computadores almacenan todas las fecha en GMT.

Por ejemplo, (aquí en Chile: America/Santiago) si tengo que agendar una reunión el 10 de enero (que corresponde al horario de verano), al registrar la fecha "10 de enero de 2008 15:00:00" mi software de agenda -antes de guardarla en su base de datos- la convertirá a hora "universal" (o GMT) por lo que le sumará 3 horas quedando guardada como "10 de enero de 2008 18:00:00". Luego, cada vez que mi agenda me muestre la fecha, leerá la fecha en GMT, la modificará según mi zona horaria y luego de eso me la mostrará.


Compliquemos las cosas...
Lo estándar es que el término del horario de verano (regresar de GMT-4 a GMT-3) fuese el segundo sábado de marzo (al final del día), pero para este año 2008 el cambio se realizará el sábado 29 de marzo.

Para los usuarios domésticos, tener algunas fechas equivocadas puede ser aceptable, pero en sistemas corporativos eso puede ser muy peligroso.

Alternativa N°0: Sincronizar con un servidor ntp (servidor de hora)
Un servidor de hora permite que nuestro equipo tenga la hora actualizada, pero estos servicios conversan en UTC y luego el equipo local ajusta la hora local en base a la zona horaria, por lo tanto no sirve para el problema del cambio de hora. De todos modos es una buena práctica tener nuestro equipo sincronizado con ntp.shoa.cl.



Alternativa N°1: Adelantar la hora
La solución más sencilla que a uno se le ocurre es que -luego de que suceda el cambio de hora- adelantemos el reloj del sistema y luego el 29 de marzo volver a retrasarlo. Este cambio haría que todas las fechas almacenadas entre el 9 y 29 de marzo queden guardadas erróneamente ya que en vez de considerarlas como GMT-4 (horario de verano) las tomará como GMT-3.

Repitamos el ejemplo inicial: Si agendo una reunión para el día 12 de marzo a las 10:00 (hora local America/Santiago), el sistema -al convertirla a UTC- le aplicará la corrección GMT-3 (le sumará 3 horas) convirtiéndola erróneamente en "12 de marzo de 2008 13:00:00" cuando lo correcto es sumarle 4 horas y almacenarla como "12 de marzo de 2008 14:00:00".

Si envío la cita a los demás asistentes, ellos podrían recibir la cita con una hora de anticipación pudiendo pasar más de un mal rato. Mientras nuestro software (o sistema operativo) no actualice las reglas de la zona horaria de America/Santiago este error pasará desapercibido en mi computador porque tanto al guardar la fecha como al leerla utilizará la misma zona horaria GMT-3. Cuando a futuro haga una actualización de mi software (o sistema operativo) es posible que se haya incluido la actualización de la zona horaria y ahí quedaría en evidencia el error haciendo que todas las fechas ingresadas durante esas 3 semanas estén adelantadas en una hora.


Alternativa N°2: Cambiar la zona horaria a GMT-4
Otra alternativa es cambiar la zona horaria manualmente a GMT-4 (cuya zona horaria es ETC/GMT+4, el signo "+" es correcto) para forzar el uso del horario de verano y luego el 29 de marzo volver a America/Santiago. Esto hará que las fechas se almacenen correctamente ya que no habremos cambiado la hora de nuestro computador, pero tiene el inconveniente que asumirá que todas las fechas están en GMT-4 por lo que al consultar una fecha del horario de invierno (por ejemplo "18 de septiembre de 2007 10:00:00"), en vez de restarle 3 horas a la fecha GMT que está almacenada en el sistema, le restará 4.



Alternativa N°3: Actualizar la "definición" de la zona horaria
La solución correcta es actualizar la definición de la zona horaria, es decir, que el sistema sepa que en 2008 el término del horario de verano para America/Santiago es el 29 de marzo y no el 9 de marzo.

El problema de esta solución es que no es fácil realizar el cambio, ya que usualmente consiste en realizar cambios a archivos del sistema operativo o al registro de Windows.


Solución para Windows
Para servidores Windows, Microsoft publicó un comunicado donde recomienda la cambiar la zona horaria (nuestra alternativa N°2) para usuarios domésticos y la actualizar la definición de la zona horaria America/Santiago (nuestra alternativa N°3) para usuarios corporativos.


Solución para LINUX
En servidores LINUX (y en general *NIX) la información de las zonas horarias es mantenida por el paquete tzdata, sin embargo a la fecha de escritura de este artículo, dicho paquete aún no contiene la actualización a nuestra zona America/Santiago, por lo tanto la solución es regenerar la definición del archivo de zona. A continuación describo el procedimiento para distribuciones RedHat, Fedora y CentOS. En otras distribuciones es posible que los comandos y ubicación de los archivos sea distinta.

Al momento de escribir este artículo UBUNTU ya cuenta con una actualización del paquete tzdata: https://bugs.launchpad.net/ubuntu/+source/tzdata/+bug/198129.

Si bien hay muchos servicios que utilizan el sistema operativo para el manejo de la zona horaria, hay software que mantiene su propio módulo de zonas horarias por lo que ignorará estos cambios, por ejemplo (algunas versiones de): PHP 5.1+, ORACLE y JAVA.

Actualizado: Ya se publicó la actulización oficial de tzdata 2008a por lo que ahora -si lo prefieres- en vez de crear el archivo tzdata.txt que indico más abajo, puedes descargar el archivo oficial y luego importar el archivo southamerica como indico a continuación:
cd /tmp
wget ftp://elsie.nci.nih.gov/pub/tzdata2008a.tar.gz
tar xfz tzdata2008a.tar.gz
zic southamerica
En caso que prefieras no descargar el archivo anterior, se debe crear el archivo newzone.txt con el siguiente contenido:

Rule Chile 1927 1932 - Sep 1 0:00 1:00 S
Rule Chile 1928 1932 - Apr 1 0:00 0 -
Rule Chile 1942 only - Jun 1 4:00u 0 -
Rule Chile 1942 only - Aug 1 5:00u 1:00 S
Rule Chile 1946 only - Jul 15 4:00u 1:00 S
Rule Chile 1946 only - Sep 1 3:00u 0:00 -
Rule Chile 1947 only - Apr 1 4:00u 0 -
Rule Chile 1968 only - Nov 3 4:00u 1:00 S
Rule Chile 1969 only - Mar 30 3:00u 0 -
Rule Chile 1969 only - Nov 23 4:00u 1:00 S
Rule Chile 1970 only - Mar 29 3:00u 0 -
Rule Chile 1971 only - Mar 14 3:00u 0 -
Rule Chile 1970 1972 - Oct Sun>=9 4:00u 1:00 S
Rule Chile 1972 1986 - Mar Sun>=9 3:00u 0 -
Rule Chile 1973 only - Sep 30 4:00u 1:00 S
Rule Chile 1974 1987 - Oct Sun>=9 4:00u 1:00 S
Rule Chile 1987 only - Apr 12 3:00u 0 -
Rule Chile 1988 1989 - Mar Sun>=9 3:00u 0 -
Rule Chile 1988 only - Oct Sun>=1 4:00u 1:00 S
Rule Chile 1989 only - Oct Sun>=9 4:00u 1:00 S
Rule Chile 1990 only - Mar 18 3:00u 0 -
Rule Chile 1990 only - Sep 16 4:00u 1:00 S
Rule Chile 1991 1996 - Mar Sun>=9 3:00u 0 -
Rule Chile 1991 1997 - Oct Sun>=9 4:00u 1:00 S
Rule Chile 1997 only - Mar 30 3:00u 0 -
Rule Chile 1998 only - Mar Sun>=9 3:00u 0 -
Rule Chile 1998 only - Sep 27 4:00u 1:00 S
Rule Chile 1999 only - Apr 4 3:00u 0 -
Rule Chile 2000 2007 - Mar Sun>=9 3:00u 0 -
Rule Chile 2008 only - Mar 30 3:00u 0 -
Rule Chile 1999 max - Oct Sun>=9 4:00u 1:00 S
Rule Chile 2009 max - Mar Sun>=9 3:00u 0 -
Zone America/Santiago -4:42:46 - LMT 1890
-4:42:46 - SMT 1910
-5:00 - CLT 1916 Jul 1
-4:42:46 - SMT 1918 Sep 1
-4:00 - CLT 1919 Jul 1
-4:42:46 - SMT 1927 Sep 1
-5:00 Chile CL%sT 1947 May 22
-4:00 Chile CL%sT
Zone Pacific/Easter -7:17:44 - LMT 1890
-7:17:28 - EMT 1932 Sep
-7:00 Chile EAS%sT 1982 Mar 13 21:00
-6:00 Chile EAS%sT
Link America/Santiago Chile/Continental
Link Pacific/Easter Chile/EasterIsland

se debe ejecutar como usuario "root" el comando:
zic  newzone.txt
luego, para verificar que haya quedado bien configurado, se debe ejecutar:
zdump -v America/Santiago | grep 2008
y verificar que en dos de las reglas de cambio de horario indique 30 de marzo (Sun Mar 30). En mi LINUX la salida de este comando es:
America/Santiago  Sun Mar 30 02:59:59 2008 UTC = Sat Mar 29 23:59:59 2008 CLST isdst=1 gmtoff=-10800
America/Santiago Sun Mar 30 03:00:00 2008 UTC = Sat Mar 29 23:00:00 2008 CLT isdst=0 gmtoff=-14400
America/Santiago Sun Oct 12 03:59:59 2008 UTC = Sat Oct 11 23:59:59 2008 CLT isdst=0 gmtoff=-14400
America/Santiago Sun Oct 12 04:00:00 2008 UTC = Sun Oct 12 01:00:00 2008 CLST isdst=1 gmtoff=-10800
Luego hay que ejecutar la herramienta para configurar la zona horaria system-config-date (puede ser por linea de comandos o en el entorno gráfico), escoger America/Santiago y luego "Aceptar".

Algunos servicios requieren ser reiniciados para reconocer los cambios, en caso de ser posible es mejor reiniciar el equipo.


PHP
Tal como comentaba más arriba, PHP desde la versión 5.1 incluye su propio módulo de zona horaria por lo que ignorará las reglas de la zona horaria del sistema operativo. En este caso se puede realizar otro procedimiento que comento aquí.



(Actualizado el domingo 9 de marzo a las 11:30)