📜 ⬆️ ⬇️

Practice of working with time in different time zones in unix-like systems

If your application depends not only on local time, but also on its representation in other time zones, you must have experienced the difficulty of representing time in different time zones. Did not come across? So you have not ported your application to the world of Unix.

Indeed, in Windows for working with time zones, the programmer is provided with a convenient set of specialized WinAPI functions. An example is the TIME_ZONE_INFORMATION structure and the GetTimeZoneInformation function to it in addition.

But what to do if you need to know the offset relative to UTC + 0, the rules for the transition to "summer time", while taking into account leap years with leap seconds and other specific information for any region, but all this in unix-like operating systems? The article is devoted to the practice of working with all this junk in C / C ++.

This topic was repeatedly covered in many articles from different points of view, but rarely from practical with examples of specific languages, systems and technologies. Examples can be found on the Stack Overflow (of which there are a lot of questions), and on Habré this topic was touched on quite deeply, but from a theoretical point of view. In addition, there is even a mini-study on the topic of local time, from which we can learn that the problem of computer time is not at all trivial, which seems at first glance. Having conducted my own investigation, I would like to briefly and intelligibly share a considerable amount of information I received, filling it in the form of ways to convert time in different time zones.
')
Classic: C Standard Library

Yes, with the help of that function maser defined in the header time.h , you can also perform time conversions. With certain restrictions.

The fact is that all locale-dependent functions (such as gmtime() or localtime() ) use the TZ environment variable to determine the locale parameters. This means that in order to convert the time to the desired time zone, you must first set this variable (with the name of the required zone), call the conversion function, and then remove the TZ from the environment again. All anything, if not multitasking and multithreading. Naturally, this method can cause conflicts and lead to difficult predictable errors.

Sample code using this approach:

 putenv("TZ=Asia/Calcutta"); ///      tzset(); ///    time_t timeToConvert = time(0); ///     struct tm *pCalcuttasTime; ///      pCalcuttasTime = localtime(&timeToConvert); ///  putenv("TZ="); ///   TZ /// ...     ... 


tz database , or juggling bytes

Using the Olson database for your own purposes is the preferred option. The advantages are obvious: the base most fully reflects all conceivable transition rules for any corner of the Earth (given the changes in these rules since the beginning of the last century), is distributed with many systems (see /usr/share/zoneinfo ) and has a unified format, while the base updated with the system. However, having tried to work with her, I decided to refuse from this option.

The database is distributed in binary format (for this, the zic compiler is used). The format description can be found in the tzfile.h header file (use the official FTP database to find it). I did not find any tools for working with the database (perhaps I looked badly?). But having tried to read the file of the necessary time zone, I was faced with the problem of data interpretation - in all these subtleties and terminology you can turn your head, forgetting about the purpose of all this digging. And, in order to abstract from such trifles, it was decided to use the most adequate and convenient tool.

Boost.Date_Time

As it often happens in such situations, the boost comes to the rescue. On the wide possibilities of the Date_Time library set, there has already been an article containing a brief translation of the official documentation. By the way, the good news is for those who do not want to introduce unnecessary dependencies into their project - the library is header-only (with the exception of a couple of specific places, like creating a timestamp object from a string of a specific format).

To resolve the issue, there are two ways: write the rules for the desired time zone with a hardcode in the program (and then hate yourself for it), or store all the rules in a special CSV-format file. Such a file can be subsequently updated automatically (and keep the transition rules up to date, which is extremely important). The file is distributed with the boost distribution (called date_time_zonespec.csv ), but can be taken from other places . Plus the use of the file, among other things - it stores the rules for all regions.

No cons, too, will not do. What if you need to convert that timestamp somewhere near the beginning of the twentieth century, when the transition rules were different? Such cases also have to be taken into account, and, unfortunately, the boost here can be of little help.

For example, here’s the code that uses the Date_Time library set feature to convert time.

 #include "boost/date_time/local_time/local_time.hpp" #include "boost/date_time/posix_time/posix_time.hpp" using namespace boost::local_time; using namespace boost::posix_time; ptime convertUTC0toCustomTimeZone(const ptime &utcTime, const std::string &tzName) { ///     tz_database tzDB; tzDB.load_from_file("./date_time_zonespec.csv"); ///    time_zone_ptr timeZone = tzDB.time_zone_from_region(tzName); /// tzName   "Asia/Calcutta" ///     local_date_time localTime(utcTime, timeZone); /// ...    return localTime.local_time(); } 


Results

I preferred to use the boost option. Of course, using the tz database will relieve you of the headwash with keeping information about time zones up to date or when converting a timestamp, the transition rules of which have been changed. But in most applications such frills come to nothing, and the boost allows you to work with dates extremely conveniently.

Source: https://habr.com/ru/post/143499/


All Articles