Publi

Obtener diferencia entre una zona horaria y UTC en PHP

2744390812_c6e2aa449b_o

Puede parecer extraño pero, a veces, cuando nuestros visitantes son de diferentes franjas horarias conviene adaptar las horas a cada uno de los usuarios. No es plan que en pleno 2015 obliguemos a los usuarios a hacer las conversiones.

También es posible que, estemos extendiendo un CMS que ignore por completo el default_timezone_set o que lo defina a placer, y nos encontremos en un dilema cuando tenemos que tratar con la hora.

Lo más sencillo es utilizar DateTimeZone::offset, podemos hacerlo de la siguiente manera:

1
2
3
<?php
$timezone = new DateTimeZone('Europe/Madrid');
echo $timezone->getOffset(new DateTime());

Teniendo en cuenta que estamos en pleno horario de verano, ahora mismo, en la España peninsular estamos en UTC+2, por lo tanto el Offset devuelto es de 7200 segundos (2horas).
Es más a mí me gusta también mirar el Offset de Caracas, para probar que esto funciona, si hacemos:

1
2
3
<?php
$timezone = new DateTimeZone('America/Caracas');
echo $timezone->getOffset(new DateTime());

Nos devolverá -16200 (un número muy raro, porque allí están en UTC-4:30.

Hasta aquí bien, pero ahora lo queremos almacenar en horas y minutos, más que nada para expresar la fecha y hora en formatos compatibles con ISO 8601 (yyyy-mm-ddTHH:MM:ss+xx:yy, por ejemplo 2015-08-20T15:20:55+0200) o RFC 2822 (Thu, 20 Aug 2015 15:21:09 +0200). Con la función date(), podemos hacerlo con date(‘c’) y date(‘r’) respectivamente pero, a veces, no es posible (si la zona horaria no está bien definida, por ejemplo).

Lo que queremos es obtener ese desfase en horas y minutos, para ello, podemos utilizar esta función:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function getUTCOffset($timezone, $colon=true)
{
  $dtz = new DateTimeZone($timezone);
  $offset = $dtz->getOffset(new DateTime());
  $format=($colon)?'%+03d:%02u':'%+03d%02u';

  return sprintf($format, $offset / 3600, abs($offset) % 3600 / 60);
}

echo getUTCOffset('Europe/Madrid')."\n";
echo getUTCOffset('Europe/Madrid', false)."\n";

Con esta función consultamos el desfase a la hora actual entre la zona horaria especificada y UTC.
¿Quieres un listado de las zonas horarias soportadas?

1
2
<?php
print_r(DateTimeZone::listIdentifiers());

Ahora bien, si lo que quieres es conocer ese desfase sin tener en cuenta el horario de verano, algo así como la hora «normal», tendremos que hacer una consulta un poco más detallada:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function getUTCOffset2($timezone, $colon=true)
{
  $dtz = new DateTimeZone($timezone);
  $transitions = $dtz->getTransitions(time());
  $format=($colon)?'%+03d:%02u':'%+03d%02u';
  foreach ($transitions as $transition)
    {
      if (!$transition['isdst'])
    return sprintf($format, $transition['offset'] / 3600, abs($transition['offset']) % 3600 / 60);
    }
  return false;
}

foreach (DateTimeZone::listIdentifiers() as $tz)
  {
    echo $tz." - ".getUTCOffset2($tz)."\n";
  }

Con getTransitions() podemos obtener las fechas en las que se realizan los cambios de hora, junto con más información como si es horario de verano (isdst), por eso, recorremos los cambios de hora, y si es horario de verano, lo ignoramos. Para reducir el número de elementos del array devuelto por getTransitions() decimos que se genere a partir de la marca de tiempo actual (time()), podemos recortarlo con array_slice() si queremos para tener menos elementos, de todas formas, a la hora de recorrerlo no avanzaremos mucho, ya que el primer elemento que no tenga horario de verano, nos hará salir del bucle.

Por supuesto, podemos hacer una función que englobe las dos y diferenciar si hacemos caso del horario de verano o no por un argumento:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getUTCOffset($timezone, $colon=true, $ignoreDST=false)
{
  $dtz = new DateTimeZone($timezone);
  $format=($colon)?'%+03d:%02u':'%+03d%02u';

  if (!$ignoreDST)
    {
      $offset = $dtz->getOffset(new DateTime());
      return sprintf($format, $offset / 3600, abs($offset) % 3600 / 60);
    }
  else
    {
      $transitions = $dtz->getTransitions(time());
      foreach ($transitions as $transition)
    {
      if (!$transition['isdst'])
        return sprintf($format, $transition['offset'] / 3600, abs($transition['offset']) % 3600 / 60);
    }
      return false;
    }
}

Foto: leoplus (Flickr CC-by)

También podría interesarte....

There are 3 comments left Ir a comentario

  1. Pingback: Obtener diferencia entre una zona horaria y UTC en PHP | PlanetaLibre /

Leave a Reply