¿Un local C++ tiene una zona horaria asociada? Y si es así, ¿cómo se accede?
locale (3)
¿Un local C ++ tiene una zona horaria asociada?
Todos los aspectos de la zona horaria actual son la implementación definida.
La redacción exacta del especificador %Z
de C99 (C ++ delega la especificación de la función de la biblioteca C al estándar C) es:
se reemplaza por el nombre de la zona horaria o la abreviatura del entorno local, o por ningún carácter si no se puede determinar ninguna zona horaria.
Parece un poco ambiguo. Una interpretación es, de hecho, que la ubicación puede afectar a la zona horaria. Otro, que tampoco se ajusta a la redacción, sería que la configuración regional afecta al nombre o la abreviatura de la zona horaria. En cualquier caso, no parece haber ninguna garantía de que la zona horaria no se vea afectada por la ubicación, aunque no esperaría que lo fuera.
¿Como puedo acceder a esto?
Que yo sepa, no se pueden usar las utilidades estándar de la biblioteca. No directamente de todos modos, y no hay forma de modificarlo.
Una forma de imprimir la zona horaria actual es usar los especificadores de formato %z
o %Z
de strftime
/ put_time
/ time_put
como se muestra.
También hay una forma de obtener la diferencia de zona como un entero. std::mktime
descomprime una estructura std::tm
en una marca de tiempo de acuerdo con la configuración regional, mientras que std::gmtime
analiza una marca de tiempo en la estructura std::tm
acuerdo con la UTC, por lo que si comienza con la época y combina esos dos, Obtendrá la diferencia de la zona horaria actual de la localidad y el UTC en segundos.
std::time_t t = 0;
std::cout << -1 * std::mktime(std::gmtime(&t));
He investigado un poco sobre esto y tengo pruebas bastante convincentes de que la respuesta es SÍ y la respuesta NO. No estoy seguro de qué lado creer.
Primero, la documentación que he encontrado en cppreference.com, y http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf no dicen nada sobre esto. Tomo eso como evidencia de que los locales NO admiten una zona horaria.
Pero https://en.cppreference.com/w/cpp/locale/time_get/get y https://en.cppreference.com/w/cpp/locale/time_put/put dicen:
% z escribe el desplazamiento desde UTC en el formato ISO 8601 (por ejemplo, -0430), o no hay caracteres si la información de la zona horaria no está disponible% Z escribe el nombre o la abreviatura de la zona horaria, o ningún carácter si la información de la zona horaria no está disponible dependiente)
lo que parece sugerir que existe una zona horaria SOMETIMES asociado con un objeto locale ().
Ahora, si toma la configuración regional en_US.utf8 (uno de mis favoritos ;-)), realmente no hay una zona horaria sensible para asociar (los Estados Unidos contienen al menos 4 o más zonas horarias).
Así que es hora de ponerse empírico.
Corrí el código:
#include <iostream>
#include <cstdlib>
#include <locale>
#include <sstream>
using namespace std;
int main ()
{
// locale l;
locale l = locale::classic ();
tm when{};
const time_put<char>& tmput = use_facet<time_put<char>> (l);
ostringstream oss;
oss.imbue (l);
static const string kTZOffsetPattern_{"%z"};
tmput.put (oss, oss, '' '', &when, kTZOffsetPattern_.c_str (), kTZOffsetPattern_.c_str () + kTZOffsetPattern_.length ());
cout << oss.str ();
return 0;
}
En Linux (ubuntu), esto dio la respuesta que esperaba, +0000 (OK, tampoco me habría sorprendido con un error o una cadena vacía).
Pero en Windows (visual studio.net 2k17 - 15.8.7) - esto da: -0500
Sí, como habrás adivinado, estoy probando esto en la zona horaria del este. Pero aún habría esperado 0, o la cadena vacía (especialmente para el caso locale :: classic ()).
El pasaje relevante del estándar C (en el que se basa el estándar C ++) dice
% z se reemplaza por el desplazamiento de UTC en el formato ISO ''8601'' ''−0430'' ''(es decir, 4 horas 30 minutos detrás de UTC, al oeste de Greenwich), o por ningún carácter si no se puede determinar una zona horaria. [tm_isdst]
% Z se reemplaza por el nombre de la zona horaria o la abreviatura del entorno local, o por ningún carácter si no se puede determinar ninguna zona horaria. [tm_isdst]
Tenga en cuenta que se dice que el nombre de la zona horaria depende de la configuración regional, pero que el desplazamiento de la zona horaria no lo es.
Cppreference necesita arreglar su redacción descuidada.
Respuesta directa a tu pregunta.
¿Un local C ++ tiene una zona horaria asociada?
No.
Tampoco lo hará en el futuro. Como se señaló correctamente en la pregunta, para muchos entornos locales no tendría sentido ya que el área geográfica representada por el entorno puede tener más de una zona horaria.
El estándar C dice en la especificación para strftime
:
%Z
se reemplaza por el nombre de la zona horaria o la abreviatura del entorno local, o por ningún carácter si no se puede determinar ninguna zona horaria.[tm_isdst]
Pero la especificación de C para la struct lconv
no proporciona tal miembro para almacenar esa información. La especificación permite que las implementaciones agreguen dichos miembros, pero en la práctica, las implementaciones no almacenan esa información con la configuración regional C.
Las facetas de configuración regional C ++ time_put
y time_get
definen a sí mismas en términos de la especificación C para strftime
, la especificación POSIX para strptime
y algunas adiciones, que no incluyen un nombre de zona horaria o abreviatura.
La especificación POSIX para strftime
es mucho más detallada que la especificación C, y elimina la asociación con "locale":
Z
Reemplazado por el nombre de la zona horaria o la abreviatura, o por no bytes si no existe información de la zona horaria.[ tm_isdst]
La especificación POSIX para struct lconv
también es mucho más detallada que la especificación C, pero aún no proporciona almacenamiento para un nombre de zona horaria o abreviatura.
Pero el futuro trae la esperanza de tener acceso más fácil y eficazmente a la información sobre las zonas horarias, al menos en C ++.
Antes de C ++ 20, C ++ tiene conocimiento de:
Un estándar de tiempo único: UTC, que está estrechamente modelado por Unix Time .
Una sola zona horaria: la "zona horaria local" establecida por el usuario o administrador de la computadora. UTC también se puede utilizar como zona horaria local.
Como se detalla anteriormente, la zona horaria local no forma parte de los datos de configuración regional de C ++ (o C). Los datos de configuración regional incluyen algunos datos de calendario tales como:
- Nombres completos y abreviados entre semana.
- Nombres de mes completos y abreviados.
- Formatos convencionales locales para mostrar la fecha y la hora (por ejemplo, año, mes, orden del día).
El desplazamiento UTC ( %z
) y la abreviatura de la zona horaria ( %Z
) pueden estar disponibles, pero se almacenarán como parte de los datos de la zona horaria local, en lugar de con los datos locales actuales, en gran parte porque no hay una buena -un mapeo entre zonas horarias y locales.
Explicación de lo que sucedió con el código presentado en la pregunta del OP
En tu ejemplo: tm when{};
pone a cero todos los miembros de tm
, incluido tm_isdst
. Cuando tm_isdst
es cero, esto significa que se sabe que el horario de verano no está vigente, para esta tm
particular.
tm
también puede tener miembros no especificados por la norma. Una extensión popular es tener un miembro tm_gmtoff
que mantenga el desplazamiento UTC en segundos. Si su implementación de Linux tiene dicho miembro, tm when{};
Lo habría puesto a 0 segundos. Si su implementación de Windows no tiene dicho miembro, el desplazamiento UTC de la zona horaria local se almacenará en otro lugar. Esto explica las diferencias que está viendo y ambas implementaciones se están ajustando.
Información útil sobre cómo acceder a las zonas horarias ya que las configuraciones regionales de C ++ no proporcionan acceso
En el borrador de la especificación C ++ 20, existe un nuevo tipo llamado std::chrono::time_zone
. Una de las funciones miembro de time_zone
es:
template<class Duration> sys_info get_info(const sys_time<Duration>& st) const;
sys_time<Duration>
es solo un system_clock::time_point
, pero de cualquier precisión. Así que le time_point
time_zone
un time_point
y recuperas un sys_info
que contiene todo tipo de información útil sobre ese time_zone
en ese time_point
:
struct sys_info
{
sys_seconds begin;
sys_seconds end;
seconds offset;
minutes save;
string abbrev;
};
- El rango
[begin, end)
le indica las veces que esta información es válida (estos son puntos de tiempo UTC). -
offset
es el offset UTC actual de la zona horaria enseconds
. - Si
save != 0min
, latime_zone
se considera actualmente en horario de verano. - La abreviatura actual de
time_zone
se almacena enabbrev
.
Además, hay una función no miembro:
const time_zone* current_zone();
que devuelve un puntero a su zona horaria local actual. Juntando todo esto, aquí hay un programa C ++ 20 que imprime información interesante sobre su zona horaria local actual:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
std::cout << current_zone()->get_info(system_clock::now()) << ''/n'';
}
Esto acaba de salir para mí:
2018-03-11 07:00:00
2018-11-04 06:00:00
-04:00:00
01:00
EDT
Si lo desea, puede experimentar con esta parte de C ++ 20 utilizando C ++ 11, 14 o 17 utilizando la biblioteca de zona horaria de Howard Hinnant . Esta biblioteca pone todo en la date
espacio de nombres en lugar de std::chrono
.
También puede obtener información sobre cualquier zona horaria de la IANA , por ejemplo:
#include "date/tz.h"
#include <chrono>
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << locate_zone("Australia/Sydney")->get_info(system_clock::now()) << ''/n'';
}
que acaba de salir para mí:
2018-10-06 16:00:00
2019-04-06 16:00:00
11:00:00
01:00
AEDT
Tenga en cuenta que incluso en C ++ 20, las zonas horarias y las configuraciones regionales no están acopladas. Simplemente no tiene sentido hacerlo.