Published on

June 2, 2025

Comprendiendo las conversiones de zona horaria en SQL Server

Las conversiones de zona horaria pueden ser una tarea compleja cuando se trabaja con valores de fecha y hora en SQL Server. En este artículo, exploraremos una solución que permite realizar conversiones precisas y flexibles de zona horaria utilizando una combinación de tablas y funciones.

El problema

Cuando se trabaja con valores de fecha y hora en SQL Server, a menudo es necesario convertirlos entre diferentes zonas horarias. Esto se vuelve particularmente desafiante al considerar factores como los ajustes por el horario de verano. El objetivo es tener una solución que pueda manejar conversiones entre cualquier par de zonas horarias, teniendo en cuenta las reglas y desplazamientos específicos de cada zona.

La solución

Para resolver este problema, podemos crear dos tablas: una tabla de zonas horarias y una tabla de calendario. La tabla de zonas horarias contendrá metadatos sobre cada zona horaria, incluyendo las descripciones estándar y de horario de verano, así como los minutos de desplazamiento estándar y de horario de verano. La tabla de calendario tendrá una fila para cada año en cada zona horaria compatible, especificando las fechas de inicio y fin para los ajustes por el horario de verano.

Al poblar estas tablas con los datos necesarios, podemos crear una función que reciba un valor de fecha y hora, la zona horaria de origen y la zona horaria de destino. La función convertirá la hora de origen a UTC y luego la convertirá a la zona horaria de destino, teniendo en cuenta los ajustes por el horario de verano.

Ejemplo de código

Aquí tienes un ejemplo de cómo crear las tablas y la función necesarias:

CREATE TABLE dbo.ZonasHorarias
(
  IDZonaHoraria TINYINT NOT NULL PRIMARY KEY,
  DescripcionEstandar VARCHAR(64) NOT NULL UNIQUE, 
  DescripcionHorarioVerano VARCHAR(64) NOT NULL UNIQUE,
  MinutosDesplazamientoEstandar SMALLINT NOT NULL,
  MinutosDesplazamientoHorarioVerano SMALLINT NOT NULL
);

CREATE TABLE dbo.CalendarioZonasHorarias
(
  [Año]           DATE NOT NULL,
  IDZonaHoraria       TINYINT NOT NULL FOREIGN KEY 
                   REFERENCES dbo.ZonasHorarias(IDZonaHoraria),
  UTC_InicioHorarioVerano    SMALLDATETIME NOT NULL,
  UTC_FinHorarioVerano      SMALLDATETIME NOT NULL,
  Local_InicioHorarioVerano  SMALLDATETIME NOT NULL,
  Local_FinHorarioVerano    SMALLDATETIME NOT NULL,
  PRIMARY KEY ([Año], IDZonaHoraria)
);

CREATE FUNCTION dbo.ConvertirEntreZonasHorarias
(
  @Origen   DATETIME,
  @ZonaHorariaOrigen TINYINT,
  @ZonaHorariaDestino TINYINT
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN 
(
  SELECT 
     Origen = @Origen, 
     src.ZonaHorariaOrigen,
     [Destino] = DATEADD(MINUTE, CASE 
       WHEN src.UTC >= trg.UTC_InicioHorarioVerano 
        AND src.UTC < trg.UTC_FinHorarioVerano THEN tz.MinutosDesplazamientoHorarioVerano 
       ELSE tz.MinutosDesplazamientoEstandar END, src.UTC),
     ZonaHorariaDestino = CASE 
       WHEN src.UTC >= trg.UTC_InicioHorarioVerano 
        AND src.UTC < trg.UTC_FinHorarioVerano THEN tz.DescripcionHorarioVerano
       ELSE tz.DescripcionEstandar END
  FROM 
  (
    SELECT 
      src.[Año],
      UTC = DATEADD(MINUTE, -CASE 
        WHEN @Origen >= src.Local_InicioHorarioVerano 
         AND @Origen < src.Local_FinHorarioVerano THEN t.MinutosDesplazamientoHorarioVerano 
        WHEN @Origen >= DATEADD(HOUR,-1,src.Local_InicioHorarioVerano) 
         AND @Origen < src.Local_InicioHorarioVerano THEN NULL
        ELSE t.MinutosDesplazamientoEstandar END, @Origen),
      ZonaHorariaOrigen = CASE 
        WHEN @Origen >= src.Local_InicioHorarioVerano 
         AND @Origen < src.Local_FinHorarioVerano THEN t.DescripcionHorarioVerano
        ELSE t.DescripcionEstandar END 
    FROM dbo.CalendarioZonasHorarias AS src
    INNER JOIN dbo.ZonasHorarias AS t 
    ON src.IDZonaHoraria = t.IDZonaHoraria
    WHERE src.IDZonaHoraria = @ZonaHorariaOrigen 
      AND t.IDZonaHoraria = @ZonaHorariaOrigen
      AND CONVERT(DATE,DATEADD(MINUTE,t.MinutosDesplazamientoEstandar,@Origen)) 
          >= src.[Año]
      AND CONVERT(DATE,DATEADD(MINUTE,t.MinutosDesplazamientoEstandar,@Origen)) 
          < DATEADD(YEAR, 1, src.[Año])
  ) AS src
  INNER JOIN dbo.CalendarioZonasHorarias AS trg
     ON CONVERT(DATE,src.UTC) >= trg.[Año] 
    AND CONVERT(DATE,src.UTC) < DATEADD(YEAR, 1, trg.[Año])
  INNER JOIN dbo.ZonasHorarias AS tz
  ON trg.IDZonaHoraria = tz.IDZonaHoraria
  WHERE trg.IDZonaHoraria = @ZonaHorariaDestino
  AND tz.IDZonaHoraria = @ZonaHorariaDestino
);

Una vez que se crean las tablas y la función, puedes usar la función para convertir valores de fecha y hora entre diferentes zonas horarias. Aquí tienes un ejemplo:

SELECT d.Origen, f.ZonaHorariaOrigen, f.[Destino], f.ZonaHorariaDestino
FROM @d AS d 
OUTER APPLY dbo.ConvertirEntreZonasHorarias
(d.Origen, d.ZonaHorariaOrigen, d.ZonaHorariaDestino) AS f;

Conclusión

Utilizando una combinación de tablas y funciones, podemos convertir con precisión y flexibilidad valores de fecha y hora entre diferentes zonas horarias en SQL Server. Esta solución tiene en cuenta los ajustes por el horario de verano y puede manejar conversiones entre cualquier par de zonas horarias. Con el ejemplo de código proporcionado, puedes implementar fácilmente esta solución en tu propio entorno de SQL Server.

Click to rate this post!
[Total: 0 Average: 0]

Let's work together

Send us a message or book free introductory meeting with us using button below.