Splitting a `char[]` in C++ - c++

I have a char[] in YYYYMMDDHHMMSS format.
e.g. 2011052504572
I want to retrieve the year, month, date, hour, minute and second from this char. How do I do that?
NOTE:I cant use any third party dll.
Thanks,
Syd

If you're using the STL then just put the string into a std::string and use the substr method:
std::string dateTime=......;
std::string year=dateTime.substr(0,4);
std::string month=dateTime.substr(4,2);
// etc
// etc

Use string::substr() for this purpose. Example,
string date = "20110524112233";
string year = date.substr(0, 4);
string month = date.substr(4, 2);
string day = date.substr(6, 2);
string hour = date.substr(8, 2);
string minute = date.substr(10, 2);
string second = date.substr(12, 2);

It depends on whether you want to extract the values as text, or convert them to numbers. For getting lots of strings, you can use std::string and substr() as thoroughly illustrated in other answers.
If you want to get numbers that you can then calculate with, then one approach is:
int year, month, day, hour, minute, second;
if (sscanf(input, "%.4d%.2d%.2d%.2d%.2d%.2d",
&year, &month, &day, &hour, &minute, &second) == 6)
{
// all 6 conversions worked... can use the values...
int second_in_day = hour * 3600 + minute * 60 + second;
...
}
Another approach is to use strptime() - if your system has it. It parses a string into a broken-down-time structure:
struct tm tm;
strptime(input, "%Y%m%d%H%M%S", &tm);
// parsed values are in tm.tm_year, tm.tm_mon, tm.tm_mday,
// tm.tm_hour, tm.tm_min, tm.tm_sec
// further, tm_wday has day of week, tm_yday has day in year
// i.e. it actually understands the date, not just chopping up numbers/text
Note: sscanf() and strncpy() are C functions callable from C++, and they're not as safe to use as C++-specific functionality (std::string, std::istringstream) in that small misunderstandings and mistakes in handling the data can lead to not just erroneous results, but program crashes. So, read the manual pages for these things carefully if you use them.

Use strncpy... okay, so if it is not homework, then this is the best way. The others using std::string are wasting resources:
static const char *yyyymmddhhmmss = "20110526101559";
char year[5] = { '\0' };
char month[3] = { '\0' };
char day[3] = { '\0' };
char hour[3] = { '\0' };
char minute[3] = { '\0' };
char second[3] = { '\0' };
strncpy(year, yyyymmddhhmmss, 4);
strncpy(month, &yyyymmddhhmmss[4], 2);
strncpy(day, &yyyymmddhhmmss[6], 2);
strncpy(hour, &yyyymmddhhmmss[8], 2);
strncpy(minute, &yyyymmddhhmmss[10], 2);
strncpy(second, &yyyymmddhhmmss[12], 2);

Since you have the input in the form of a char[] (or char*—for
this use, it comes out to the same thing), the simplest solution is
probably using the two iterator constructors for std::string to creat
a string for each field, e.g.:
std::string year ( date , date + 4 );
std::string month( date + 4, date + 6 );
std::string day ( date + 6, date + 8 );
// ...
If you need the numerical values, boost::lexical_cast can be used,
e.g.:
int extractField( char const* string, int begin, int end )
{
return boost::lexical_cast<int>(
std::string( date + begin, date + end ) );
}
int year = extractField( date, 0, 4 );
int year = extractField( date, 4, 6 );
// ...

Related

Parsing Subsecond Date with Howard Hinnant Date Library

I have a date string like so YYYYMMDD HHMMSSFFF. I am trying to use Howard Hinnats date library. Snippet of code is like so,
std::chrono::system_clock::time_point tp;
char date[20] = {0};
std::istringstream ss{date};
ss >> date::parse("%Y%m%d %H%M%S%s", tp);
long ts = (std::chrono::time_point_cast<std::chrono::nanoseconds>(tp)
.time_since_epoch() /
std::chrono::nanoseconds(1));
But this code isn't reading the subsecond FFF. I loooked on the documentation here and it states that %s represents fractional of a second time. An example value for date is 20170110 103648340. But when I output ts I get 0. If you are wondering why I convert to nanoseconds its because I need the date in nanoseconds for other operations.
Use %T, it seems to work. Here is an example:
#include <date/date.h>
int main()
{
std::string dt{ "20190501 113001234" };
dt = dt.insert(11, ":");
dt = dt.insert(14, ":");
dt = dt.insert(17, ".");
// now we have "20190501 11:30:01.234"
std::chrono::system_clock::time_point tp;
std::istringstream ss{ dt };
ss >> date::parse("%Y%m%d %T", tp);
long ts = (std::chrono::time_point_cast<std::chrono::nanoseconds>(tp)
.time_since_epoch() /
std::chrono::nanoseconds(1));
}
You could also parse it this way:
sys_seconds tp;
int ims;
ss >> parse("%Y%m%d %H%M%2S", tp) >> ims;
return tp + milliseconds{ims};
The %2S says: parse as much as 2 chars for the seconds. That leaves the trailing three digits yet to be parsed. Pick those up with a integral parse and convert that integer to milliseconds, and you're good.
This won't work if there are trailing digits after the 3 millisecond digits.

Get System Date Format String Win32

I need to get the system current date format string like ("dd-mm-yyyy" , "mm/dd/yyyy" ect.
GetDateFormat() API returns the formatted string like "12-09-2015" but need string like "dd-mm-yyyy"
C# solution
string sysFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
But I need in Win32.
You can get a list of format strings currently applicable, by enumerating them. This is done through EnumDateFormats. Note, that there can be (and usually is) more than one, so you will have to decide, which one to pick1).
The following code returns the system's default short dateformat pattern:
static std::list<std::wstring> g_DateFormats;
BOOL CALLBACK EnumDateFormatsProc( _In_ LPWSTR lpDateFormatString ) {
// Store each format in the global list of dateformats.
g_DateFormats.push_back( lpDateFormatString );
return TRUE;
}
std::wstring GetShortDatePattern() {
if ( g_DateFormats.size() == 0 &&
// Enumerate all system default short dateformats; EnumDateFormatsProc is
// called for each dateformat.
!::EnumDateFormatsW( EnumDateFormatsProc,
LOCALE_SYSTEM_DEFAULT,
DATE_SHORTDATE ) ) {
throw std::runtime_error( "EnumDateFormatsW" );
}
// There can be more than one short date format. Arbitrarily pick the first one:
return g_DateFormats.front();
}
int main() {
const std::wstring strShortFormat = GetShortDatePattern();
return 0;
}
1) The .NET implementation does the same thing. From its list of candidates, it arbitrarily picks the first one.
You can use time function together with localtime function.
Example code:
//#include <time.h>
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
//The years are since 1900 according the documentation, so add 1900 to the actual year result.
char cDate[255] = {};
sprintf(cDate, "Today is: %d-%d-%d", timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year + 1900);

C++ Split decimal string into two integers

Given a decimal values (seconds and fractions thereof) as a string such as
std::string span = "ssss.nnnn" // ssss is value in seconds, nnnn is fractional seconds
What is the best way to convert it to a timeval structure (val.ts_sec and val.ts_usec) or a timespec structure (tv_sec and tv_nsec).
Most of the answers discuss converting values or are not C++. Some answers get very complex or set up classes which really is too much for this usage.
Obviously sscanf or istringstream can be used if the two values are separated by white space. However, is there a simple way of doing this if they are separated by a "." without looping over the character buffer searching for the "."
EDIT: As Borgleader rightly mentioned, simply reading into a double can incur precision loss if the timestamp becomes sufficiently large (larger than a million, give or take). A numerically stable way is
timeval v;
time_t seconds;
double fraction;
std::istringstream parser(span);
if(parser >> seconds >> std::noskipws >> fraction) {
v.tv_sec = seconds;
v.tv_usec = static_cast<suseconds_t>(fraction * 1e6);
}
Since now the fraction part is guaranteed to be small enough that the mantissa of an ieee-754 double will cover more than 9 decimal digits after the comma. One possible addition is
v.tv_usec = static_cast<suseconds_t>(fraction * 1e6 + 0.5); // rounding to nearest instead of down
depending on your use case.
If you decide to use string class and its functions If the number is always decimal, then I would suggest the following solution:
string span = "1234.123";
span += "000000";
size_t pos = span.find('.');
struct timeval val;
val.tv_sec = stol(span.substr(0,pos));
val.tv_usec = stol(span.substr(pos+1,6));
If the string may also get integer value without the dot '.' character then use
string span = "1234";
size_t pos = span.find('.');
struct timeval val;
val.tv_sec = stol( (pos!=string::npos)? span.substr(0,pos):span );
val.tv_usec = (pos!=string::npos)? stol((span+"000000").substr(pos+1,6)):0;
This solution also uses some c++11.
You can use strtok_s to split a string based off a delimiter. In your case would be "."
#include <iostream>
#include <string>
int main()
{
std::string span = "ssss.nnnn";
char * span1 = (char *)span.c_str();
char * pch = NULL;
char * context;
pch = strtok_s(span1, " .", &context);
while (pch != NULL)
{
printf("%s\n", pch);
pch = strtok_s(NULL, " .", &context);
}
return 0;
}
Output:
ssss
nnnn
I just found this as a possible answer. I would still like to find something else as well.
Parse (split) a string in C++ using string delimiter (standard C++)
strtok allows you to pass in multiple chars as delimiters. I bet if
you passed in ">=" your example string would be split correctly (even
though the > and = are counted as individual delimiters).
EDIT if you don't want to use c_str() to convert from string to char*,
you can use substr and find_first_of to tokenize.
string token, mystring("scott>=tiger");
while(token != mystring){
token = mystring.substr(0,mystring.find_first_of(">="));
mystring = mystring.substr(mystring.find_first_of(">=") + 1);
printf("%s ",token.c_str());
}
Update:
#Wintermute pointed out that the following code snippet would not work because of the possibility of leading zeros.
string span;
int sec;
int usec;
timeval inTime;
sscanf(span.c_str(), "%d.%d", &sec, &usec);
inTime.tv_sec = sec;
inTime.tv_usec = usec;

Build string as in printf function

printf("%d.%d.%d", year, month, day);
Can I do the same but without printing, smth like
char* date = "%d.%d.%d", year, month, day;
Or maybe some other simple ways to do that?
In plain c there is asprintf() which will allocate memory to hold the resulting string:
#include <stdio.h>
char *date;
asprintf(&date, "%d.%d.%d", year, month, day);
(error handling omitted)
Since you have tagged C++ you probably want to use the C++ solutions.
In C++:
#include <string>
std::string date = std::to_string(year) + '.' +
std::to_string(month) + '.' + std::to_string(day);
If you need the underlying char const *, say date.c_str().
The function std::to_string uses snprintf internally; you should probably look up that function, too, as it is fairly fundamental to formatted output, and you can use it directly if you really think you need to.
There are various implementations of a format function that looks something like:
std::string format(const std::string& fmt, ...);
so your example would be:
std::string date = format("%d.%d.%d", year, month, day);
One possible implementation is shown below.
Boost has a format library that works a little differently. It assumes you like cin, cout, and their ilk:
cout << boost::format("%1%.%2%.%3%") % year % month % day;
Or, if you just wanted a string:
boost::format fmt("%1%.%2%.%3%");
fmt % year % month % day;
std::string date = fmt.str();
Note that % flags are not the ones you're used to.
Finally, if you want a C string (char*) instead of a C++ string, you could use the asprintf function:
char* date;
if(asprintf(&date, "%d.%d.%d", year, month, day) == -1)
{ /* couldn't make the string; format was bad or out of memory. */ }
You could even use vasprintf to make your own format function returning a C++ string:
std::string format(const char* fmt, ...)
{
char* result = 0;
va_list ap;
va_start(ap, fmt);
if(vasprintf(*result, fmt, ap) == -1)
throw std::bad_alloc();
va_end(ap);
std::string str_result(result);
free(result);
return str_result;
}
This isn't terribly efficient, but it works. There also might be a way to call vsnprintf twice, the first with no buffer to get the formatted string length, then allocate the string object with the right capacity, then call the second time to get the string. This avoids allocating the memory twice, but has to make two passes through the formatted string.
In C++ I wrote a function to create strings using the printf format.
Headerfile stringf.h:
#ifndef STRINGF_H
#define STRINGF_H
#include <string>
template< typename... argv >
std::string stringf( const char* format, argv... args ) {
const size_t SIZE = std::snprintf( NULL, 0, format, args... );
std::string output;
output.resize(SIZE+1);
std::snprintf( &(output[0]), SIZE+1, format, args... );
return std::move(output);
}
#endif
Usage:
#include "stringf.h"
int main(){
int year = 2020;
int month = 12;
int day = 20
std::string date = stringf("%d.%d.%d", year, month, day);
// date == "2020.12.20"
}
In C language use sprintf function from stdio.h header file.
char buffer[100];
sprintf(buffer,"%d.%d.%d", year, month, day);
See here for more info.

How to convert a string to datetime in C++

I have a resultset(from a function) which is based on time. But the datetime value is in string format(e.g. "21:5 Jan 23, 11"). I want to convert "21:5 Jan 23, 11" to datetime. How can I do this in C++? I just want to filter records for today. So i need to retrieve the current date from "21:5 Jan 23, 11".
Edit:
I can get the current date and time using
SYSTEMTIME st;
GetSystemTime(&st);
Is there any way to convert "21:5 Jan 23, 11" in the above format?
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
// Converts UTC time string to a time_t value.
std::time_t getEpochTime(const std::wstring& dateTime)
{
// Let's consider we are getting all the input in
// this format: '2014-07-25T20:17:22Z' (T denotes
// start of Time part, Z denotes UTC zone).
// A better approach would be to pass in the format as well.
static const std::wstring dateTimeFormat{ L"%Y-%m-%dT%H:%M:%SZ" };
// Create a stream which we will use to parse the string,
// which we provide to constructor of stream to fill the buffer.
std::wistringstream ss{ dateTime };
// Create a tm object to store the parsed date and time.
std::tm dt;
// Now we read from buffer using get_time manipulator
// and formatting the input appropriately.
ss >> std::get_time(&dt, dateTimeFormat.c_str());
// Convert the tm structure to time_t value and return.
return std::mktime(&dt);
}
I'm not a C++ programmer but in C you can use strptime http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
The most general C++ way is to use Boost.DateTime.
This is sample program to validate input datetime and parse
#include <iostream>
#include <comutil.h>
#include <iomanip>
bool ValidateAndParseDateTime(const std::wstring & strInputDate, std::tm & date)
{
bool bSuccess = false;
if (!strInputDate.empty())
{
_variant_t varIn = strInputDate.c_str();
if (SUCCEEDED(VariantChangeTypeEx(&varIn, &varIn, GetThreadLocale(), 0, VT_DATE)))
{
std::get_time(&date, strInputDate.c_str());
bSuccess = true;
}
}
}
int main()
{
std::tm date;
std::wstring strInputDate = L"7/20/2020 1:29:37 PM";
if (!ValidateAndParseDateTime(strInputDate, date))
{
//Invalid date and time
}
return 0;
}
If all you need is to check whether two strings have same date or not and if it is guaranteed that strings are in the same format, then there is no need to convert it to date time. You just have to compare the substrings after the first space character. If they are same, then the dates are same. Here is the sample code:
using namespace std;
string getCurrentDate()
{
//Enumeration of the months in the year
const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
//Get the current system date time
SYSTEMTIME st;
GetSystemTime(&st);
//Construct the string in the format "21:5 Jan 23, 11"
ostringstream ss;
ss<<st.wHour<<":"<<st.wMinute<<
" "<<months[st.wMonth-1]<<
" "<<st.wDay<<", "<<st.wYear%1000;
//Extract the string from the stream
return ss.str();
}
string getDateString(const string& s)
{
//Extract the date part from the string "21:5 Jan 23, 11"
//Look for the first space character in the string
string date;
size_t indx = s.find_first_of(' ');
if(indx != string::npos) //If found
{
//Copy the date part
date = s.substr(indx + 1);
}
return date;
}
bool isCurrentDate(const string& s1)
{
//Get the date part from the passed string
string d1 = getDateString(s1);
//Get the date part from the current date
string d2 = getDateString(getCurrentDate());
//Check whether they match
return ! d1.empty() && ! d2.empty() && d1 == d2;
}
int main( void )
{
bool s = isCurrentDate("21:5 Jan 23, 11");
bool s1 = isCurrentDate("21:5 Jan 25, 11");
return 0;
}
In the function "ValidateAndParseDateTime" above, I think it's supposed to return "bSuccess". Without it, caller will always get "Invalid date and time".
{