Skip to content

Instantly share code, notes, and snippets.

@GasparVardanyan
Created May 15, 2026 06:43
Show Gist options
  • Select an option

  • Save GasparVardanyan/e263b5ddaed89419949a5de1e0c7dc24 to your computer and use it in GitHub Desktop.

Select an option

Save GasparVardanyan/e263b5ddaed89419949a5de1e0c7dc24 to your computer and use it in GitHub Desktop.
#include <cctype>
#include <ctime>
#include <stdexcept>
#include <vector>
#include <string>
std::vector<std::string> split(const std::string &s, char delimiter) {
std::vector <std::string> tokens;
# if 0 // stringstream is too much heavy for basic tokenization
std::string token;
std::stringstream ss (s);
while (std::getline(ss, token, delimiter)) {
tokens.push_back(token);
}
# else
std::size_t tBegin = 0;
std::size_t tEnd = 0;
while (std::string::npos != (tEnd = s.find (delimiter, tBegin))) {
tokens.push_back (s.substr (tBegin, tEnd - tBegin));
tBegin = tEnd + 1;
}
if (tBegin < s.size ()) {
tokens.push_back (s.substr (tBegin, s.size ()));
}
# endif
return tokens;
}
/* ------------------------------------------------------------
Trim whitespace from both ends
------------------------------------------------------------ */
std::string trim(const std::string &s) {
if (s.empty()) return "";
std::size_t start = s.find_first_not_of(" \t\r\n");
if (start == std::string::npos) return "";
std::size_t end = s.find_last_not_of(" \t\r\n");
return s.substr(start, end - start + 1);
}
/* ------------------------------------------------------------
C++98-safe integer conversion (replaces stoi)
It's not C++98 safe. C++98 uses C90's atoi implementation and
the C90 standard says:
int atoi(const char *nptr);
The atoi function converts the initial portion of the string
pointed to by nptr to int representation. Except for the behavior
on error, it is equivalent to
(int)strtol(nptr, (char **)NULL, 10)
So in case of error the behaviour is undefined!
Btw this project doesn't compile with C++98. At least C++11 is necessary.
------------------------------------------------------------ */
// NOTE: better to use std::string_view , but since commets talk about C++98
// safety and C++11 is the oldest C++ standard after C++98 where this project
// compiles, I avoid std::string_view which is introduced in C++17
int toInt(const std::string &s) {
# if 0
return atoi(s.c_str());
# endif
try {
return std::stoi (s);
}
catch (...) {
// Can throw:
// - std::invalid_argument if no conversion could be performed.
//
// - std::out_of_range if the converted value would fall out of the range
// of the result type or if the underlying function (std::strtol or
// std::strtoll) sets errno to ERANGE.
return 0;
}
}
/* ------------------------------------------------------------
C++98-safe double conversion (replaces stod)
Not safe. See toInt.
------------------------------------------------------------ */
double toDouble(const std::string &s) {
try {
return std::stod (s);
}
catch (...) {
return 0;
}
}
/* ------------------------------------------------------------
Validate time format HH:MM or --:--
------------------------------------------------------------ */
bool isValidTime(const std::string &t) {
if (t == "--:--") return true;
if (t.size() != 5) return false;
if (t[2] != ':') return false;
if (!std::isdigit(t[0]) || !std::isdigit(t[1])) return false;
if (!std::isdigit(t[3]) || !std::isdigit(t[4])) return false;
int H = (t[0] - '0') * 10 + (t[1] - '0');
int M = (t[3] - '0') * 10 + (t[4] - '0');
// H and M can't be negative
// if (H < 0 || H > 23) return false;
// if (M < 0 || M > 59) return false;
if (H > 23 || M > 59) {
return false;
}
return true;
}
int toMinutes(const std::string &t) {
if (t == "--:--") return 0;
int H = (t[0] - '0') * 10 + (t[1] - '0');
int M = (t[3] - '0') * 10 + (t[4] - '0');
return H * 60 + M;
}
bool dateSortAscending(const std::string &a, const std::string &b) {
// TODO: review
// If we aren't limited with C++11, std::string_view is a better choice here
// (after reimplementing toInt to work with string views)
int d1 = toInt(a.substr(0, 2));
int m1 = toInt(a.substr(3, 2));
int y1 = toInt(a.substr(6, 4));
int d2 = toInt(b.substr(0, 2));
int m2 = toInt(b.substr(3, 2));
int y2 = toInt(b.substr(6, 4));
if (y1 != y2) return y1 < y2;
if (m1 != m2) return m1 < m2;
return d1 < d2;
}
bool isValidWeekday (const std::string &date) {
// date format: DD-MM-YYYY
if (date.length () != 10) {
// TODO: what must be returned in invalid date case?
throw std::runtime_error ("invalid date");
}
int d = toInt(date.substr(0,2));
int m = toInt(date.substr(3,2));
int y = toInt(date.substr(6,4));
if (false == (
1 <= d && d <= 30 &&
1 <= m && m <= 12 &&
1 <= y && y <= 9999 // preserving 0 as an toInt error
)) {
return false;
}
return true;
}
int getWeekday(const std::string &date) {
// date format: DD-MM-YYYY
if (false == isValidWeekday (date)) { // TODO: is this the desired behaviour?
return 0; // return sunday
}
int d = toInt(date.substr(0,2));
int m = toInt(date.substr(3,2));
int y = toInt(date.substr(6,4));
if (false == (
1 <= d && d <= 30 &&
1 <= m && m <= 12 &&
1 <= y && y <= 9999
)) {
return -1;
}
tm t = { 0 };
// std::memset(&t, 0, sizeof(t)); // no need for memset, structs can be initialized with zeros
t.tm_mday = d;
t.tm_mon = m - 1;
t.tm_year = y - 1900;
std::mktime (&t); // normalizes and fills tm_wday
return t.tm_wday; // 0 = Sunday
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment