diff --git a/include/faker-cxx/Date.h b/include/faker-cxx/Date.h index 44887f6c6..297230c33 100644 --- a/include/faker-cxx/Date.h +++ b/include/faker-cxx/Date.h @@ -7,6 +7,11 @@ namespace faker class Date { public: + enum class DateFormat { + ISO, + Timestamp, + }; + /** * @brief Generates a random date in the past. * @@ -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. @@ -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. @@ -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. @@ -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. @@ -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. @@ -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. diff --git a/src/modules/date/Date.cpp b/src/modules/date/Date.cpp index 5f49d1937..3637c2dd7 100644 --- a/src/modules/date/Date.cpp +++ b/src/modules/date/Date.cpp @@ -14,7 +14,7 @@ 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); @@ -22,68 +22,76 @@ std::string serializeTimePoint(const auto& timePoint) std::stringstream ss; - ss << std::put_time(&utcTime, "%Y-%m-%dT%H:%M:%SZ"); + if (dateFormat == Date::DateFormat::Timestamp) + { + ss << std::chrono::duration_cast(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(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}; @@ -91,10 +99,10 @@ std::string Date::birthdateByAge(int minAge, int 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; @@ -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() diff --git a/src/modules/date/DateTest.cpp b/src/modules/date/DateTest.cpp index 5f3b68630..4aef97e87 100644 --- a/src/modules/date/DateTest.cpp +++ b/src/modules/date/DateTest.cpp @@ -44,7 +44,7 @@ class DateTest : public Test } }; -TEST_F(DateTest, shouldGeneratePastDate) +TEST_F(DateTest, shouldGeneratePastDateISO) { const auto currentDate = std::chrono::system_clock::now(); @@ -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(currentDate - pastDate).count() < secondsInYear); + EXPECT_TRUE(pastDate < currentDate); +} + +TEST_F(DateTest, shouldGenerateRecentDateISO) { const auto currentDate = std::chrono::system_clock::now(); @@ -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(currentDate - recentDate).count() < secondsInYear); + EXPECT_TRUE(recentDate < currentDate); +} + +TEST_F(DateTest, shouldGenerateFutureDateISO) { const auto currentDate = std::chrono::system_clock::now(); @@ -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(futureDate - currentDate).count() < secondsInYear); + EXPECT_TRUE(futureDate > currentDate); +} + +TEST_F(DateTest, shouldGenerateSoonDateISO) { const auto currentDate = std::chrono::system_clock::now(); @@ -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(soonDate - currentDate).count() < secondsInYear); + EXPECT_TRUE(soonDate > currentDate); +} + +TEST_F(DateTest, shouldGenerateBirthDateByAgeISO) { const auto birthdateISO = Date::birthdateByAge(5, 15); @@ -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); @@ -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); @@ -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();