Generating a short string containing the time with strange results - c++

I have a function which creates a std::string with the time in it in cpp as follows:
#include "msattime.h"
#include <QTime>
#include <stdio.h>
#include <iostream>
std::string msattime::getTime(){
QTime t = QTime::currentTime();
std::string s;
char hours[5];
char minutes[5];
int min = t.minute();
itoa(t.hour(), hours, 10);
itoa(min, minutes, 10);
if(min < 10){
minutes[1] = minutes[0];
minutes[0] = '0';
minutes[2] = '\0';
}
s.clear();
s = s.append(hours).append(":").append(minutes);
return s;
}
This works just fine is the time in the minutes field does not contain a zero in the tens position. I changed my system time to many different values and used cout to print the string that resulted from this function. This table shows the input on the left and the output on the right.
9:19 AM || 9:19
11:20 AM || 11:20
10:20 PM || 22:20
5:24 PM || 17:24
5:00 PM || 17:00[
7:03 PM || 19:03┐
11:04 PM || 23:04!! (This is supposed to be a single character double bang)
12:09 AM || 0:09┌
The values for the hour are all correct. At some point there will be an option for the user to change the display format to 12h but for now there are no errors in those results. The only error is in the minutes place where there is an extra character added after the string. I don't get any errors when I print the string to the terminal so that suggests that it is null terminated at some point.
I have a display routine for a low resolution black and white lcd display that will print a value with center alignment. If I print this string with that alignment it gets forced to left alignment. The center printing function calculates a starting position based off of string length and then begins printing from that position. Because this string is forced to left alignment when printing using this method there is reason to believe that it is very very long.
Does anyone have any ideas about why this might be happening to me or have a suggestion for another get time function that with the same or similar output?
Environment:
Window 7 Pro w/ SP1 64bit
QT Creator IDE
QT 5.1.1
MinGW 32bit compiler with C++11 compliance turned on

I'd use strftime to do the formatting. Code would be something like this:
std::string getTime(){
time_t now = time(NULL);
struct tm *t = localtime(&now);
char buffer[16];
strftime(buffer, sizeof(buffer), "%H:%M", t);
return std::string(buffer);
}
If you're using multiple threads, you may want switch to from localtime to localtime_r instead.

The QTime class provides toString. This should be enough:
return QTime::currentTime().toString("h:mm").toStdString();
I also would recommend to use QString until you really need std::string.

Related

RFC3339 and in UTC/Zulu string to integer C++

I am writing a C+ application for an embedded ARM device.
My application receives a date and time string in format RFC3339 and in UTC/Zulu. For example "2020-12-14T23:18:13Z"
I need to get the time from this in seconds, compare it to the current time in seconds and take action if there is a difference between those times of greater than 8 seconds.
I've included a third party header file to help, date.h from here : https://howardhinnant.github.io/date/date.html
I have to say i'm still struggling to properly get the seconds from the date and time string as an integer and do comparisons. It is always zero in my case. This is what I have been trying for example so far:
#include <iostream>
#include <string>
#include <sstream>
#include "date.h"
int main()
{
std::string ts = "2020-11-14T22:14:16Z"; // Literal string to test at first
std::istringstream infile1{ts};
sys_seconds tps;
infile1 >> parse("%FT%T%Ez", tps);
auto stampSinceEpoch_s = tps.time_since_epoch();
// Get the current time
auto now = system_clock::now();
auto now_s = time_point_cast<seconds>(now);
auto nowSinceEpoch_s = now_s.time_since_epoch();
if ( (nowSinceEpoch_s .count() - stampSinceEpoch_s.count()) > 8)
{
// Time difference is greater than 8 seconds, take action
}
}
Any help is greatly appreciated.

C++ cannot name txt file after current sytem time and date

Hi im having issue with this code i have made. It will compile but once i hit enter in the program it says this:
Unhandled exception at 0x008E8641 in Log Test.exe: 0xC0000005: Access violation reading location 0x566D846A.
Here is the code:
#include <iostream>
#include <time.h>
#include <fstream>
using namespace std;
int main() {
cin.get();
time_t Time;
Time = time(0);
string Date = __DATE__;
string LOG = Time + "_" + Date;
ofstream TEST;
TEST.open(LOG);
TEST << "This Text Should Appear Inside Of File.";
TEST.close();
cout << "Log has been Made.";
cin.get();
return 0;
}
I beleive that the problem is the time and how i tried putting it into a string but i dont see what i did doesn't work.
I would think that Time is an integer type so this:
Time + "_"
results in pointer addition so that what gets added to the string is a bad pointer to some location beyond the beginning of "_".
You see string literals like "_" actually resolve to an address (pointer). Adding an integer like Time to it simple makes it point elsewhere in memory.
First you need to convert your Time to a string.
I happen to have this code laying around that may work for you:
std::string get_stamp()
{
time_t t = std::time(0);
char buf[sizeof("YYYY-MM-DD HH-MM-SS")];
return {buf, std::strftime(buf, sizeof(buf), "%F %H-%M-%S", std::localtime(&t))};
}
Note: Using std::localtime is not threas-safe.
If you enabled compiler warnings, it should have screamed at you about this line:
string LOG = Time + "_" + Date;
Here, Time is being converted to a pointer, and you're getting undefined behaviour. For a not completely C++ solution, I recommend this simple approach:
time_t t = time(0);
struct tm ttm;
localtime_r( &t, &ttm );
char timestamp[20]; // actually only need 17 chars plus terminator.
sprintf_s( timestamp, sizeof(timestamp), "%04d-%02d-%02d_%02d%02d%02d",
ttm.tm_year + 1900, ttm.tm_mon + 1, ttm.tm_day, ttm.tm_hour, ttm.tm_min, ttm.tm_sec );
string logfilename = string(timestamp) + ".log";
ofstream logfile( logfilename.c_str() );
Note that localtime_r is not completely portable. On windows, use localtime_s, which unfortunately also reverses the order of the arguments.

Variable filename with ifstream and ofstream

I want to use the system date I have from the computer and use it as the name of the .txt file. Here's what I have so far:
void idle_time()
{
LASTINPUTINFO last_info;
last_info.cbSize = sizeof(LASTINPUTINFO);
int tickCount = 0;
int minutes = 0;
int count = 0;
SYSTEMTIME a;
while(true)
{
GetLastInputInfo(&last_info);
tickCount = GetTickCount();
int minutes = (tickCount - last_info.dwTime) / 60000;
count++;
string date;
date += a.wMonth;
date += "/";
date += a.wDay;
date += "/";
date += a.wYear;
if((minutes >= 1) && (count%3000==0))
{
//std::string filename = date;
//filename += ".txt";
ifstream in(string(date + ."txt").c_str());
float sum;
in >> sum;
sum++;
in.close();
ofstream out(string(date + ".txt").c_str());
out << sum;
out.flush();
out.close();
}
I'm sorry for the terrible indentation. This editor doesn't do it justice. But anyway, how would I use the date as the filename?
The date string you are using contains / characters which are used to separate directories in path. You simply need to replace them with another (not forbidden) character.
I'd also suggest you not to use m/d/Y date format for filenames. They do not sort well. Y-m-d is usually better because the files will be sorted from oldest to newest.
Edit: ah, and for my last statement to be true, you'd also need to pad month and day with zero to two digits, i.e. have something like 2011-08-05.
Ah, I see that the time appending is also done incorrectly. You can't append integers to strings like that. #hmjd already posted you one method of solving this; but I think it will be better to just use a dedicated time->string conversion method.
I'm not a Windows programmer, so I won't help you much with this SYSTEMTIME thing. You'll probably need to use GetTimeFormat(). But here's a simple example how to solve it with standard C/C++ strftime():
char date_buf[11];
time_t a = time(0);
strftime(date_buf, sizeof(buf), "%Y-%m-%d", gmtime(&t));
date += buf;
If you want the date in local time zone rather than UTC, use localtime() instead of gmtime().
Hope it helps someone. I believe GetTimeFormat() works similarly, so maybe this can guide you a bit.

How do I find the current system timezone?

On Linux, I need to find the currently configured timezone as an Olson location. I want my (C or C++) code to be portable to as many Linux systems as possible.
For example. I live in London, so my current Olson location is "Europe/London". I'm not interested in timezone IDs like "BST", "EST" or whatever.
Debian and Ubuntu have a file /etc/timezone that contains this information, but I don't think I can rely on that file always being there, can I? Gnome has a function oobs_time_config_get_timezone() which also returns the right string, but I want my code to work on systems without Gnome.
So, what's the best general way to get the currently configured timezone as an Olson location, on Linux?
It's hard to get a reliable answer. Relying on things like /etc/timezone may be the best bet.
(The variable tzname and the tm_zone member of struct tm, as suggested in other answers, typically contains an abbreviation such as GMT/BST etc, rather than the Olson time string as requested in the question).
On Debian-based systems (including Ubuntu), /etc/timezone is a file containing the right answer.
On some Redhat-based systems (including at least some versions of CentOS, RHEL, Fedora), you can get the required information using readlink() on /etc/localtime, which is a symlink to (for example) /usr/share/zoneinfo/Europe/London.
OpenBSD seems to use the same scheme as RedHat.
However, there are some issues with the above approaches. The /usr/share/zoneinfo directory also contains files such as GMT and GB, so it's possible the user may configure the symlink to point there.
Also there's nothing to stop the user copying the right timezone file there instead of creating a symlink.
One possibility to get round this (which seems to work on Debian, RedHat and OpenBSD) is to compare the contents of the /etc/localtime file to the files under /usr/share/zoneinfo, and see which ones match:
eta:~% md5sum /etc/localtime
410c65079e6d14f4eedf50c19bd073f8 /etc/localtime
eta:~% find /usr/share/zoneinfo -type f | xargs md5sum | grep 410c65079e6d14f4eedf50c19bd073f8
410c65079e6d14f4eedf50c19bd073f8 /usr/share/zoneinfo/Europe/London
410c65079e6d14f4eedf50c19bd073f8 /usr/share/zoneinfo/Europe/Belfast
410c65079e6d14f4eedf50c19bd073f8 /usr/share/zoneinfo/Europe/Guernsey
410c65079e6d14f4eedf50c19bd073f8 /usr/share/zoneinfo/Europe/Jersey
410c65079e6d14f4eedf50c19bd073f8 /usr/share/zoneinfo/Europe/Isle_of_Man
...
...
Of course the disadvantage is that this will tell you all timezones that are identical to the current one. (That means identical in the full sense - not just "currently at the same time", but also "always change their clocks on the same day as far as the system knows".)
Your best bet may be to combine the above methods: use /etc/timezone if it exists; otherwise try parsing /etc/localtime as a symlink; if that fails, search for matching timezone definition files; if that fails - give up and go home ;-)
(And I have no idea whether any of the above applies on AIX...)
I've been working on a free, open source C++11/14 library which addresses this question in a single line of code:
std::cout << date::current_zone()->name() << '\n';
It is meant to be portable across all recent flavors of Linux, macOS and Windows. For me this program outputs:
America/New_York
If you download this library, and it doesn't work you, bug reports are welcome.
There is no standard c or c++ function for this. However, GNU libc has an extention. its struct tm has two extra members:
long tm_gmtoff; /* Seconds east of UTC */
const char *tm_zone; /* Timezone abbreviation */
This means that if you use one of the functions which populates a struct tm (such as localtime or gmtime) you can use these extra fields. This is of course only if you are using GNU libc (and a sufficiently recent version of it).
Also many systems have a int gettimeofday(struct timeval *tv, struct timezone *tz); function (POSIX) which will fill in a struct timezone. This has the following fields:
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};
Not exactly what you asked for, but close...
Pretty late in the day, but I was looking for something similar and found that ICU library has the provision to get the Olson timezone ID: http://userguide.icu-project.org/datetime/timezone
It is now installed on most linux distributions (install the libicu-dev package or equivalent). Code:
#include <unicode/timezone.h>
#include <iostream>
using namespace U_ICU_NAMESPACE;
int main() {
TimeZone* tz = TimeZone::createDefault();
UnicodeString us;
tz->getID(us);
std::string s;
us.toUTF8String(s);
std::cout << "Current timezone ID: " << s << '\n';
delete tz;
return 0;
}
And to get the abbreviated/POSIX timezone names (should also work on Windows):
#include <time.h>
int main() {
time_t ts = 0;
struct tm t;
char buf[16];
::localtime_r(&ts, &t);
::strftime(buf, sizeof(buf), "%z", &t);
std::cout << "Current timezone (POSIX): " << buf << std::endl;
::strftime(buf, sizeof(buf), "%Z", &t);
std::cout << "Current timezone: " << buf << std::endl;
I see two major linux cases:
Ubuntu. There should be a /etc/timezone file. This file should only contain the timezone and nothing else.
Red Hat. There should be a /etc/sysconfig/clock that contains something like: ZONE="America/Chicago"
In addition, Solaris should have an /etc/TIMEZONE file that contains a line like: TZ=US/Mountain
So based on the above, here is some straight C that I believe answers the OP's question. I have tested it on Ubuntu, CentOS (Red Hat), and Solaris (bonus).
#include <string.h>
#include <strings.h>
#include <stdio.h>
char *findDefaultTZ(char *tz, size_t tzSize);
char *getValue(char *filename, char *tag, char *value, size_t valueSize);
int main(int argc, char **argv)
{
char tz[128];
if (findDefaultTZ(tz, sizeof(tz)))
printf("Default timezone is %s.\n", tz);
else
printf("Unable to determine default timezone.\n");
return 0;
}
char *findDefaultTZ(char *tz, size_t tzSize)
{
char *ret = NULL;
/* If there is an /etc/timezone file, then we expect it to contain
* nothing except the timezone. */
FILE *fd = fopen("/etc/timezone", "r"); /* Ubuntu. */
if (fd)
{
char buffer[128];
/* There should only be one line, in this case. */
while (fgets(buffer, sizeof(buffer), fd))
{
char *lasts = buffer;
/* We don't want a line feed on the end. */
char *tag = strtok_r(lasts, " \t\n", &lasts);
/* Idiot check. */
if (tag && strlen(tag) > 0 && tag[0] != '#')
{
strncpy(tz, tag, tzSize);
ret = tz;
}
}
fclose(fd);
}
else if (getValue("/etc/sysconfig/clock", "ZONE", tz, tzSize)) /* Redhat. */
ret = tz;
else if (getValue("/etc/TIMEZONE", "TZ", tz, tzSize)) /* Solaris. */
ret = tz;
return ret;
}
/* Look for tag=someValue within filename. When found, return someValue
* in the provided value parameter up to valueSize in length. If someValue
* is enclosed in quotes, remove them. */
char *getValue(char *filename, char *tag, char *value, size_t valueSize)
{
char buffer[128], *lasts;
int foundTag = 0;
FILE *fd = fopen(filename, "r");
if (fd)
{
/* Process the file, line by line. */
while (fgets(buffer, sizeof(buffer), fd))
{
lasts = buffer;
/* Look for lines with tag=value. */
char *token = strtok_r(lasts, "=", &lasts);
/* Is this the tag we are looking for? */
if (token && !strcmp(token, tag))
{
/* Parse out the value. */
char *zone = strtok_r(lasts, " \t\n", &lasts);
/* If everything looks good, copy it to our return var. */
if (zone && strlen(zone) > 0)
{
int i = 0;
int j = 0;
char quote = 0x00;
/* Rather than just simple copy, remove quotes while we copy. */
for (i = 0; i < strlen(zone) && i < valueSize - 1; i++)
{
/* Start quote. */
if (quote == 0x00 && zone[i] == '"')
quote = zone[i];
/* End quote. */
else if (quote != 0x00 && quote == zone[i])
quote = 0x00;
/* Copy bytes. */
else
{
value[j] = zone[i];
j++;
}
}
value[j] = 0x00;
foundTag = 1;
}
break;
}
}
fclose(fd);
}
if (foundTag)
return value;
return NULL;
}
FWIW, RHEL/Fedora/CentOS have /etc/sysconfig/clock:
ZONE="Europe/Brussels"
UTC=true
ARC=false
I liked the post made by psmears and implemented this script to read the first output of the list. Of course there must have more elegant ways of doing this, but there you are...
/**
* Returns the (Linux) server default timezone abbreviation
* To be used when no user is logged in (Ex.: batch job)
* Tested on Fedora 12
*
* #param void
* #return String (Timezone abbreviation Ex.: 'America/Sao_Paulo')
*/
public function getServerTimezone()
{
$shell = 'md5sum /etc/localtime';
$q = shell_exec($shell);
$shell = 'find /usr/share/zoneinfo -type f | xargs md5sum | grep ' . substr($q, 0, strpos($q, '/') - 2);
$q = shell_exec($shell);
$q = substr($q, strpos($q, 'info/') + 5, strpos($q, " "));
return substr($q, 0, strpos($q, chr(10)));
}
In my Brazilian Fedora 12, it returns:
Brazil/East
And does exactly what I need.
Thank you psmears
Here's code that works for most versions of Linux.
#include <iostream>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
using namespace std;
void main()
{
char filename[256];
struct stat fstat;
int status;
status = lstat("/etc/localtime", &fstat);
if (S_ISLNK(fstat.st_mode))
{
cout << "/etc/localtime Is a link" << endl;
int nSize = readlink("/etc/localtime", filename, 256);
if (nSize > 0)
{
filename[nSize] = 0;
cout << " linked filename " << filename << endl;
cout << " Timezone " << filename + 20 << endl;
}
}
else if (S_ISREG(fstat.st_mode))
cout << "/etc/localtime Is a file" << endl;
}
According to this page, it looks like if you #include <time.h> it will declare the following.
void tzset (void);
extern char *tzname[2];
extern long timezone;
extern int daylight;
Does that give you the information that you need?
On Linux, I need to find the current timezone as an Olson location. I want my (C or C++) code to be portable to as many Linux systems as possible.
If you want to be portable, then use only GMT internally. Due to multi-user heritge, *NIX system clock is normally is in GMT and there is no system wide timezone - because different users connected to the system might be living in different timezones.
The user specific timezone is reflected in TZ environment variable and you might need to use that only when converting internal date/time into the user readable form. Otherwise, localtime() takes care of it automatically for you.
The libc accesses the Olson database when tzset is called, and uses simplified time zones afterwards. tzset looks at the TZ environment variable first, and falls back to parsing the binary data in /etc/localtime.
At first systemd standardised on having the Olson time zone name in /etc/timezone, Debian-style. After systemd 190 and the /usr merge, systemd only reads and updates /etc/localtime, with the extra requirement that the file be a symlink to /usr/share/zoneinfo/${OLSON_NAME}.
Looking at TZ, then readlink("/etc/localtime"), is the most reliable way to match the libc's tzset logic and still keep symbolic Olson names. For systems that don't follow the systemd symlink convention, reading /etc/timezone (and possibly checking that /usr/share/zoneinfo/$(</etc/timezone) is the same as /etc/localtime) is a good fallback.
If you can live without symbolic names, parsing the /etc/localtime tzfile is as portable as it gets, though a lot more complex. Reading just the last field gets you a Posix time zone (for example: CST5CDT,M3.2.0/0,M11.1.0/1), which can interoperate with a few time-handling libraries, but drops some of the metadata (no historical transition info).
Since tzselect was not mentioned by anyone and you do need a nearly goof-proof solution, work with what Olson did. Get the tzcode and tzdata files from elsie, plus tab files.
ftp://elsie.nci.nih.gov
In March 2017, the correct location to download from would be ftp://ftp.iana.org/tz/releases (and download tzcode2017a.tar.gz and tzdata2017a.tar.gz).
Then get tzselect.ksh from the glibc download. Then you can see how to reverse engineer timezones. One sticking point: you WILL sometimes have to ask what country and city the linux box is in. You can serialize that data if you want, and then verify it against the timezone data you can find.
There is no way to do this reliably all the time without the possibility of user intervention, for example, as part of program installation.
Good luck on Arizona in general, and in Western Indiana.... hopefully your code is going to run elsewhere.
The code below was tested successfully in bash shell on
SUSE Linux Enterprise Server 11
Ubuntu 20.04.5 LTS
CentOS Linux release 7.9.2009
Shell code:
echo $(hash=$(md5sum /etc/localtime | cut -d " " -f 1) ; find /usr/share/zoneinfo -type f -print0 | while read -r -d '' f; do md5sum "$f" | grep "$hash" && break ; done) | rev | cut -d "/" -f 2,1 | rev
Result example:
Europe/Vienna

String manipulation using Arduino and C++

I am trying to manipulate a string in C++. I am working with an Arduino board so I am limited on what I can use. I am also still learning C++ (Sorry for any stupid questions)
Here is what I need to do:
I need to send miles per hour to a 7 segment display. So if I have a number such as 17.812345, I need to display 17.8 to the 7 segment display. What seems to be most efficient way is to first multiply by 10 (This is to shift the decimal point right one place), then cast 178.12345 to an int (to chop decimal points off). The part I am stuck on is how to break apart 178. In Python I could slice the string, but I can't find anything on how to do this in C++ (or at least, I can't find the right terms to search for)
There are four 7 segment displays and a 7 segment display controller. It will measure up to tenths of a mile per hour. Thank you very much for an assistance and information you can provide me.
It would probably be easiest to not convert it to a string, but just use arithmetic to separate the digits, i.e.
float speed = 17.812345;
int display_speed = speed * 10 + 0.5; // round to nearest 0.1 == 178
int digits[4];
digits[3] = display_speed % 10; // == 8
digits[2] = (display_speed / 10) % 10; // == 7
digits[1] = (display_speed / 100) % 10; // == 1
digits[0] = (display_speed / 1000) % 10; // == 0
and, as pointed out in the comments, if you need the ASCII value for each digit:
char ascii_digits[4];
ascii_digits[0] = digits[0] + '0';
ascii_digits[1] = digits[1] + '0';
ascii_digits[2] = digits[2] + '0';
ascii_digits[3] = digits[3] + '0';
This a way you can do it in C++ without modulus math (either way seems fine to me):
#include "math.h"
#include <stdio.h>
#include <iostream.h>
int main( ) {
float value = 3.1415;
char buf[16];
value = floor( value * 10.0f ) / 10.0f;
sprintf( buf, "%0.1f", value );
std::cout << "Value: " << value << std::endl;
return 0;
}
If you actually want to be processing this stuff as strings, I would recommend looking into stringstream. It can be used much the same as any other stream, such as cin and cout, except instead of sending all output to the console you get an actual string out of the deal.
This will work with standard C++. Don't know much about Arduino, but some quick googling suggests it won't support stringstreams.
A quick example:
#include <sstream> // include this for stringstreams
#include <iostream>
#include <string>
using namespace std; // stringstream, like almost everything, is in std
string stringifyFloat(float f) {
stringstream ss;
ss.precision(1); // set decimal precision to one digit.
ss << fixed; // use fixed rather than scientific notation.
ss << f; // read in the value of f
return ss.str(); // return the string associated with the stream.
}
int main() {
cout << stringifyFloat(17.812345) << endl; // 17.8
return 0;
}
You can use a function such as this toString and work your way up from there, like you would in Python, or just use modulo 10,100,1000,etc to get it as numbers. I think manipulating it as a string might be easier for you, but its up to you.
You could also use boost::lexical_cast, but it will probably be hard to get boost working in an embedded system like yours.
A good idea would be to implement a stream for the display. That way the C++ stream syntax could be used and the rest of the application would remain generic. Although this may be overkill for an embedded system.
If you still want to use std::string you may want to use a reverse iterator. This way you can start at the right most digit (in the string) and work towards the left, one character at a time.
If you have access to the run-time library code, you could set up a C language I/O for the display. This is easier to implement than a C++ stream. You could then use fprint, fputs to write to the display. I implemented a debug port in this method, and it was easier for the rest of the developers to use.