Getting numeric timezone in specified format - c++

I would like to print the current time as 2011-08-18 10:11:12 -07:00. I developed a code snippet as below,
#include <iostream>
using namespace std;
void time_to_string(time_t clock,const char *fmtstr )
{
char buf[256];
if (strftime(buf, 256, fmtstr, localtime(&clock)) == 0)
buf[0] = 0;
cout << buf << endl;
}
int main()
{
time_to_string(time(NULL), "%Y-%m-%d %H%M%S %z");
}
I am able to display the time as 2011-08-18 10:11:12 -0700 but not as 2011-08-18 10:11:12 -07:00. Using "%Y-%m-%d %H%M%S %:z" produces 2011-08-18 10:11:12 %:z.
How can i accomplish the above task in C/C++.

You would have to manually split the string which is formated by %z as +hhmm or -hhmm. %z has a fixed format. Look at the description of strftime.
Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no characters if no timezone is determinable.
Build one string with date and time. Build a second string with the offset from UTC with %z, insert the : in the second string. Concatenate first and second string.

It tries to interpret %: and it doesn't match a format specifier, so it prints it out as is. But you probably knew that already =)
In your time_to_string function, I would manually insert the ':' into the buffer before displaying it.

The syntax you tried don't exist.
What I would do is calling the function twice : once with "%Y-%m-%d %H%M%S ", and once with "%z", manually add the : in the second string, and then concatenate the two.
To insert the :, you could do an ugly buffer manipulation :
buf2[5]=buf2[4];
buf2[4]=buf2[3];
buf2[3]=':';
strcat(buf,buf2);
Note that the layout isn't likely to change for this specific data, so it's not so ugly.
0r if you really like overkill, a regexp. But you'll need an external library.

You can manually add the ':' at the end, modifying the result string. e.g.,
buf[26]='\0';
buf[25]=buf[24];
buf[24]=buf[23];
buf[23]=':';
I may be overlooking a better solution.

Related

Using C++'s date library to read times

I am attempting to use Howard Hinnant's date library (https://github.com/HowardHinnant/date) to read user input into a datetime object. I would like to use this library since it's modern, and is to be included in C++20.
The program should be able to accept datetimes in ISO 8601 format (YYYY-MM-DDTHH:MM:SS+HHMM etc, eg 2020-07-07T18:30+0100) as well as simple datetimes in the form DD-MM HH:MM or HH:MM. In the second case, it should assume that the missing information is to be filled in with the current date/year (the timezone is to be dealt with later).
Here is my attempt at doing this.
using namespace date;
int main(int argc, char ** argv)
{
std::istringstream ss { argv[1] };
std::chrono::system_clock::time_point dt
{ std::chrono::system_clock::now() };
if ( date::from_stream(ss, "%F T %T %z", dt) ) {
std::cout << "Cond 1 entered\n";
}
else if ( ss.clear(), ss.seekg(0); date::from_stream(ss, "%d-%m %R", dt)) {
std::cout << "Cond 2 entered\n";
}
std::cout << dt << "\n";
}
For the first format, this works as expected:
./a.out 2020-06-07T18:30:00+0200
Cond 1 entered
2020-06-07 16:30:00.000000000
However, the second method returns something strange, depending on the compiler used. When compiled with GCC and -std=c++17/-std=c++2a:
./a.out "07-08 15:00"
Cond 2 entered
1754-04-06 13:43:41.128654848
EDIT 2: When compiled with LLVM and -std=c++2a:
./a.out "07-08 15:00"
Cond 2 entered
0000-08-07 15:00:00.000000
which is a little closer to what I was expecting.
I'd rather not have the behaviour dependent on the compiler used though!
I'm really stumped as to what's going on here, and I can't seem to make head or tail of the documentation.
How can I get date::from_stream to simply overwrite the time and date and leave everything else?
EDIT 1:
For clarity, I was (incorrectly) expecting that when the second condition is entered the current year is preserved, since the time_point object was initialised with the current year. E.g I hoped the second call to from_stream would leave the time_point object as 2020-08-07 15:00:33.803726000 in my second example.
See comments for further info.
EDIT 2:
Added results of trying with different compilers.
Good question!!!
You're not doing it quite right, and you found a bug in date.h! :-)
First, I've fixed the bug you hit here. The problem was that I had the wrong value for not_a_year in from_stream, and that bug has been hiding in there for years! Thanks much for helping me find it! To update just pull the tip of the master branch.
When your program is run with the fixed date.h with the argument "07-08 15:00", it enters neither condition and prints out the current time.
Explanation:
The semantics of from_stream(stream, fmt, x) is that if stream doesn't contain enough information to fully specify x using fmt, then stream.failbit gets set and x is not modified. And "07-08 15:00" doesn't fully specify a system_clock::time_point.
The bug in date.h was that date.h was failing to recognize that there wasn't enough information to fully specify the system_clock::time_point, and was writing deterministic garbage to it. And that garbage happened to produce two different values on LLVM/libc++ and gcc because of the different precisions of system_clock::time_point (microseconds vs nanoseconds).
With the bug fix, the parse fails outright, and thus doesn't write the garbage.
I'm sure your next question will be:
How do I make the second parse work?
int main(int argc, char ** argv)
{
std::istringstream ss { argv[1] };
auto dt = std::chrono::system_clock::now();
ss >> date::parse("%FT%T%z", dt);
if (!ss.fail())
{
std::cout << "Cond 1 entered\n";
}
else
{
ss.clear();
ss.seekg(0);
date::month_day md;
std::chrono::minutes hm;
ss >> date::parse("%d-%m", md) >> date::parse(" %R", hm);
if (!ss.fail())
{
std::cout << "Cond 2 entered\n";
using namespace date;
auto y = year_month_day{floor<days>(dt)}.year();
dt = sys_days{y/md} + hm;
}
}
std::cout << dt << "\n";
}
The first parse is just as you had it, except that I switched the use of parse for from_stream which is a little higher level API. This doesn't really matter for the first parse, but makes the second parse neater.
For the second parse you need to parse two items:
A month_day
A time of day
And then combine those two elements with the current year to produce the desired time_point.
Now each parse fully specifies the variable it is parsing from the stream.
The mistake you made originally was in imagining that there is a "year field" under the hood of system_clock::time_point. And actually this data structure is nothing but a count of microseconds or nanoseconds (or whatever) since 1970-01-01 00:00:00 UTC. So the second parse has to:
Parse the fields, and then
Deconstruct the time_point into fields to get the current year, and then
Put the fields back together again into a time_point.

How to deal with time in C++

I have a question about managing the date and time in c++. I have two classes Plane and Flight in my program.
My Plane will consist data as:
string tailNumber;//Plane's unique trait
vector<vector<string>> planeSchedule;//2D string vector to contain plane's depature date and arrival date
My Flight class will consist data as:
string tailNumber;//Plane's unique trait
string departureDate;
string arrivalDate;
In my main class, I will input the value for departureDate and arrivalDate in format: "YYYY/MM/DD HH:MM" such as: "2019/04/15 10:30" and "2019/04/16 9:30" (I will use the 24-hour format and time will be GMT).
My question is how do I convert the two strings above to a proper format to store in my planeSchedule, so that I will be able to avoid the time conflict in the planeSchedule.
For example, If the next time I'm adding a flight with departure and arrival date beetween the: 2019/04/15 10:30" and "2019/04/16 9:30" such as: "2019/04/15 13:30" and "2019/04/16 7:30", I will get an error like "Flight conflict, plane is not available to flight."
My professor recommends using an unsigned long int to store time, but I really do not know where to start to solve this problem. Any help/suggestion is appreciated.
The go-to place regarding dates and times in C++ is <chrono>. Some of it has been with us since C++11, some of it we'll see coming with C++20. It works in conjunction with the C-style date and time utilities in <ctime>, which might even suffice for your purposes.
Trying to handle date / time as either integers or strings, parsing them from input, comparing, and converting them to strings for output, will effectively result in you reimplementing parts of what's already in those headers (a.k.a. "reinventing the wheel").
I have two pieces of advice based on long experience with systems that did it badly :-)
The first is to not store date and time information as strings or integral values, especially when C++ has very good support for that in std::chrono. If you use the correct types, comparisons and manipulations become relatively simple.
Second, make sure you use UTC for all times. You should convert local times to UTC as soon as possible after getting them, and convert back to local as late as possible when presenting them. This will also greatly simplify comparisons.
By way of example, here's a complete program which show the simplicity(a) in action:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <chrono>
using std::chrono::system_clock;
using std::chrono::duration_cast;
namespace {
system_clock::time_point getTimePoint(std::string strTime) {
std::tm myTm = {};
std::stringstream ss(strTime.c_str());
ss >> std::get_time(&myTm, "%Y/%m/%d %H:%M");
return system_clock::from_time_t(std::mktime(&myTm));
}
void outputTime(const char *desc, system_clock::time_point &tp) {
std::time_t now = system_clock::to_time_t(tp);
std::cout << desc
<< std::put_time(std::localtime(&now), "%Y-%m-%d %H:%M") << "\n";
}
}
int main() {
std::string startTime = "2019/04/15 10:30";
std::string endTime = "2019/04/16 09:30";
auto startTp = getTimePoint(startTime);
auto endTp = getTimePoint(endTime);
outputTime("Start time: ", startTp);
outputTime(" End time: ", endTp);
auto duration = duration_cast<std::chrono::minutes>(endTp - startTp);
std::cout << "\nThere are " << duration.count() << " minutes between "
<< startTime << " and " << endTime << "\n";
}
The output of that program is:
Start time: 2019-04-15 10:30
End time: 2019-04-16 09:30
There are 1380 minutes between 2019/04/15 10:30 and 2019/04/16 09:30
(a) Yes, the program may seem reasonably big but that's just because of the stuff making it a complete program. The getTimePoint and outputTime functions show how to do the conversion to and from time points, and the meat of the simplicity is the line containing duration_cast to get the number of minutes between the two time points.

How to insert a colon in between string as like date formats in c++?

I have string 20150410 121416 in c++.
I need to turn this into 20150410 12:14:16
How can i insert a colon to the string?
One can format date/times in C and C++ with strftime. There also exists a non-standard but common POSIX function called strptime one can use to parse times. One could use these to parse your date/time in your input format, and then format it back out in your desired format.
That is, assuming you didn't want to write the parsing code yourself.
If you have C++11, then you could use this free, open-source date/time library to help you do all this with strftime-like format strings. Such code could look like:
#include "tz.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::string input = "20150410 121416";
std::stringstream stream{input};
stream.exceptions(std::ios::failbit);
sys_seconds tp;
parse(stream, "%Y%m%d %H%M%S", tp);
auto output = format("%Y%m%d %T", tp);
std::cout << output << '\n';
}
Output:
20150410 12:14:16
One advantage of using a date/time parsing/formatting library, as opposed to just treating these as generic strings, is that you can more easily alter the formatting, or manipulate the datetime during the format conversion (e.g. have it change timezones).
For example, next month the specification might change on you and now you're told that this is a timestamp representing local time in Moscow and you need to convert it to local time in London and output it in the form YYYY-MM-DD HH:MM:SS <UTC offset>. The above code hardly changes at all if you're using a good date/time library.
#include "tz.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::string input = "20150410 121416";
std::stringstream stream{input};
stream.exceptions(std::ios::failbit);
local_seconds tp;
parse(stream, "%Y%m%d %H%M%S", tp);
auto moscow_time = make_zoned("Europe/Moscow", tp);
auto london_time = make_zoned("Europe/London", moscow_time);
auto output = format("%F %T %z", london_time);
std::cout << output << '\n';
}
2015-04-10 10:14:16 +0100
But if you started out just doing string manipulation, all of the sudden you've got a major task in front of you. Writing code that understands the semantics of the datetime "20150410 121416" is a significant leap above manipulating the characters of "20150410 121416" as a string.
<script type="text/javascript">
function formatTime(objFormField){
intFieldLength = objFormField.value.length;
if(intFieldLength==2 || intFieldLength == 2){
objFormField.value = objFormField.value + ":";
return false;
}
}
</script>
Enter time <input type="text" maxlength="5" minlength="5" onKeyPress="formatTime(this)"/>

C++ check if a date is valid

is there any function to check if a given date is valid or not?
I don't want to write anything from scratch.
e.g. 32/10/2012 is not valid
and 10/10/2010 is valid
If your string is always in that format the easiest thing to do would be to split the string into its three components, populate a tm structure and pass it to mktime(). If it returns -1 then it's not a valid date.
You could also use Boost.Date_Time to parse it:
string inp("10/10/2010");
string format("%d/%m/%Y");
date d;
d = parser.parse_date(inp, format, svp);
The boost date time class should be able to handle what you require.
See http://www.boost.org/doc/libs/release/doc/html/date_time.html
If the format of the date is constant and you don't want to use boost, you can always use strptime, defined in the ctime header:
const char date1[] = "32/10/2012";
const char date2[] = "10/10/2012";
struct tm tm;
if (!strptime(date1, "%d/%m/%Y", &tm)) std::cout << "date1 isn't valid\n";
if (!strptime(date2, "%d/%m/%Y", &tm)) std::cout << "date2 isn't valid\n";

Getting a unix timestamp as a string in C++

I'm using the function time() in order to get a timestamp in C++, but, after doing so, I need to convert it to a string. I can't use ctime, as I need the timestamp itself (in its 10 character format). Trouble is, I have no idea what form a time_t variable takes, so I don't know what I'm converting it from. cout handles it, so it must be a string of some description, but I have no idea what.
If anyone could help me with this it'd be much appreciated, I'm completely stumped.
Alternately, can you provide the output of ctime to a MySQL datetime field and have it interpreted correctly? I'd still appreciate an answer to the first part of my question for understanding's sake, but this would solve my problem.
time_t is some kind of integer. If cout handles it in the way you want, you can use a std::stringstream to convert it to a string:
std::string timestr(time_t t) {
std::stringstream strm;
strm << t;
return strm.str();
}
I had the same problem. I solved it as follows:
char arcString [32];
std::string strTmp;
// add start-date/start-time
if (strftime (&(arcString [0]), 20, "%Y-%m-%d_%H-%M-%S", (const tm*) (gmtime ((const time_t*) &(sMeasDocName.sStartTime)))) != 0)
{
strTmp = (char*) &(arcString [0]);
}
else
{
strTmp = "1970-01-01_00:00:00";
}
Try sprintf(string_variable, "%d", time) or std::string(itoa(time))?
http://www.codeguru.com/forum/showthread.php?t=231056
In the end time_t is just an integer.