I'm working on an application that will make extensive use of dates, there will be various forms of input such as a GUI or text files and the data will be stored in a MySQL database.
The planned use for this class is in Models, Model View and Model to Database interface classes that require dates. One of the models will have at least 6 dates involved. Almost all of the models will have at least one date (creation date of the record in the database).
The class is pretty basic, it needs to be able to convert dates to multiple formats.
I searched for existing libraries before implementing this, that is when I discovered std::put_time and std::get_time. If you are aware of any libraries that provide this functionality, please comment as well.
Development Environment:
Ubuntu 22.04
GCC 12
CMake 3.31
C++23 (should work in previous version, since C++17).
The Code:
DateData.h
#ifndef DATEDATA_H_
#define DATEDATA_H_
/*
* Contains Date data as year, month and day. Dates are stored as actual dates
* and not as an offset from January 1, 1900.
* Provides various formats for input and output. The primary form of output is
* a MySQL date format. Currently only supports USA English formating of dates
* for both input and output.
*/
#include <ctime>
#include <string>
class DateData
{
public:
DateData() : year{0}, month{0}, day{0} {};
DateData(unsigned int yearIn, unsigned int monthIn, unsigned int dayIn)
: year{yearIn}, month{monthIn}, day{dayIn} {};
DateData(std::string dateString);
DateData(std::time_t currentTime);
~DateData() = default;
std::string getDBDateValue() { return USEnglishDateUsingFormat("%Y-%Om-%Od"); };
std::string getFormalUSEnglishDate() { return USEnglishDateUsingFormat("%B %d, %Y"); };
std::string getUsEnglishDateWithSlashes() { return USEnglishDateUsingFormat("%m/%d/%Y"); };
std::string getUsEnglishDateWithDashes() { return USEnglishDateUsingFormat("%m-%d-%Y"); };
private:
void translateFromStdTM(std::tm someDate);
std::tm translateToStdTM();
std::string USEnglishDateUsingFormat(const char* format);
unsigned int year;
unsigned int month;
unsigned int day;
};
#endif // DATEDATA_H_
DateData.cpp
#include "DateData.h"
#include <ctime>
#include <iomanip>
#include <iostream>
#include <locale>
#include <sstream>
#include <vector>
DateData::DateData(std::string dateString)
: year{0}, month{0}, day{0}
{
std::locale usEnglish("en_US.UTF-8");
std::vector<std::string> legalFormats = {
{"%B %d, %Y"},
{"%m/%d/%Y"},
{"%m-%d-%Y"},
{"%Y-%m-%d"}
};
for (auto legalFormat: legalFormats)
{
std::tm dateFromString{};
std::istringstream ss(dateString);
ss.imbue(usEnglish);
ss >> std::get_time(&dateFromString, legalFormat.c_str());
if (!ss.fail())
{
translateFromStdTM(dateFromString);
return;
}
}
}
DateData::DateData(std::time_t currentTime)
{
std::tm* now = std::localtime(¤tTime);
translateFromStdTM(*now);
}
void DateData::translateFromStdTM(std::tm someDate)
{
year = someDate.tm_year + 1900;
month = someDate.tm_mon + 1;
day = someDate.tm_mday;
}
std::tm DateData::translateToStdTM()
{
std::tm LinuxDate{};
LinuxDate.tm_year = year - 1900;
LinuxDate.tm_mon = month -1;
LinuxDate.tm_mday = day;
return LinuxDate;
}
std::string DateData::USEnglishDateUsingFormat(const char *format)
{
std::locale usEnglish("en_US.UTF-8");
std::tm dateConverter = translateToStdTM();
std::stringstream ss;
ss.imbue(usEnglish);
ss << std::put_time(&dateConverter, format);
return ss.str();
}
main.cpp
#include "DateData.h"
#include <iostream>
#include <string>
#include <vector>
void outputTest(DateData& test)
{
std::cout << "\tTo database format:\t" << test.getDBDateValue() << "\n";
std::cout << "\tTo Formal US English format:\t" << test.getFormalUSEnglishDate() << "\n";
std::cout << "\tTo US with Slashes format:\t" << test.getUsEnglishDateWithSlashes() << "\n";
std::cout << "\tTo US with Dashes format:\t" << test.getUsEnglishDateWithDashes() << "\n\n";
}
void testInputAndOutput(std::string &input)
{
DateData test(input);
std::cout << "testing " << input << ":\n";
outputTest(test);
}
void testInputAndOutput(unsigned int year, unsigned int month, unsigned int day)
{
DateData test(year, month, day);
std::cout << "testing: year = " << year << " month = " << month << " day = " << day << ":\n";
outputTest(test);
}
int main()
{
std::vector<std::string> testStrings = {
"May 13, 2025",
"5/13/2025",
"5-13-2025",
"2025-4-5"
};
for (auto testString: testStrings)
{
testInputAndOutput(testString);
}
testInputAndOutput(1995, 5, 13);
testInputAndOutput(1977, 7, 7);
std::time_t currentTime = std::time(0);
DateData today(currentTime);
std::cout << "Testing the TM interface:\n";
outputTest(today);
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
project(TestDateData LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
add_executable(TestDateData
main.cpp
DateData.h DateData.cpp
)
date. \$\endgroup\$intvalues". Handy if you needdatediff()ordayofweek()things... \$\endgroup\$