1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
#include <iostream>
#include <string>
#include <regex>
#include <cctype>
#include <ctime>
// convert three-character string "sun", "mon" etc. to week day (sunday==0)
int str_to_week_day( std::string str )
{
static constexpr std::size_t NWDAYS = 7 ;
static const std::string wdays[NWDAYS] { "sun", "mon", "tue", "wednes", "thurs", "fri", "satur" } ;
for( char& c : str ) c = std::tolower(c) ; // convert str to all lower case
// http://en.cppreference.com/w/cpp/algorithm/find (1)
return std::find( wdays, wdays+NWDAYS, str ) - wdays ; // return the position in the array
}
// http://en.cppreference.com/w/cpp/chrono/c/tm
// return true if tm holds valid values for day, month, year and day of week
bool valid_tm( const std::tm& tm )
{
auto cpy = tm ;
// this two step process would correct any out of range values which may be present in cpy
// http://en.cppreference.com/w/cpp/chrono/c/mktime
// note: "The values in time are permitted to be outside their normal ranges."
const auto as_time_t = std::mktime( std::addressof(cpy) ) ;
// http://en.cppreference.com/w/cpp/chrono/c/localtime
cpy = *std::localtime( std::addressof(as_time_t) ) ;
return tm.tm_mday == cpy.tm_mday && // valid day
tm.tm_mon == cpy.tm_mon && // valid month
tm.tm_year == cpy.tm_year && // valid year
tm.tm_wday == cpy.tm_wday ; // valid day of week
}
// fill up tm with the parsed values and return true if dt contains a valid date
// in the form "dd/mm/yyyy, day_of_week" eg. "23/10/2017, Monday"
bool valid_date( const std::string& dt, std::tm& tm )
{
tm = {} ; // set all members of tm to zeroes
// http://en.cppreference.com/w/cpp/regex/basic_regex
// ^ - beginning of string
// \s* - zero or more white space
// (\d\d) - two decimal digits (day), captured
// / - literal /
// (\d\d) - two decimal digits (month), captured
// / - literal /
// (\d{4}) - four decimal digits (year), captured
// \, - literal ,
// \s+ - one or more white space
// (Sun|Mon|Tue|Wednes|Thurs|Fri|Satur) - any one of these seven alternatives, captured
// day - literal "day"
// \s* - zero or more white space
// $ - end of string
static const std::regex date_re( R"(^\s*(\d\d)/(\d\d)/(\d{4})\,\s+(Sun|Mon|Tue|Wednes|Thurs|Fri|Satur)day\s*$)",
std::regex::icase ) ; // ignore case (for names of week days)
// http://en.cppreference.com/w/cpp/regex/match_results
std::smatch match ;
// http://en.cppreference.com/w/cpp/regex/regex_match (4)
if( std::regex_match( dt, match, date_re ) ) // if the pattern in the regex is matched
{
// fill up tm with dd, mm, yy and day of week
// http://en.cppreference.com/w/cpp/chrono/c/tm
// http://en.cppreference.com/w/cpp/string/basic_string/stol
tm.tm_mday = std::stoi( match[1] ) ;
tm.tm_mon = std::stoi( match[2] ) - 1 ; // -1 because in struct tm, January == 0
tm.tm_year = std::stoi( match[3] ) - 1900 ; // years since 1900
tm.tm_wday = str_to_week_day( match[4] ) ; // Sunday == 0
tm.tm_hour = 12 ; // noon
return valid_tm(tm) ; // return true if tm holds values within the valid range
}
return false ;
}
int main()
{
const std::string str = " 23/10/2017, MondaY" ;
std::tm tm ;
if( valid_date( str, tm ) )
{
std::cout << "valid date '" << str << "'\n\n"
<< " day of the month: " << tm.tm_mday << '\n'
<< " month (january==1): " << tm.tm_mon + 1 << '\n'
<< " year: " << tm.tm_year + 1900 << '\n'
<< " day of the week (Sunday==0): " << tm.tm_wday << '\n' ;
}
}
|