Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/date #387

Merged
merged 3 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions include/faker-cxx/Date.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ namespace faker
class Date
{
public:
enum class DateFormat {
ISO,
Timestamp,
};

/**
* @brief Generates a random date in the past.
*
Expand All @@ -17,9 +22,11 @@ class Date
* @code
* Date::pastDate() // "2023-12-08T19:31:32Z"
* Date::pastDate(5) // "2020-06-16T15:24:09Z"
* Date::pastDate(5, Date::DateFormat::ISO) // "2020-06-16T15:24:09Z"
* Date::pastDate(5, Date::DateFormat::Timestamp) // "1592321049"
* @endcode
*/
static std::string pastDate(int years = 1);
static std::string pastDate(int years = 1, DateFormat dateFormat = DateFormat::ISO);

/**
* @brief Generates a random date in the future.
Expand All @@ -31,9 +38,11 @@ class Date
* @code
* Date::futureDate() // "2023-09-27T09:47:46Z"
* Date::futureDate(5) // "2024-06-11T19:46:29Z"
* Date::futureDate(5, Date::DateFormat::ISO) // "2024-06-11T19:46:29Z"
* Date::futureDate(5, Date::DateFormat::Timestamp) // "1718229989"
* @endcode
*/
static std::string futureDate(int years = 1);
static std::string futureDate(int years = 1, DateFormat dateFormat = DateFormat::ISO);

/**
* @brief Generates a random date in the recent past.
Expand All @@ -45,9 +54,11 @@ class Date
* @code
* Date::recentDate() // "2023-07-05T14:12:40Z"
* Date::recentDate(10) // "2023-06-29T18:24:12Z"
* Date::recentDate(10, Date::DateFormat::ISO) // "2023-06-29T18:24:12Z"
* Date::recentDate(10, Date::DateFormat::Timestamp) // "1718229989"
* @endcode
*/
static std::string recentDate(int days = 3);
static std::string recentDate(int days = 3, DateFormat dateFormat = DateFormat::ISO);

/**
* @brief Generates a random date in the soon future.
Expand All @@ -59,9 +70,11 @@ class Date
* @code
* Date::soonDate() // "2023-07-07T18:19:12Z"
* Date::soonDate(10) // "2023-07-15T09:59:11Z"
* Date::soonDate(10, Date::DateFormat::ISO) // "2023-07-15T09:59:11Z"
* Date::soonDate(10, Date::DateFormat::Timestamp) // "1718229989"
* @endcode
*/
static std::string soonDate(int days = 3);
static std::string soonDate(int days = 3, DateFormat dateFormat = DateFormat::ISO);

/**
* @brief Generates a random birthdate by age.
Expand All @@ -74,9 +87,11 @@ class Date
* @code
* Date::birthdateByAge() // "2002-12-07T23:20:12Z"
* Date::birthdateByAge(20, 30) // "1996-11-14T11:27:09Z"
* Date::birthdateByAge(20, 30, Date::DateFormat::ISO) // "1996-11-14T11:27:09Z"
* Date::birthdateByAge(20, 30, Date::DateFormat::Timestamp) // "1718229989"
* @endcode
*/
static std::string birthdateByAge(int minAge = 18, int maxAge = 80);
static std::string birthdateByAge(int minAge = 18, int maxAge = 80, DateFormat dateFormat = DateFormat::ISO);

/**
* @brief Generates a random birthdate by year.
Expand All @@ -89,9 +104,11 @@ class Date
* @code
* Date::birthdateByYear() // "1965-02-19T02:19:47Z"
* Date::birthdateByYear(1996, 1996) // "1996-05-19T12:00:23Z"
* Date::birthdateByYear(1996, 1996, Date::DateFormat::ISO) // "1996-05-19T12:00:23Z"
* Date::birthdateByYear(1996, 1996, Date::DateFormat::Timestamp) // "1718229989"
* @endcode
*/
static std::string birthdateByYear(int minYear = 1920, int maxYear = 2000);
static std::string birthdateByYear(int minYear = 1920, int maxYear = 2000, DateFormat dateFormat = DateFormat::ISO);

/**
* @brief Returns a name of random day of the week.
Expand Down
42 changes: 25 additions & 17 deletions src/modules/date/Date.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,87 +14,95 @@ namespace faker
{
namespace
{
std::string serializeTimePoint(const auto& timePoint)
std::string serializeTimePoint(const auto& timePoint, Date::DateFormat dateFormat)
{
time_t timePointTimeT = std::chrono::system_clock::to_time_t(timePoint);

std::tm utcTime = *std::gmtime(&timePointTimeT);

std::stringstream ss;

ss << std::put_time(&utcTime, "%Y-%m-%dT%H:%M:%SZ");
if (dateFormat == Date::DateFormat::Timestamp)
{
ss << std::chrono::duration_cast<std::chrono::seconds>(timePoint.time_since_epoch()).count();
}

else
{
ss << std::put_time(&utcTime, "%Y-%m-%dT%H:%M:%SZ");
}

return ss.str();
}

std::string betweenDate(const auto& from, const auto& to)
std::string betweenDate(const auto& from, const auto& to, Date::DateFormat dateFormat)
{
if (from > to)
{
throw std::runtime_error{fmt::format("Start date is greater than end date. {{from: {}, to: {}}}",
serializeTimePoint(from), serializeTimePoint(to))};
serializeTimePoint(from, dateFormat), serializeTimePoint(to, dateFormat))};
}

const auto size = std::chrono::duration_cast<std::chrono::seconds>(to - from).count();

const auto randomDateWithinRange = from + std::chrono::seconds{Number::integer(size - 1)};

return serializeTimePoint(randomDateWithinRange);
return serializeTimePoint(randomDateWithinRange, dateFormat);
}

const auto numberOfHoursInDay = 24;
const auto numberOfDaysInYear = 365;
}

std::string Date::futureDate(int years)
std::string Date::futureDate(int years, Date::DateFormat dateFormat)
{
const auto startDate = std::chrono::system_clock::now() + std::chrono::hours{1};

const auto endDate = startDate + std::chrono::hours{numberOfHoursInDay * numberOfDaysInYear * years};

return betweenDate(startDate, endDate);
return betweenDate(startDate, endDate, dateFormat);
}

std::string Date::pastDate(int years)
std::string Date::pastDate(int years, Date::DateFormat dateFormat)
{
const auto startDate =
std::chrono::system_clock::now() - std::chrono::hours{numberOfHoursInDay * numberOfDaysInYear * years};

const auto endDate = std::chrono::system_clock::now() - std::chrono::hours{1};

return betweenDate(startDate, endDate);
return betweenDate(startDate, endDate, dateFormat);
}

std::string Date::soonDate(int days)
std::string Date::soonDate(int days, Date::DateFormat dateFormat)
{
const auto startDate = std::chrono::system_clock::now() + std::chrono::hours{1};

const auto endDate = startDate + std::chrono::hours{numberOfHoursInDay * days};

return betweenDate(startDate, endDate);
return betweenDate(startDate, endDate, dateFormat);
}

std::string Date::recentDate(int days)
std::string Date::recentDate(int days, Date::DateFormat dateFormat)
{
const auto startDate = std::chrono::system_clock::now() - std::chrono::hours{numberOfHoursInDay * days};

const auto endDate = std::chrono::system_clock::now() - std::chrono::hours{1};

return betweenDate(startDate, endDate);
return betweenDate(startDate, endDate, dateFormat);
}

std::string Date::birthdateByAge(int minAge, int maxAge)
std::string Date::birthdateByAge(int minAge, int maxAge, Date::DateFormat dateFormat)
{
const auto startDate =
std::chrono::system_clock::now() - std::chrono::hours{numberOfHoursInDay * numberOfDaysInYear * maxAge};

const auto endDate =
std::chrono::system_clock::now() - std::chrono::hours{numberOfHoursInDay * numberOfDaysInYear * minAge};

return betweenDate(startDate, endDate);
return betweenDate(startDate, endDate, dateFormat);
}

std::string Date::birthdateByYear(int minYear, int maxYear)
std::string Date::birthdateByYear(int minYear, int maxYear, Date::DateFormat dateFormat)
{
tm startDateTime{};
startDateTime.tm_year = minYear - 1900;
Expand All @@ -118,7 +126,7 @@ std::string Date::birthdateByYear(int minYear, int maxYear)

const auto endDate = std::chrono::system_clock::from_time_t(mktime(&endDateTime));

return betweenDate(startDate, endDate);
return betweenDate(startDate, endDate, dateFormat);
}

std::string Date::weekdayName()
Expand Down
113 changes: 106 additions & 7 deletions src/modules/date/DateTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class DateTest : public Test
}
};

TEST_F(DateTest, shouldGeneratePastDate)
TEST_F(DateTest, shouldGeneratePastDateISO)
{
const auto currentDate = std::chrono::system_clock::now();

Expand All @@ -56,7 +56,19 @@ TEST_F(DateTest, shouldGeneratePastDate)
EXPECT_TRUE(pastDate < currentDate);
}

TEST_F(DateTest, shouldGenerateRecentDate)
TEST_F(DateTest, shouldGeneratePastDateTimestamp)
{
const auto currentDate = std::chrono::system_clock::now();

const auto pastDateTimestamp = Date::pastDate(1, Date::DateFormat::Timestamp);

const auto pastDate = std::chrono::system_clock::from_time_t(std::stoi(pastDateTimestamp));

EXPECT_TRUE(std::chrono::duration_cast<std::chrono::seconds>(currentDate - pastDate).count() < secondsInYear);
EXPECT_TRUE(pastDate < currentDate);
}

TEST_F(DateTest, shouldGenerateRecentDateISO)
{
const auto currentDate = std::chrono::system_clock::now();

Expand All @@ -70,7 +82,21 @@ TEST_F(DateTest, shouldGenerateRecentDate)
EXPECT_TRUE(recentDate < currentDate);
}

TEST_F(DateTest, shouldGenerateFutureDate)
TEST_F(DateTest, shouldGenerateRecentDateTimestamp)
{
const auto currentDate = std::chrono::system_clock::now();

const auto recentDays = 5;

const auto recentDateTimestamp = Date::recentDate(recentDays, Date::DateFormat::Timestamp);

const auto recentDate = std::chrono::system_clock::from_time_t(std::stoi(recentDateTimestamp));

EXPECT_TRUE(std::chrono::duration_cast<std::chrono::seconds>(currentDate - recentDate).count() < secondsInYear);
EXPECT_TRUE(recentDate < currentDate);
}

TEST_F(DateTest, shouldGenerateFutureDateISO)
{
const auto currentDate = std::chrono::system_clock::now();

Expand All @@ -82,7 +108,19 @@ TEST_F(DateTest, shouldGenerateFutureDate)
EXPECT_TRUE(futureDate > currentDate);
}

TEST_F(DateTest, shouldGenerateSoonDate)
TEST_F(DateTest, shouldGenerateFutureDateTimestamp)
{
const auto currentDate = std::chrono::system_clock::now();

const auto futureDateTimestamp = Date::futureDate(1, Date::DateFormat::Timestamp);

const auto futureDate = std::chrono::system_clock::from_time_t(std::stoi(futureDateTimestamp));

EXPECT_TRUE(std::chrono::duration_cast<std::chrono::seconds>(futureDate - currentDate).count() < secondsInYear);
EXPECT_TRUE(futureDate > currentDate);
}

TEST_F(DateTest, shouldGenerateSoonDateISO)
{
const auto currentDate = std::chrono::system_clock::now();

Expand All @@ -96,7 +134,21 @@ TEST_F(DateTest, shouldGenerateSoonDate)
EXPECT_TRUE(soonDate > currentDate);
}

TEST_F(DateTest, shouldGenerateBirthDateByAge)
TEST_F(DateTest, shouldGenerateSoonDateTimestamp)
{
const auto currentDate = std::chrono::system_clock::now();

const auto soonDays = 2;

const auto soonDateTimestamp = Date::soonDate(soonDays, Date::DateFormat::Timestamp);

const auto soonDate = std::chrono::system_clock::from_time_t(std::stoi(soonDateTimestamp));

EXPECT_TRUE(std::chrono::duration_cast<std::chrono::seconds>(soonDate - currentDate).count() < secondsInYear);
EXPECT_TRUE(soonDate > currentDate);
}

TEST_F(DateTest, shouldGenerateBirthDateByAgeISO)
{
const auto birthdateISO = Date::birthdateByAge(5, 15);

Expand All @@ -112,7 +164,23 @@ TEST_F(DateTest, shouldGenerateBirthDateByAge)
EXPECT_TRUE(birthdate < expectedEndDate);
}

TEST_F(DateTest, shouldGenerateBirthDateByExactYear)
TEST_F(DateTest, shouldGenerateBirthDateByAgeTimestamp)
{
const auto birthdateTimestamp = Date::birthdateByAge(5, 15, Date::DateFormat::Timestamp);

const auto birthdate = std::chrono::system_clock::from_time_t(std::stoi(birthdateTimestamp));

const auto expectedStartDate =
std::chrono::system_clock::now() - std::chrono::hours{numberOfHoursInDay * numberOfDaysInYear * 15};

const auto expectedEndDate =
std::chrono::system_clock::now() - std::chrono::hours{numberOfHoursInDay * numberOfDaysInYear * 5};

EXPECT_TRUE(birthdate > expectedStartDate);
EXPECT_TRUE(birthdate < expectedEndDate);
}

TEST_F(DateTest, shouldGenerateBirthDateByExactYearISO)
{
const auto birthdateISO = Date::birthdateByYear(1996, 1996);

Expand All @@ -121,7 +189,22 @@ TEST_F(DateTest, shouldGenerateBirthDateByExactYear)
EXPECT_EQ(birthdate.tm_year + 1900, 1996);
}

TEST_F(DateTest, shouldGenerateBirthDateByRangeYear)
TEST_F(DateTest, shouldGenerateBirthDateByExactYearTimestamp)
{
const auto birthdateTimestamp = Date::birthdateByYear(1996, 1996, Date::DateFormat::Timestamp);

const auto birthdate = std::chrono::system_clock::from_time_t(std::stoi(birthdateTimestamp));

// Convert std::chrono::system_clock::time_point to std::time_t
std::time_t birthdateTimeT = std::chrono::system_clock::to_time_t(birthdate);

// Convert std::time_t to std::tm
std::tm birthdateStruct = *std::localtime(&birthdateTimeT);

EXPECT_EQ(birthdateStruct.tm_year + 1900, 1996);
}

TEST_F(DateTest, shouldGenerateBirthDateByRangeYearISO)
{
const auto birthdateISO = Date::birthdateByYear(1990, 2000);

Expand All @@ -131,6 +214,22 @@ TEST_F(DateTest, shouldGenerateBirthDateByRangeYear)
EXPECT_LE(birthdate.tm_year + 1900, 2000);
}

TEST_F(DateTest, shouldGenerateBirthDateByRangeYearTimestamp)
{
const auto birthdateTimestamp = Date::birthdateByYear(1990, 2000, Date::DateFormat::Timestamp);

const auto birthdate = std::chrono::system_clock::from_time_t(std::stoi(birthdateTimestamp));

// Convert std::chrono::system_clock::time_point to std::time_t
std::time_t birthdateTimeT = std::chrono::system_clock::to_time_t(birthdate);

// Convert std::time_t to std::tm
std::tm birthdateStruct = *std::localtime(&birthdateTimeT);

EXPECT_GE(birthdateStruct.tm_year + 1900, 1990);
EXPECT_LE(birthdateStruct.tm_year + 1900, 2000);
}

TEST_F(DateTest, shouldGenerateWeekdayName)
{
const auto generatedWeekdayName = Date::weekdayName();
Expand Down