Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Validate date/time

Status
Not open for further replies.

mangocinn

Programmer
Jul 26, 2001
66
0
0
US
I have a string (std::string) that needs to be checked to see if the value in the string is a valid date/time. What is the best way to validate a date/time in C++.
 
No (platform-independent) silver bullet to do that (remember date format differences).

Try to parse a date string (by find date separator/substr combinations + atoi() calls), extract day, month, year as integers, then (if the date parsed OK) assign them into (cleared) struct tm var, then apply strftime to it (test its return code).

You may also check the date (year, month, day triad) by hands (remember leap years: days 1..29 in feb). I think it's the best approach.
 
a little more info...

the date/time string should be in the following format...
yyyy/mm/dd hh:mm:ss

If the value in my string is not in that format, then it is not valid for my purposes. How can I verify that the value in the string is in the format "yyyy/mm/dd hh:mm:ss"?
 
You need to parse the string and verify each piece character by character...

I thought there was a C function that did that, but it looks like they only convert TO strings instead of FROM strings. :(
 
Well, try this improvisation:
Code:
#include <cstring>
#include <string>

// Declaration:
struct TStamp
{
	TStamp();
	TStamp(const char* pstr);
	bool parse(const char* pstr);
	bool parse(const string& str) 
        { 
             return parse(str.c_str()); 
        }
	bool isOK() const { return y != 0; }
	bool verify();
	bool isLeap() const;
	bool clear();

	std::string getString() const;

	TStamp& operator =(const char* pstr)
	{
		parse(pstr);
		return *this;
	}
	int y, mon, d, h, m, s;
};

// Implementation:

namespace { // Locals:
const int dpm[13] = 
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
}	// End of Locals.

TStamp::TStamp(): y(0), mon(0), d(0), h(0), m(0), s(0)
{
}

TStamp::TStamp(const char* pstr):
y(0), mon(0), d(0), h(0), m(0), s(0)
{
	parse(pstr);
}

bool TStamp::clear()
{
	y = mon = d = h = m = s = 0;
	return false;
}

bool TStamp::isLeap() const
{
	return y > 0
		? y%4 == 0 && (y % 100 != 0 || y%400 == 0)
		: false;
}

bool TStamp::verify()
{
  if (y <= 0 || mon <= 0 || mon > 12 || d <= 0 || d > 31)
     return clear();
	bool res;
	res = (mon == 2
		? d <= (isLeap()?29:28)
		: d <= dpm[mon]
		);
	return res?true:clear();
}

std::string TStamp::getString() const
{
	char	buff[24];
	if (isOK())
		sprintf(buff,"%4d/%02d/%02d %02d:%02d:%02d",
			y,mon,d,h,m,s);
	else
		strcpy(buff,"Bad timestamp value");
	return buff; // Don't worry: std::string returned...
}

bool TStamp::parse(const char* pstr)
{
	if (!pstr || !*pstr)
		return clear();
	const char* p = pstr + strspn(pstr," \t");
	long	x;
	char*	pend;

	x = strtol(p,&pend,10);
	if (x < 1900 || x > 2100 || *pend != '/') 
        // change min/max years if you wish...
		return clear();
	y = x;
	
	x = strtol(p=pend+1,&pend,10);
	if (x < 1 || x > 12 || *pend != '/')
		return clear();
	mon = x;
	
	x = strtol(p=pend+1,&pend,10);
	if (x < 1 || x > 31)
		return clear();
	d = x;
	// Now we have a date, let's get a time.
	if (*pend != ' ' && *pend != 'T')
	{
		h = m = s = 0;
		return verify();
	}
	x = strtol(p=pend+1,&pend,10);
	if (x < 0 || x > 23 || *pend != ':')
		return clear();
	h = x;
	x = strtol(pend+1,&pend,10);
	if (x < 0 || x > 59 || *pend != ':')
		return clear();
	m = x;
	x = strtol(pend+1,&pend,10);
	if (x < 0 || x > 59)
		return clear();
	return verify();
}
Add comparation ops to TStamp, convert it to a class with getters functions etc...
Don't forget to add comments...

See the test bed of this masterpiece (of RAD;):
Code:
int main(int argc, char* argv[])
{
	TStamp	t("2005/06/02 09:35:00");

	cout << t.getString() << endl;
	cout << t.isOK() << endl;
	t = "2005/06/02 10:01:00";
	cout << t.getString() << endl;

	return 0;
}
 
I don't think you need to have both:
Code:
bool parse(const char* pstr);
and
bool parse(const string& str);
since a char* should get promoted to a string anyways. Then you could do all your string parsing with the commands in STL string...
Although I think you'd need to use:
Code:
bool parse(const string str);
 
cpjust,
two parse() members are redundant, but:
1. parse(const string&) is inline function, the body is the same as casting string=>char*, so no any overheads.
2. parse(const char*) is more universal: we may pass char arrays without char*=>string casting.

Never use const string parameters (w/o special requirements): the compiler must build a temporary std::string argument (pass by value!), but this function can't change its value (const!) - const string& reference is better (for single thread apps;)...

Apropos, if we change delimiter test
Code:
*pend != '/'
// to
(*pend != '/' && *pend != '-')
then we can recognize ISO date/time format (for example, 2005-06-03T04:32:00).
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top