Obtener la hora local exacta con Xamarin y Google Maps Time Zone API
Trabajar con fechas y horas en una app móvil no es una tarea trivial: zonas horarias, horario de verano, configuración personalizada por parte del usuario… Si lo que deseamos es mostrarle al usuario la hora actual dependiendo su ubicación (o una coordenada geográfica específica, por ejemplo, queremos saber qué hora es en Italia) podemos hacerlo gracias al API de Time Zone que se encuentra dentro de los servicios de Google Maps. En esta entrada vamos a desarrollar una app móvil con Xamarin que utilice dicho servicio.
Google Maps API es una serie de servicios web que proporcionan información geográfica específica, por ejemplo indicaciones a seguir para desplazarte de un punto a otro, geocodificación (convertir una dirección en una coordenada geográfica), datos de un lugar, y la diferencia horaria para una ubicación específica, entre otros.
Precisamente el Time Zone API es este último, proporcionando la diferencia en tiempo para un punto en la Tierra dadas sus coordenadas geográficas (latitud y longitud) así como la fecha específica en que se desea conocer dicha diferencia. Y una ventaja de utilizar este API es que si tu app es gratuito, el acceso al API es totalmente gratuito para tí y tus usuarios. Si tus usuarios pagan por el acceso o una suscripción al servicio, entonces también deberás pagar por utilizar este API. Revisa el esquema de Tarifas y Planes para más información.
A continuación se muestra un ejemplo de llamada al API pasando las coordenadas de la Ciudad de México y la fecha del 16 de Abril a la 1:59:05 am (por supuesto, tú necesitas generar tu propia llave para que funcione):
https://maps.googleapis.com/maps/api/timezone/json?location=19.4334,-99.13323×tamp=1523869015&key=A...
Que devolvería lo siguiente:
{ "dstOffset" : 3600, "rawOffset" : -21600, "status" : "OK", "timeZoneId" : "America/Mexico_City", "timeZoneName" : "Central Daylight Time" }
- El parámetro timestamp se refiere al número de segundos que han transcurrido a partir de la medianoche del 1° de Enero de 1970. Es un valor convencional y estándar en muchas aplicaciones. Fechas anteriores a la estándar se expresan en valores negativos.
- El elemento dstOffset representa el número de segundos en relación al horario de verano. Por ejemplo, 0 indica que no hay horario de verano para la fecha solicitada, mientras que 3600 significa que se adelantó el reloj una hora.
- El elemento rawOffset indica la diferencia horaria en segundos con respecto a la zona UTC. En este caso, -21600/3600 = -6, lo cual se traduce a UTC-6 para la Ciudad de México.
Con esta información es fácil utilizar una expresión matemática para conocer la hora actual en dicha ubicación:
timestamp_actual = timestamp + dstOffset + rawOffset
Tal vez te preguntes. ¿No es darle mucha vuelta? ¿Por qué no simplemente obtener la hora del teléfono? La respuesta es simple: Los usuarios controlan y establecen la hora en su dispositivo, por lo que si colocamos un servicio disponible digamos hasta las 12 pm y el usuario modifica su reloj para que siempre sea inferior a la fecha límite, éste siempre tendrá acceso al recurso a pesar de que pusimos la limitante. Eso del lado del cliente. Del lado del servidor depende de nosotros. Si montamos un servicio en un proveedor de la nube (Azure, AWS) no sabemos en qué servidor (ubicación) se instalará nuestro servicio, por lo que podemos utilizar este API con coordenadas específicas y que “esa sea la hora del servidor” independientemente de dónde se aloje nuestra aplicación.
Por otro lado, el parámetro timestamp lo podemos asociar con la hora UTC. Con C# es posible obtener este dato sin importar en qué zona se encuentre el usuario:
var utc = DateTime.UtcNow;
Bueno, manos a la obra. En primer lugar accede a la página de Time Zone API y da clic en Obtén una Clave
Crea un nuevo proyecto, en este caso lo llamaré HoraLocal y da clic en Siguiente.
Espera a que se genere el proyecto.
Una vez concluido el aprovisionamiento de recursos, obtendrás tu API KEY, la cual puedes copiar en un bloc de notas. La utilizaremos en nuestra app móvil.
Ahora vamos a crear nuestro proyecto de Xamarin. Abre Visual Studio, crea una Cross-Platform App de Xamarin.Forms. El nombre de este proyecto es HoraLocal.
Selecciona las plataformas de destino que correspondan con tu entorno. La tecnología de interfaz de usuario es Xamarin.Forms, y la estrategia de código compartido es .NET Standard.
Una vez creado tu espacio de trabajo, vamos a agregar paquetes Nuget a nuestra solución.
Primero, Microsoft.Net.Http (versión más reciente) en todos los proyectos.
Seguido de Newtonsoft.Json.
Y finalmente el Geolocator Plugin de James Montemagno:
En tu proyecto HoraLocal agrega 4 carpetas:
En la carpeta Helpers crea la clase Constantes.cs, en la cual colocarás la ApiKey del Time Zone API que generamos hace unos instantes.
A continuación, crea la clase TimeZoneInfo dentro de la carpeta Modelos, con la siguiente estructura (es donde recibiremos la información contenida en el JSON que devuelve el API).
Como siguiente paso, en la carpeta Servicios crea la clase ServicioGelocalizacion, la cual tiene el método ObtenerUbicacionActual para devolver las coordenadas geográficas de la ubicación del dispositivo.
Ahora crea la clase ServicioTimeZone dentro de la misma carpeta, donde vamos a hacer la llamada al API para devolver la hora de un dispositivo en base a una ubicación geográfica con el método ObtenerHoraLocal.
El código anterior puede parecer un poco complejo, y tal vez diferente a lo que conocías (usando HttpClient con GetStringAsync únicamente). Es verdad que se puede hacer con solo 2 líneas de código pero mi recomendación es seguir las indicaciones de este post, donde Jhon Tiriet (Microsoft MVP) explica a detalle cómo hacer llamadas eficientes utilizando HttpClient y Json.Net.
Último paso. Agrega un ContentPage en la carpeta Paginas con el nombre PaginaHoraLocal
El código de la interfaz de usuario de esta página es el siguiente. Mostramos 2 botones (uno para obtener la ubicación actual y otro para la hora), cajas de texto para las coordenadas geográficas y labels para mostrar información y la fecha+hora actual.
Mientras que el código de C# correspondiente es mostrado a continuación. Básicamente, en los botones se hace la llamada a los servicios respectivos para obtener primero la ubicación y después la hora.
Ya solo nos falta modificar la clase App.xaml.cs para marcar la página que acabamos de crear como elemento de inicio en nuestra app:
Ahora vamos a compilar y probar la aplicación.
Esta es la pantalla inicial de nuestra app.
En este caso estoy utilizando Genymotion para hacer la prueba de la app. Para este emulador debo activar el GPS con el botón que aparece a la derecha. Después, puedo indicar cual es la ubicación actual del dispositivo.
A continuación podemos dar clic en el primer botón para obtener la ubicación.
Como podemos ver, se han obtenido tanto la latitud como la longitud. Ya podemos dar clic en el segundo botón.
Y obtener la fecha y hora de dichas coordenadas geográficas.
Por supuesto, si se introducen datos no válidos al momento de solicitar la hora, obtendremos el siguiente mensaje:
Y también está controlado si no podemos obtener la ubicación, ya sea por permisos o que el GPS no esté activado.
Como puedes ver, en términos generales es muy sencillo obtener el tiempo en una zona horaria específica de acuerdo a la ubicación del dispositivo gracias al API de Time Zone. El código de C# de la clase ServicioTimeZone puede ir dentro de la app móvil (código de Xamarin) pero también es posible incorporarlo en una Azure Function, por ejemplo:
Y por supuesto, al ser un API, lo puedes consumir con otras tecnologías (PHP, Java, NodeJS, etc).
¿Quieres extender este ejemplo y obtener la fecha de una ciudad específica, digamos para conocer la hora en Praga, Ciudad de México o Seattle por ejemplo? Eso se hace gracias a la geocodificación, el proceso que convierte una dirección en una coordenada geográfica (y Google tiene un API para eso, Geocoding). En realidad es muy sencillo de lograr, lo dejaremos para la próxima publicación (pero te recomiendo que intentes utilizar el API y me cuentes en los comentarios tus resultados =) )
Antes de que se me olvide, el código fuente lo puedes descargar desde mi repositorio de GitHub: https://github.com/icebeam7/HoraLocal