C++ declare zoned_time for 1 PM today east coast time - c++

I'm looking for how to declare a time for a configurable number of minutes before 1 pm east coast time on the day of running the program. I've found ways to get a fixed time of the timezone such as:
chrono::zoned_time onePmEastCoastTime ("New York/New York", chrono::sys_days{ 2022y / chrono::April/ 15d } + 13h );
How can I declare a configurable time that will match a specific timezone?

It sounds like you want to specify the time in terms of local time as opposed to UTC. The key to this is in the use of local_days vs sys_days.
sys_days is a count of days in UTC.
local_days is a count of days in some local time that can be subsequently paired with a timezone.
Maybe you are looking for:
chrono::minutes m = ...
chrono::zoned_time onePmEastCoastTime{"America/New_York",
chrono::local_days{ 2022y / chrono::April/ 15d } + 13h - m};

Related

std::localtime one second leads to 30 mins gap

#include <iostream>
int main(){
std::time_t t = 893665799;
std::tm * tm = std::localtime(&t);
printf("local time duration =>year:%d, month:%d, day:%d, hour:%d, min:%d, sec:%d\n",tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
t = 893665800;
tm = std::localtime(&t);
printf("local time duration =>year:%d, month:%d, day:%d, hour:%d, min:%d, sec:%d\n",tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
}
893665799 is a magic utc.
When u change ur timezone to Pitcairn Islands.
The log shows below
local time duration =>year:98, month:3, day:26, hour:23, min:59, sec:59
local time duration =>year:98, month:3, day:27, hour:0, min:30, sec:0
what's going on ? why 1 sec leads to 30 mins gap??
Given that the time_t given is exactly one second prior to rolling into a new hour, it's almost certainly to do with daylight savings time or some other adjustment.
And, with a bit of Google-fu, here it is:
27 Apr 1998 - Time Zone Change (PNT → PST)
When local standard time was about to reach Monday, 27 April 1998, 12:00:00, midnight clocks were turned forward 0:30 hours to Monday, 27 April 1998, 12:30:00 am local standard time instead.
This actually happens quite often, with various countries around the world moving their local times willy-nilly all over the place :-)
As mentioned by another answer, local times change around all the time, what with the daylight savings and whatnots.
To "portably" represent one instant in time became almost impossible and so UTC is invented, which is a magical timezone* that is the standard way to represent time, only taking into account leap seconds.
To correctly handle all these cases is incredibly tedious, but thankfully Howard Hinnant has this amazing library that does everything for you :)
* UTC is not really a timezone

What's the most efficient way to programmatically check if the year is changed

I am trying to capture packets from the NIC and save part of the packet payload as a string.
On part of packet that must be stored is its Log Time known as SysLog. Each packets has a SysLog with the following Format:
Nov 01 03 14:50:25 TCP...[other parts of packet Payload]
As it can be seen, the packet SysLog has no Year Number. My program must be running all over the year, so I need to add Year Number to the packet SysLog and convert SysLog to epoch time. The final string that I have to store is like this:
1478175389-TCP, ….
I use the following peace of code to convert Syslog to EpochTime.
tm* tm_date = new tm();
Std ::string time = Current_Year;
time += " ";
time += packet.substr(0,18);
strptime(time.c_str(), "%Y %b %d %T", tm_date);
EpochTime = timegm(tm_date);
The currentYear Method:
std::string currentYear() {
std::stringstream now;
auto tp = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
size_t modulo = ms.count() % 1000;
time_t seconds = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
#if HAS_STD_PUT_TIME
#else
char buffer[25]; // holds "2013-12-01 21:31:42"
if (strftime(buffer, 25, "%Y", localtime(&seconds))) {
now << buffer;
}
#endif // HAS_STD_PUT_TIME
return now.str();
}
The above operations are what i have to do for every packets. The packet rate is 100000-1000000 pps and the above peace of code is very time consuming specially on currentYear().
One possible optimization is to remove currentYear() Method and save the
Year number as a constant value. As said earlier my program must be run all over the year and as you know 2017 is comming. We can not change our binary at 31/12/2016 23:59:00 and also we don’t want to waste our time for calculating Year Number!!
I need a more efficient way to calculate the current year number without running it for each packet.
Is it possible? What is your suggestion for me?
Once you have obtained the current date and time, based on this it shouldn't be too difficult to calculate what the epoch time will be for midnight of next January 1st.
After calculating the expected epoch time for when the year rolls around, going forward all you have to do is compare it to the current time, when making a log entry. If it hasn't reached the precalculated Jan 1 midnight time, you know that the year hasn't rolled around yet.
So, you don't need to calculate the year for every packet at all. Just need to check the current time against the precalculated January 1st midnight time, which shouldn't change unless the politicians decide to change your timezone, while all of this is running...
The year is changed for log entries beginning with Jan, and only those log entries.
Log entries sometimes come out of order, or carry a timestamp saved during previous processing.
Attaching the year from the PC clock will give bad results, such as
2016 Dec 31 23:59:58 normal
2016 Jan 01 00:01:01 printing time placed in packet by remote device, remote clock is running a bit fast
2017 Dec 31 23:59:59 printing timestamp saved locally two seconds before logging occurred
2017 Jan 01 00:00:03 back to normal
You can't just concatenate the year of local clock with the month...second of the log message. You have to assign the year that avoids large clock jumps.
Since you're trying to produce Unix time (seconds since epoch) anyway, start by turning the log message time into Julian (seconds since start of year) and test whether the Julian is less than or greater than say 10 million (roughly 4 months).
You can "cache" the string you generate and only change it when the year changes. It may be though just a "little" improvement depending on what operations take the most time.
//somewhere
static int currentYear = 0;
static std::string yearStr = "";
//in your function
auto now = std::chrono::system_clock::now();
auto tnow = system_clock::to_time_t(now);
auto lt = localtime(&tnow); //or gmtime depends on your needs.
if(currentYear != lt.tm_year)
{
yearStr = std::to_string(lt.tm_year + 1900);
currentYear = t.tm_year;
}
return yearStr;
I am not sure if static has any negative/positive aspects on the performance of reading the string or a member variable may be better here due to cache locality. You have to test this.
If you use this in multiple threads you have to use a mutex here which probably will reduce performance though (again you have to measure this).
First, you might consider currentYear() returning an int (e.g. 2016), probably with time(2), localtime_r(3), the tm_year field.... You'll then avoid making C++ strings.
Then, you speak of high packet rate, so you probably have some event loop. You don't explain how it is done (hopefully you use some library à la libevent, or at least your own loop around poll(2)....), but you might compute the current year only once every tenth of second in that event loop. Or have some other thread computing the current year once in a while (you'll probably need a mutex, or use std::atomic<int> as the type of current year...)

Convert std::chrono::time_point to unix timestamp

How can I get an std::chrono::duration since a fixed date? I need this to convert a std::chrono::time_point to an unix timestamp.
Insert code into XXX
auto unix_epoch_start = XXX;
auto time = std::chrono::system_clock::now();
auto delta = time - unix_epoc_start;
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(delta).count();
I know time_point has a method time_since_epoch() but it's not guaranteed that this is the same as the unix epoch (00:00:00 UTC on 1 January 1970).
A unix time stamp is defined as the number of seconds since January 1, 1970 UTC, except not counting all the seconds. This is somewhat ridiculous and one has to wonder what the point of it is, so I agree that this is a silly question.
Anyway, lets look at some platform documentation for time_t and time().
Linux:
time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
POSIX.1 defines seconds since the Epoch using a formula that approximates the number of seconds between a specified time and the Epoch. This formula takes account of the facts that all years that are evenly divisible by 4 are leap years, but years that are evenly divisible by 100 are not leap years unless they are also evenly divisible by 400, in which case they are leap years. This value is not the same as the actual number of seconds between the time and the Epoch, because of leap seconds and because system clocks are not required to be synchronized to a standard reference. The intention is that the interpretation of seconds since the Epoch values be consistent; see POSIX.1-2008 Rationale A.4.15 for further rationale.
Windows:
The time function returns the number of seconds elapsed since midnight (00:00:00), January 1, 1970, Coordinated Universal Time (UTC), according to the system clock.
Mac OS X:
The functions ctime(), gmtime(), and localtime() all take as an argument
a time value representing the time in seconds since the Epoch (00:00:00
UTC, January 1, 1970;
The asctime(), ctime(), difftime(), gmtime(), localtime(), and mktime()
functions conform to ISO/IEC 9899:1990 (ISO C90''), and conform to
ISO/IEC 9945-1:1996 (POSIX.1'') provided the selected local timezone
does not contain a leap-second table (see zic(8)).
Similar documentation can be found for other systems, such as AIX, HP-UX, Solaris, etc.
So although not specified in C++ there is an easy and widely portable way to get a Unix timestamp:
auto unix_timestamp = std::chrono::seconds(std::time(NULL));
And if you want a number of milliseconds since 1 Jan 1970 UTC (similarly not counting all of them) then you can do this:
int unix_timestamp_x_1000 = std::chrono::milliseconds(unix_timestamp).count();
Just remember that these values aren't real times, so you can't in general use unix timestamps in arithmetic. For example subtracting unix timestamps does not give you an accurate count of seconds between the times. Or if you did something like:
std::chrono::steady_clock::now() - unix_timestamp;
you would not get a time point actually corresponding to 1970-01-01 00:00:00+0000.
As Andy Prowl suggests you could do something silly like:
// 1 Jan 1970 (no time zone)
std::tm c = { 0, 0, 0, 1, 0, 70, 0, 0, -1};
// treat it as 1 Jan 1970 (your system's time zone) and get the
// number of seconds since your system's epoch (leap seconds may
// or may not be included)
std::time_t l = std::mktime(&c);
// get a calender time for that time_point in UTC. When interpreted
// as UTC this represents the same calendar date and time as the
// original, but if we change the timezone to the system TZ then it
// represents a time offset from the original calendar time by as
// much as UTC differs from the local timezone.
std::tm m = *std::gmtime(&l);
// Treat the new calendar time as offset time in the local TZ. Get
// the number of seconds since the system epoch (again, leap seconds
// may or may not be counted).
std::time_t n = std::mktime(&m);
l -= (n-l); // subtract the difference
l should now represent the (wrong) number of seconds since 1 Jan 1970 UTC. As long as there are no leap seconds between the system epoch and 1 Jan 1970 (system time zone), or within an equal amount of time in the other direction from the system epoch, then any counted leap seconds should cancel out and l will be wrong in just the way that unix timestamps are wrong.
Another option is to use a decent date library such as Howard Hinnant's chrono::date. (Howard Hinnant was one of the guys that worked on the C++11 <chrono> library.)
auto now = system_clock::now();
sys_days today = time_point_cast<days>(now);
system_clock::time_point this_morning = today;
sys_days unix_epoch = day(1)/jan/1970;
days days_since_epoch = today - unix_epoch;
auto s = now - this_morning;
auto tz_offset = hours(0);
int unix_timestamp = (days_since_epoch + s + tz_offset) / seconds(1);
If you want to handle leap seconds Howard Hinnant also provides a library that includes facilities for handling them as well as for parsing time zone databases as the source for leap second data.
what about this C++11 implementation
auto microsecondsUTC = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
Long story short, here is the function I use to get Unix timestamp (seconds count since Jan 1, 1970 UTC):
static uint64_t getUnixTimeStamp(const std::time_t* t = nullptr)
{
//if specific time is not passed then get current time
std::time_t st = t == nullptr ? std::time(nullptr) : *t;
auto secs = static_cast<std::chrono::seconds>(st).count();
return static_cast<uint64_t>(secs);
}
The advantage of this function is that the default parameter value just gives you current time. However if you wanted to convert some specific time to unix timestamp than you can do so as well.
You could use mktime() to convert the desired date encoded in a tm structure into a local-time time_t value.
If you need a UTC time, then use gmttime() to convert that time_t value into a UTC tm structure, and figure out from the output you get which tm structure yields the desired UTC time_t when given in input to mktime().
A bit elaborate, but hopefully it will work or at least provide a hint.
I know time_point has a method time_since_epoch() but it's not
guaranteed that this is the same as the unix epoch (00:00:00 UTC on 1
January 1970).
As of C++20 and P0355R7, std::chrono::time_point::time_since_epoch can be guaranteed to be the UNIX epoch when the std::chrono::time_point is a std::chrono::system_clock.

Getting Windows Time Zone Information (C++/MFC)

I’m trying to:
Get a list of the time zone names as they appear in when the user selects a time zone from the Windows Control Panel (e.g., “(UTC-08:00) Pacific Time (US & Canada)”).
I’m doing that by reading “CurrentVersion\Time Zones” section of the registry which works well.
Get the currently selected time zone as a string.
I can get the current time zone using GetTimeZoneInformation(), but the names don’t match the list of time zones retrieved from “CurrentVersion\Time Zones” section of the registry. For example, Pacific time appears as “(UTC-08:00) Pacific Time (US & Canada)” in the list of time zones, but GetTimeZoneInformation() contains the strings “Pacific Standard Time” and “Pacific Daylight Time”. I need to find a name that corresponds to the “CurrentVersion\Time Zones” names so I can select it from a list.
Get the current offset (in minutes) between UTC and any other time zone based on the string name.
I can retrieve the offset from UTC in timeZoneInformation.Bias, but that’s only for the currently selected time zone. I need to know the “bias” from any zone as listed in the registry (e.g., “(UC-07:00) Mountain Time (US & Canada)” while adjusting for the daylight savings if in effect.
Microsoft chooses to put the string you want in a different area, some strings are non trivial to retrieve. But parts of your question can be answered with the information you have already.
In CurrentVersion\Time Zones, yes are the names of the zones.
Inside that key are the values for some of the parts you want.
re: “(UTC-08:00) Pacific Time (US & Canada)”
This is in the registry value "Display" under the zone name you have.
There are more strings in a resource DLL, specified by the values "MUI_Display", "MUI_Std" and "MUI_Dlt". It is usually something like "#tzres.dll,-220"
To get these strings you will need to load that resource DLL into memory and fetch those specific resources as UNICODE strings.
re: Offset in minutes of any timezone.
Also inside the registry key you have (the time zone name) is a value called "TZI".
This is a binary blob that (according to the MSDN) looks like this:
typedef struct _REG_TZI_FORMAT
{
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} REG_TZI_FORMAT;
Where the various BIAS are in minutes, using the equation UTC = local + theBias and theBias is either Bias + StandardBias or Bias + DaylightBias depending on the date in question. The two SYSTEMTIME values describe the transition dates.
TimeZoneInfo.Bias
TIME_ZONE_INFORMATION structure - http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
SetTimeZoneInformation function - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724944(v=vs.85).aspx
// Get the local system time.
SYSTEMTIME LocalTime = { 0 };
GetSystemTime( &LocalTime );
// Get the timezone info.
TIME_ZONE_INFORMATION TimeZoneInfo;
GetTimeZoneInformation( &TimeZoneInfo );
// Convert local time to UTC.
SYSTEMTIME GmtTime = { 0 };
TzSpecificLocalTimeToSystemTime( &TimeZoneInfo,
&LocalTime,
&GmtTime );
// GMT = LocalTime + TimeZoneInfo.Bias
// TimeZoneInfo.Bias is the difference between local time
// and GMT in minutes.
// Local time expressed in terms of GMT bias.
float TimeZoneDifference = -( float(TimeZoneInfo.Bias) / 60 );
CString csLocalTimeInGmt;
csLocalTimeInGmt.Format( _T("%ld:%ld:%ld + %2.1f Hrs"),
GmtTime.wHour,
GmtTime.wMinute,
GmtTime.wSecond,
TimeZoneDifference );
TimeZoneInfo Class - http://msdn.microsoft.com/en-us/library/bb396389
Determine Daylight Savings Time - http://www.metatrader4.com/forum/2155

Convert historical time to GMT

I need to convert some string times in the format "2011061411322100" into GMT - my first attempt is below. However, the problem is that the times are coming from another PC and is a historical time. So I am not getting the times in real time so I cannot simply get the GMT from the local time on the box that my code is running.
The problem is that if my code is running during a time change, the time change will have occurred on my box but not on the remote box where I am getting the times. I can however, query the box to get the current time at any time.
So, to give more detail:
I start the job on a remote box
The job completes
I get some times related to the job running
I convert the time to GMT
If a time change (daylight savings) occurs between 1. and 2. I am screwed. My GMT conversion will break. I guess after 2) I need to get the current Remote Box time and see if there is a difference of >58 mins and then apply that to the conversion. But I cannot figure out a reliable method of doing this.
string GMTConverter::strToGMT(const string& timeToConvert)
{
// Set time zone from TZ environment variable.
_tzset();
struct tm tmTime;
//2011 06 14 11 32 21 00
// (strToInt is just a wrapper for atoi)
int year = strToint(timeToConvert.substr(0, 4) );
int month = strToint(timeToConvert.substr(4, 2) );
int day = strToint(timeToConvert.substr(6, 2) );
int hour = strToint(timeToConvert.substr(8, 2) );
int min = strToint(timeToConvert.substr(10, 2) );
int sec = strToint(timeToConvert.substr(12, 2) );
cout<<"Time after parsing: "<<year<<"/"<<month<<"/"<<day<<" "<<hour<<":"<<min<<":"<<sec<<endl;
// add to tm struct and return
tmTime.tm_hour = hour;
tmTime.tm_min = min;
tmTime.tm_sec = sec;
tmTime.tm_mday = day;
tmTime.tm_mon = (month-1);
tmTime.tm_year = (year - 1900);
cout <<"Time in TM: "<<tmTime.tm_year<<"/"<<tmTime.tm_mon<<"/"<<tmTime.tm_mday<<" "<<tmTime.tm_hour<<":"<<tmTime.tm_min<<":"<<tmTime.tm_sec<<endl;
char currDateTime[64];
// For logging
strftime(currDateTime, 63, "%c", &tmTime);
cout <<"Actual time:"<<currDateTime<<endl;
time_t remotePCTime = mktime( &tmTime );
struct tm *gmt = gmtime( &remotePCTime );
cout << "gmt = " << asctime( gmt ) << endl;
char datebuf_2[12];
char timebuf_2[13];
strftime( datebuf_2, 13, "%Y-%m-%d\0", gmt );
strftime( timebuf_2, 13, "%H:%M:%S\0", gmt );
return string(datebuf_2) + "T" + string(timebuf_2) + "." + string("000");
}
The obvious reliable solution would be to use UTC (which has no daylight savings) for the time stamp you're sending over. Using any time system that has inherent ambiguity (there is one hour of overlap each year where you can get the same time stamps on a different time) will make it impossible to have a fool-proof method, since information is lost.
If you have no control over the time format that the remote machine is sending, you can only try to extrapolate from the information that you do have, for instance, if the end time is lower than the start time, add one hour. This again introduces ambiguity if the job took longer than one hour, but at least time won't move backwards.
Time change appears twice a year - why should you bother? Anyway, can't you change the time format to include the time change event? Or check if job is done during time change by comparing to some fixed time and date at which time change appears?
Get the local time in UTC at the start and end of the remote job. Get the remote job time and covert to UTC at the start and end of the job. Convert the collection "historic" times to GMT/UTC as stated in your original post. Keep this data together in a struct or class and give additional start end times a clear name like LocalDLSValidation etc
We must now checkfor following scenarios:
1. Start and end time delta between Local and Remote is within allowed threshold(50mins?)
This the gold case. No modification is required to our collection of historical times
2. Local start/end time and remote time delta is outside threshold.
This is the second simplest case. It means that we can + or - hour to our entire collection of times.
3.Local start time and remote time delta is within threshold. But end is outside.
This is our worst case scenario as it means change has occurred in the middle of our job. If the job lasts less than hour then it will be easy to see which times in our collection need to be + or - one hour.
If it is greater than 1 hour....mmmmm. This is where we run into problems.
4. Start time between local and remote is different but end time is different
According to use case in OP this should not occur.