Have a Unix timestamp and I need equivalents of localtime_s, but where I can pass in the timezone. Alternately I'm looking for a way to change the timezone programatically for the app (not the whole system), so that localtime_s returns the correct values.
Strangely enough, if I set TZ environment variable in a shell to GMT, and then launch my application, localtime_s returns values in GMT.
I've tried:
::SetEnvironmentVariable("TZ", "GMT");
tzset();
But it does not change the results of later calls to localtime_s
Note that I'm running the application on Windows 7, using VS 2013.
If you are willing to use a free, open-source 3rd party library, this works on VS-2013. There are many examples on how to use it here:
https://github.com/HowardHinnant/date/wiki/Examples-and-Recipes
For example here is how to output the current time in New York and Chicago:
#include "tz.h"
#include <iostream>
int
main()
{
auto now = std::chrono::system_clock::now();
std::cout << "Current time in New York: "
<< date::make_zoned("America/New_York", now) << '\n';
std::cout << "Current time in Chicago : "
<< date::make_zoned("America/Chicago", now) << '\n';
}
This just output for me:
Current time in New York: 2016-08-12 10:36:24.670966 EDT
Current time in Chicago : 2016-08-12 09:36:24.670966 CDT
Related
I have been wracking my head crazy trying to figure this out with this API
My original implementation was something like:
// TimezonePtr is just a share_ptr to the timezone
std::tm getGMT(const std::tm& rawtime, TimezonePtr tz)
{
std::tm result = rawtime;
const auto loct = mktime_z(tz.get(), &result);
gmtime_r(&loct, &result);
return result;
}
However, this does not take into account DST. For example, if I feed it a date of Sep 28 2012 15:54:24 I get back Sep 28 2012 20:54:24, which is incorrect. It looks like I want to use localtime_rz, except that takes an epoch, which is driving me nuts because if I could get the epoch then I'd already have my answer. :(
How can I accomplish this?
mktime_z takes a struct tm as one of its arguments. If you don't know whether DST is in effect for the input date, you want to set the tm_isdst member of that tm to -1 to signify that the system should figure out whether DST is in effect for that date/time/timezone when you call mktime.
At least for me, this seems to work correctly (i.e., it correctly concludes that at least in my time zone, DST was in effect in September of 2012).
In addition to Jerry Coffin's correct (and up-voted) answer, I wanted to show how this computation could be done with a modern C++11/14 library (free and open source).
I've kept the API the same in the interest of making the code easy to compare:
template <class Duration>
auto
getGMT(date::local_time<Duration> rawtime, const date::time_zone* tz)
{
return tz->to_sys(rawtime);
}
This returns a std::chrono::time_point<system_clock, Duration> where Duration is the finer of the input Duration and seconds. If the ragtime doesn't have a unique mapping to UTC according to the indicated time zone, an exception will be thrown. Such an event can occur (for example) if rawtime is during a daylight saving transition and occurs twice, or not at all. If desired, there exists API for avoiding the exception if you want to "pre-decide" how you would like to map ambiguous and non-existent local times into UTC.
This function can be exercised like this:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
std::cout << getGMT(local_days{sep/28/2012} + 15h + 54min + 24s,
current_zone()) << " UTC\n";
std::cout << getGMT(local_days{sep/28/2012} + 15h + 54min + 24s,
locate_zone("America/New_York")) << " UTC\n";
}
This exercises the code twice:
With whatever the current time zone is for this computer.
With the time zone "America/New_York"
For me these are both the same time zone, resulting in the following output:
2012-09-28 19:54:24 UTC
2012-09-28 19:54:24 UTC
Not only is it simpler to use this library than the BSD ctime API,
but this API has type safety. For example the UTC time point and local time point are represented by different types, allowing the compiler to tell you if you accidentally use the wrong one. In contrast the BSD ctime API uses the same type (tm) for both local time and UTC.
I want to get the transition time for DST
Under Linux with giving time zone or TZ env.
My way is stupid, giving the start of the year and try every hour then check tm_isdst value of local time to get the transition time.
Is there some simple way to do this?
There is the source code in glibc, which you can browse here:
http://sourceware.org/git/?p=glibc.git;a=tree;f=timezone
Or you can use the timezone database here:
ftp://ftp.iana.org/tz/releases/tzdata2012c.tar.gz
Since you haven't given a particular timezone/location, I can't look up and give you the exact information for you.
You can also use boost_datetime.
#include <iostream>
#include <boost/date_time/local_time/local_time.hpp>
using namespace boost::local_time;
using namespace boost::posix_time;
int main()
{
tz_database tz_db;
tz_db.load_from_file("/path_to_boost/boost/libs/date_time/data/date_time_zonespec.csv");
time_zone_ptr zone = tz_db.time_zone_from_region("America/New_York");
ptime t1 = zone->dst_local_start_time(2013);
ptime t2 = zone->dst_local_end_time(2013);
std::cout << t1 << std::endl;
std::cout << t2 << std::endl;
}
Some related SO links:
c++ How to find the time in foreign country taking into account daylight saving?
How do you get the timezone (offset) for a location on a particular date?
But as RedX said earlier, politics may change time zones. So actually your original solution has the advantage of being automatically updated with the underlying OS. Also, you can improve your existing solution by using binary search.
I have a setup working using localtime() to get a tm with the local times represented in it. And that is all good.
However, if I change timezone while the application is running, it does not notice that I have changed timezones.
Is there some way to tell it to 'go look again' to refresh to the system timezone?
I know this is probably not a common case, but it is what test are doing to test this feature, so they want it supported!
Take a look at tzset (this is posix only). This might give you what you need. If your TZ environment variable is unset, it should reinitialize from the OS.
From the man page:
DESCRIPTION
The tzset() function initializes the tzname variable from the TZ environment variable. This function is automatically called by the
other time conversion functions that depend on the time zone. In a
SysV-like environment it will also set the variables timezone
(seconds West of GMT) and daylight (0 if this time zone does not
have any daylight savings time rules, non-zero if there is a time
during the year when daylight savings time applies).
If the TZ variable does not appear in the environment, the tzname variable is initialized with the best approximation of local
wall clock time, as specified by the tzfile(5)-format file localtime
found in the system timezone directory (see below). (One also often
sees /etc/localtime used here, a symlink to the right file in the
system timezone directory.)
A simple test:
#include <iostream>
#include <time.h>
#include <stdlib.h>
int main()
{
tzset();
time_t t;
time(&t);
std::cout << "tz: " << tzname[0] << " - " << tzname[1] << " " << ctime(&t) << std::endl;
setenv("TZ", "EST5EDT", 1);
tzset();
std::cout << "tz: " << tzname[0] << " - " << tzname[1] << " " << ctime(&t) << std::endl;
return 0;
}
Gives me output:
tz: CST - CDT Wed Jan 11 12:35:02 2012
tz: EST - EDT Wed Jan 11 13:35:02 2012
There's nothing in the standard library to do this. Unless your platform offers some extension to the library for updating the time zone, your program's calls to localtime() will probably always use the time zone that was active at program start up.
You could probably work around that by putting the localtime stuff in a separate process that your main program can startup and shutdown at will, thus re-initializing that process's time zone.
Or instead your platform may offer some other API for getting the local time that will reflect changes in the system time zone.
I am writing a Linux (Ubuntu and Debian Lenny) application in C++.
Now I need to know the distance/offset between UTC and the currently set system time at a given day in the past. Since I need to convert recorded data, I the distance need to be calculated with respect to a date in the past (which may have a different DST setting than the current day).
Anyone know how to do this?
Edit: Reading the first answer I think I was misunderstood: I do not want to compare dates/times. I have date/time values which I want to convert from UTC to local time.
Prepare the tm structure with date:
struct tm date;
memset(&date,0,sizeof(date));
date.tm_year = year - 1900;
date.tm_mon = month - 1;
date.tm_mday = day;
date.tm_hour = hour;
date.tm_min = minute;
date.tm_sec = second;
date.tm_isdst = -1; // VERY IMPORTANT
mktime(&date); /// fill rest of fields
And then take a look on tm_gmtoff
printf("%d\n",date.tm_gmtoff);
This is distance from UTC.
Now this is Linux and BSD specific, it would not work on other stystems, and this works
with respect to DST.
Read man mktime for more information. And filling struct tm with correct values
P.S.: Converting from UTC to Local and back?
time_t posix_time = timegm(&UTC_Filled_struct_tm); // Linux specific line
localtime_r(&posix_time,&local_Filled_struct_tm);
Local to UTC
time_t posix_time = mktime(&local_Filled_struct_tm);
gmtime_r(&posix_time,&UTC_Filled_struct_tm);
I think you may benefit from using Boost.DateTime or ICU.
As for Boost.DateTime tt might be like this:
1) You prepare a database with timezone information Boost.Datetime and create a timezone. Timezones are important since they hold information about DST
tz_database tz_db;
tz_db.load_from_file("./date_time_zonespec.csv");
time_zone_ptr nyc = tz_db.time_zone_from_region("America/New_York"); // or other timezone
Or just create a timezone like this.
std::string kaliningrad_string = "EET+02:00:00EEST+01:00:00,M3.5.0/02:00:00,M10.5.0/03:00:00";
boost::local_time::time_zone_ptr kaliningrad_tzone_posix(new boost::local_time::posix_time_zone(kaliningrad_string));
std::string vladivostok_string = "VLAT+10:00:00VLAST+01:00:00,M3.5.0/02:00:00,M10.5.0/03:00:00";
boost::local_time::time_zone_ptr vladivostok_tzone_posix(new boost::local_time::posix_time_zone(vladivostok_string));
Creating timezones using a string specification of timezones looks more difficult but you can make use of it if you can't find a particular timezone in date_time_zonespec.csv.
For example Samara used to be in UTC+4 before March 2010 and now it is in UTC+3. date_time_zonespec.csv doesn't have history of changes, so in this situation it is necessary to create a timezone out of the string specification. However I recall that ICU seems to have timezones with this sort of history ICU TimeZone Classes:
Time zone data changes often in
response to governments around the
world changing their local rules and
the areas where they apply. The ICU
time zone data is updated for each
release, and the easiest way to stay
up to date may be to upgrade to the
latest ICU release, which also
provides bug fixes, code improvements
and additional features.
3) Make a localtime you need, for example
local_date_time tmp(boost::gregorian::date(2010, 3, 28), boost::posix_time::time_duration(1,59,0),nyc, boost::local_time::local_date_time::EXCEPTION_ON_ERROR);
4) And then calculate difference using functions utc_time and local_time
There is an example on that page:
ptime pt(date(2004,Nov,5),
hours(10));
time_zone_ptr zone(new posix_time_zone("MST-07"));
local_date_time az(pt, zone);
az.utc_time(); // 10am 2004-Nov-5
az.local_time(); // 3am 2004-Nov-5
5) Another example. The local time is the same but UTC is different
local_date_time tmp(boost::gregorian::date(2010, 3, 28), boost::posix_time::time_duration(1,59,0),kaliningrad_tzone_posix, boost::local_time::local_date_time::EXCEPTION_ON_ERROR);
std::cout << "As is: " << tmp << ", UTC: " << tmp.utc_time() << std::endl;
local_date_time tmp(boost::gregorian::date(2010, 3, 28), boost::posix_time::time_duration(1,59,0),vladivostok_tzone_posix, boost::local_time::local_date_time::EXCEPTION_ON_ERROR);
std::cout << "As is: " << tmp << ", UTC: " << tmp.utc_time() << std::endl;
Make sure all the times are converted to UTC (see mktime() for example). You then can use either difftime() or the timeval_subtract example function from the same link, depending on what structure the times are stored in.
i m new to c.I want to FTP to a system and get the timezone of that system.in c++
There is no way in the FTP standard to do this. You could try uploading a new (small) file and check its date-time as reported by FTP, compared to current local time.
New answer for old question.
Rationale for revisiting this: We have better tools now.
This free, open-source, portable library will get the current time zone setting in terms of the IANA time zone database. On Windows it will convert the Windows timezone into an IANA time zone. Fully documented.
Here's how you print out the name of the current time zone:
#include "tz.h"
#include <iostream>
int
main()
{
std::cout << date::current_zone()->name() << '\n';
}
For me this currently outputs:
America/New_York
You can convert a std::chrono::system_clock to a local time and print it out:
std::cout << date::make_zoned(date::current_zone(),
std::chrono::system_clock::now()) << '\n';
which just output for me:
2016-07-16 19:24:00.447015 EDT
See the github wiki for many more examples.