Skip to content

Commit

Permalink
fix: toString in DateTime and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Laateef committed Feb 23, 2024
1 parent 66c58af commit ccc20e0
Show file tree
Hide file tree
Showing 7 changed files with 302 additions and 40 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get install -y lcov
lcov --capture --directory . --output-file=coverage.info
lcov --extract coverage.info '*/Xclox/include/*' '*/Xclox/test/ntp/tools/*' --output-file coverage.txt
lcov --capture --directory . --output-file=coverage.info \
--include '*/Xclox/include/*' \
--include '*/Xclox/test/ntp/tools/*' \
--exclude '*/Xclox/test/ntp/tools/helper.hpp'
- name: Upload Coverage to Codecov
if: matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt
file: ./coverage.info
verbose: true

deploy:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ It has been thoroughly tested, as its development has been test-driven (TDD).

## Usage

The following example shows only the basic functionalities of the library. For further details, please see the documentation.
The following example shows only the basic functionalities of the library. For further details, please see the [documentation](https://laateef.github.io/Xclox/index.html).

### Example

Expand Down
79 changes: 63 additions & 16 deletions include/xclox/datetime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,16 @@ class DateTime {
/// Move-constructs a DateTime object from \p other.
DateTime(DateTime&& other) = default;

// const auto& microseconds = std::chrono::duration_cast<std::chrono::microseconds>(timePoint.time_since_epoch() % std::chrono::seconds(1));
// const auto& floatingSecond = std::chrono::seconds(timePoint.time_since_epoch().count() < 0 && microseconds.count() != 0 ? 1 : 0);
// const auto t {std::chrono::system_clock::to_time_t(timePoint - floatingSecond)};
// stream << std::put_time(std::gmtime(&t), "%F %T") << "." << std::setfill('0') << std::setw(6) << (floatingSecond + microseconds).count();

/**
* Constructs a DateTime object from \p duration since the epoch "1970-01-01 00:00:00 UTC".
* The constructed datetime has whatever precision it is given, down to nanoseconds.
*/
explicit DateTime(const Duration& duration)
// : m_date(std::chrono::duration_cast<Days>(duration))
// , m_time(duration % Days(1))
{
const auto& subday = duration % Days(1);
const auto& floatingDay = Days(duration.count() < 0 && subday.count() != 0 ? 1 : 0);
const auto& subDay = duration % Days(1);
const auto& floatingDay = Days(duration.count() < 0 && subDay.count() != 0 ? 1 : 0);
m_date = Date(std::chrono::duration_cast<Days>(duration - floatingDay));
m_time = Time(subday + floatingDay);
m_time = Time(subDay + floatingDay);
}

/// Constructs a DateTime object from the standard library's chrono time point, \p timePoint.
Expand Down Expand Up @@ -622,7 +615,7 @@ class DateTime {
* ----------- | -----------------------------------------------------
* # | era of year as a positive sign(+) or negative sign(-)
* E | era of year as CE or BCE
* y | year as one digit or more (1, 9999)
* y | year as one digit or more (1, 9999+)
* yy | year of era as two digits (00, 99)
* yyyy | year as four digits (0000, 9999)
* M | month of year as one digit or more (1, 12)
Expand Down Expand Up @@ -657,12 +650,17 @@ class DateTime {
* If the datetime is invalid, an empty string will be returned.
* @see dayOfWeekName(), monthName()
*/
std::string toString(const std::string& format) const
std::string toString(const std::string& format = "yyyy-MM-dd hh:mm:ss") const
{
if (!isValid())
if (!isValid() || format.empty())
return std::string();

return m_date.toString(m_time.toString(format));
std::stringstream output;
for (size_t pos = 0; pos < format.size(); ++pos) {
const auto count = internal::countIdenticalCharsFrom(pos, format);
output << (internal::isPattern(format.at(pos), count) ? stringify(format.at(pos), count) : format.substr(pos, count));
pos += count - 1;
}
return output.str();
}

/// @}
Expand All @@ -673,7 +671,7 @@ class DateTime {
*/
static DateTime current()
{
return DateTime(Date::current(), Time::current());
return DateTime(std::chrono::system_clock::now());
}

/// Returns a DateTime object set to the epoch "1970-1-1T00:00:00".
Expand Down Expand Up @@ -848,6 +846,55 @@ class DateTime {
/// @}

private:
std::string stringify(char flag, size_t count) const
{
std::stringstream output;
output << std::setfill('0') << std::setw(count);
if (flag == '#') {
output << (year() < 0 ? "-" : "+");
} else if (flag == 'E') {
output << (year() < 0 ? "BCE" : "CE");
} else if (flag == 'y') {
const int y = std::abs(year());
if (count == 1) {
output << y;
} else if (count == 2) {
output << y - (y / 100 * 100);
} else if (count == 4) {
output << y - (y / 10000 * 10000);
}
} else if (flag == 'M') {
if (count == 1 || count == 2) {
output << month();
} else if (count == 3) {
output << internal::getShortMonthName(month());
} else if (count == 4) {
output << internal::getLongMonthName(month());
}
} else if (flag == 'd') {
if (count == 1 || count == 2) {
output << day();
} else if (count == 3) {
output << internal::getShortWeekdayName(dayOfWeek());
} else if (count == 4) {
output << internal::getLongWeekdayName(dayOfWeek());
}
} else if (flag == 'h') {
output << hour();
} else if (flag == 'm') {
output << minute();
} else if (flag == 's') {
output << second();
} else if (flag == 'f') {
output << nanosecond() / static_cast<long>(std::pow(10, 9 - count));
} else if (flag == 'a') {
output << (hour() < 12 ? "am" : "pm");
} else if (flag == 'A') {
output << (hour() < 12 ? "AM" : "PM");
}
return output.str();
}

Date m_date;
Time m_time;
};
Expand Down
32 changes: 26 additions & 6 deletions include/xclox/internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
#ifndef XCLOX_INTERNAL_HPP
#define XCLOX_INTERNAL_HPP

#include <algorithm>
#include <array>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <array>
#include <algorithm>
#include <unordered_map>

namespace xclox {

Expand All @@ -39,7 +40,7 @@ namespace internal {
return std::stoi(intStr);
}

inline std::string getShortWeekdayName(int index)
inline std::string getShortWeekdayName(int day)
{
static const std::string weekdayNameArray[] = {
"Mon",
Expand All @@ -50,10 +51,10 @@ namespace internal {
"Sat",
"Sun"
};
return weekdayNameArray[index - 1];
return weekdayNameArray[day - 1];
}

inline std::string getLongWeekdayName(int index)
inline std::string getLongWeekdayName(int day)
{
static const std::string weekdayNameArray[] = {
"Monday",
Expand All @@ -64,7 +65,7 @@ namespace internal {
"Saturday",
"Sunday"
};
return weekdayNameArray[index - 1];
return weekdayNameArray[day - 1];
}

inline const std::array<std::string, 12>& getShortMonthNameArray()
Expand Down Expand Up @@ -125,6 +126,25 @@ namespace internal {
return static_cast<int>(std::distance(getLongMonthNameArray().cbegin(), std::find(getLongMonthNameArray().cbegin(), getLongMonthNameArray().cend(), month)) + 1);
}

inline bool isPattern(char flag, size_t count)
{
static const std::unordered_map<char, size_t> patternMap {
{ '#', 1 },
{ 'E', 1 },
{ 'y', (1 | 1 << 1 | 1 << 3) },
{ 'M', (1 | 1 << 1 | 1 << 2 | 1 << 3) },
{ 'd', (1 | 1 << 1 | 1 << 2 | 1 << 3) },
{ 'h', (1 | 1 << 1) },
{ 'm', (1 | 1 << 1) },
{ 's', (1 | 1 << 1) },
{ 'f', (1 | 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8) },
{ 'a', 1 },
{ 'A', 1 }
};
const auto iter = patternMap.find(flag);
return iter != patternMap.cend() && iter->second & (1 << count - 1);
}

} // namespace internal

} // namespace xclox
Expand Down
2 changes: 2 additions & 0 deletions test/date.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ TEST_SUITE("Date")
}
SUBCASE("days in month")
{
CHECK(Date::daysInMonthOfYear(1, 0) == -1);
CHECK(Date::daysInMonthOfYear(1, 32) == -1);
CHECK(Date::daysInMonthOfYear(1970, 1) == 31);
CHECK(Date(1970, 1, 1).daysInMonth() == 31);
CHECK(Date(1970, 2, 1).daysInMonth() == 28);
Expand Down
Loading

0 comments on commit ccc20e0

Please sign in to comment.