Define subtraction and addition for temporal types (#209)
This commit is contained in:
parent
5abcb5081d
commit
086fc47769
@ -3,6 +3,7 @@
|
||||
#include <charconv>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
@ -717,6 +718,11 @@ int64_t Duration::Months() const {
|
||||
return std::chrono::duration_cast<std::chrono::months>(ms).count();
|
||||
}
|
||||
|
||||
int64_t Duration::Days() const {
|
||||
std::chrono::microseconds ms(microseconds);
|
||||
return std::chrono::duration_cast<std::chrono::days>(ms).count();
|
||||
}
|
||||
|
||||
int64_t Duration::SubMonthsAsDays() const {
|
||||
namespace chrono = std::chrono;
|
||||
const auto months = chrono::months(Months());
|
||||
@ -732,6 +738,14 @@ int64_t Duration::SubDaysAsSeconds() const {
|
||||
return chrono::duration_cast<chrono::seconds>(micros - months - days).count();
|
||||
}
|
||||
|
||||
int64_t Duration::SubDaysAsMicroseconds() const {
|
||||
namespace chrono = std::chrono;
|
||||
const auto months = chrono::months(Months());
|
||||
const auto days = chrono::days(SubMonthsAsDays());
|
||||
const auto micros = chrono::microseconds(microseconds);
|
||||
return (micros - months - days).count();
|
||||
}
|
||||
|
||||
int64_t Duration::SubSecondsAsNanoseconds() const {
|
||||
namespace chrono = std::chrono;
|
||||
const auto months = chrono::months(Months());
|
||||
@ -742,6 +756,9 @@ int64_t Duration::SubSecondsAsNanoseconds() const {
|
||||
}
|
||||
|
||||
Duration Duration::operator-() const {
|
||||
if (microseconds == std::numeric_limits<decltype(microseconds)>::min()) [[unlikely]] {
|
||||
throw utils::BasicException("Duration arithmetic overflows");
|
||||
}
|
||||
Duration result{-microseconds};
|
||||
return result;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "utils/exceptions.hpp"
|
||||
@ -24,6 +24,88 @@ constexpr auto GetAndSubtractDuration(TSecond &base_duration) {
|
||||
return duration.count();
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
bool Overflows(const TType &lhs, const TType &rhs) {
|
||||
if (lhs > 0 && rhs > 0 && lhs > (std::numeric_limits<TType>::max() - rhs)) [[unlikely]] {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TType>
|
||||
bool Underflows(const TType &lhs, const TType &rhs) {
|
||||
if (lhs < 0 && rhs < 0 && lhs < (std::numeric_limits<TType>::min() - rhs)) [[unlikely]] {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct DurationParameters {
|
||||
double years{0};
|
||||
double months{0};
|
||||
double days{0};
|
||||
double hours{0};
|
||||
double minutes{0};
|
||||
double seconds{0};
|
||||
double milliseconds{0};
|
||||
double microseconds{0};
|
||||
};
|
||||
|
||||
DurationParameters ParseDurationParameters(std::string_view string);
|
||||
|
||||
struct Date;
|
||||
struct LocalTime;
|
||||
struct LocalDateTime;
|
||||
|
||||
struct Duration {
|
||||
explicit Duration(int64_t microseconds);
|
||||
explicit Duration(const DurationParameters ¶meters);
|
||||
|
||||
auto operator<=>(const Duration &) const = default;
|
||||
|
||||
int64_t Months() const;
|
||||
int64_t Days() const;
|
||||
int64_t SubMonthsAsDays() const;
|
||||
int64_t SubDaysAsSeconds() const;
|
||||
int64_t SubDaysAsMicroseconds() const;
|
||||
int64_t SubSecondsAsNanoseconds() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const Duration &dur) {
|
||||
// ISO 8601 extended format: P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss].
|
||||
namespace chrono = std::chrono;
|
||||
auto micros = chrono::microseconds(dur.microseconds);
|
||||
const auto y = GetAndSubtractDuration<chrono::years>(micros);
|
||||
const auto mo = GetAndSubtractDuration<chrono::months>(micros);
|
||||
const auto dd = GetAndSubtractDuration<chrono::days>(micros);
|
||||
const auto h = GetAndSubtractDuration<chrono::hours>(micros);
|
||||
const auto m = GetAndSubtractDuration<chrono::minutes>(micros);
|
||||
const auto s = GetAndSubtractDuration<chrono::seconds>(micros);
|
||||
return os << fmt::format("P{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}.{:0>6}", y, mo, dd, h, m, s, micros.count());
|
||||
}
|
||||
|
||||
Duration operator-() const;
|
||||
|
||||
friend Duration operator+(const Duration &lhs, const Duration rhs) {
|
||||
if (Overflows(lhs.microseconds, rhs.microseconds)) {
|
||||
throw utils::BasicException("Duration arithmetic overflows");
|
||||
}
|
||||
|
||||
if (Underflows(lhs.microseconds, rhs.microseconds)) {
|
||||
throw utils::BasicException("Duration arithmetic underflows");
|
||||
}
|
||||
|
||||
return Duration(lhs.microseconds + rhs.microseconds);
|
||||
}
|
||||
|
||||
friend Duration operator-(const Duration &lhs, const Duration rhs) { return lhs + (-rhs); }
|
||||
|
||||
int64_t microseconds;
|
||||
};
|
||||
|
||||
struct DurationHash {
|
||||
size_t operator()(const Duration &duration) const;
|
||||
};
|
||||
|
||||
struct DateParameters {
|
||||
int64_t years{0};
|
||||
int64_t months{1};
|
||||
@ -35,6 +117,19 @@ struct DateParameters {
|
||||
// boolean indicates whether the parsed string was in extended format
|
||||
std::pair<DateParameters, bool> ParseDateParameters(std::string_view date_string);
|
||||
|
||||
constexpr std::chrono::year_month_day ToChronoYMD(uint16_t years, uint8_t months, uint8_t days) {
|
||||
namespace chrono = std::chrono;
|
||||
return chrono::year_month_day(chrono::year(years), chrono::month(months), chrono::day(days));
|
||||
}
|
||||
|
||||
constexpr std::chrono::sys_days ToChronoSysDaysYMD(uint16_t years, uint8_t months, uint8_t days) {
|
||||
return std::chrono::sys_days(ToChronoYMD(years, months, days));
|
||||
}
|
||||
|
||||
constexpr std::chrono::days DaysSinceEpoch(uint16_t years, uint8_t months, uint8_t days) {
|
||||
return ToChronoSysDaysYMD(years, months, days).time_since_epoch();
|
||||
}
|
||||
|
||||
struct Date {
|
||||
explicit Date() : Date{DateParameters{}} {}
|
||||
// we assume we accepted date in microseconds which was normilized using the epoch time point
|
||||
@ -49,6 +144,26 @@ struct Date {
|
||||
int64_t MicrosecondsSinceEpoch() const;
|
||||
int64_t DaysSinceEpoch() const;
|
||||
|
||||
friend Date operator+(const Date &date, const Duration &dur) {
|
||||
namespace chrono = std::chrono;
|
||||
const auto date_as_duration = Duration(date.MicrosecondsSinceEpoch());
|
||||
const auto result = date_as_duration + dur;
|
||||
const auto ymd = chrono::year_month_day(chrono::sys_days(chrono::days(result.Days())));
|
||||
return Date({static_cast<int>(ymd.year()), static_cast<unsigned>(ymd.month()), static_cast<unsigned>(ymd.day())});
|
||||
}
|
||||
|
||||
friend Date operator+(const Duration &dur, const Date &date) { return date + dur; }
|
||||
|
||||
friend Date operator-(const Date &date, const Duration &dur) { return date + (-dur); }
|
||||
|
||||
friend Duration operator-(const Date &lhs, const Date &rhs) {
|
||||
namespace chrono = std::chrono;
|
||||
const auto lhs_days = utils::DaysSinceEpoch(lhs.years, lhs.months, lhs.days);
|
||||
const auto rhs_days = utils::DaysSinceEpoch(rhs.years, rhs.months, rhs.days);
|
||||
const auto days_elapsed = lhs_days - rhs_days;
|
||||
return Duration(chrono::duration_cast<chrono::microseconds>(days_elapsed).count());
|
||||
}
|
||||
|
||||
auto operator<=>(const Date &) const = default;
|
||||
|
||||
uint16_t years;
|
||||
@ -95,6 +210,34 @@ struct LocalTime {
|
||||
static_cast<int>(lt.seconds), subseconds.count());
|
||||
}
|
||||
|
||||
friend LocalTime operator+(const LocalTime &local_time, const Duration &dur) {
|
||||
namespace chrono = std::chrono;
|
||||
auto rhs = dur.SubDaysAsMicroseconds();
|
||||
auto abs = [](auto value) { return (value >= 0) ? value : -value; };
|
||||
const auto lhs = local_time.MicrosecondsSinceEpoch();
|
||||
if (rhs < 0 && lhs < abs(rhs)) {
|
||||
constexpr int64_t one_day_in_microseconds = 24LL * 60 * 60 * 1000 * 1000;
|
||||
rhs = one_day_in_microseconds + rhs;
|
||||
}
|
||||
auto result = chrono::microseconds(lhs + rhs);
|
||||
const auto h = GetAndSubtractDuration<chrono::hours>(result) % 24;
|
||||
const auto m = GetAndSubtractDuration<chrono::minutes>(result);
|
||||
const auto s = GetAndSubtractDuration<chrono::seconds>(result);
|
||||
const auto milli = GetAndSubtractDuration<chrono::milliseconds>(result);
|
||||
const auto micro = result.count();
|
||||
return LocalTime(LocalTimeParameters{h, m, s, milli, micro});
|
||||
}
|
||||
|
||||
friend LocalTime operator+(const Duration &dur, const LocalTime &local_time) { return local_time + dur; }
|
||||
|
||||
friend LocalTime operator-(const LocalTime &local_time, const Duration &duration) { return local_time + (-duration); }
|
||||
|
||||
friend Duration operator-(const LocalTime &lhs, const LocalTime &rhs) {
|
||||
Duration lhs_dur(lhs.MicrosecondsSinceEpoch());
|
||||
Duration rhs_dur(rhs.MicrosecondsSinceEpoch());
|
||||
return lhs_dur - rhs_dur;
|
||||
}
|
||||
|
||||
uint8_t hours;
|
||||
uint8_t minutes;
|
||||
uint8_t seconds;
|
||||
@ -124,6 +267,25 @@ struct LocalDateTime {
|
||||
return os;
|
||||
}
|
||||
|
||||
friend LocalDateTime operator+(const LocalDateTime &dt, const Duration &dur) {
|
||||
const auto local_date_time_as_duration = Duration(dt.MicrosecondsSinceEpoch());
|
||||
const auto result = local_date_time_as_duration + dur;
|
||||
namespace chrono = std::chrono;
|
||||
const auto ymd = chrono::year_month_day(chrono::sys_days(chrono::days(result.Days())));
|
||||
const auto date_part =
|
||||
Date({static_cast<int>(ymd.year()), static_cast<unsigned>(ymd.month()), static_cast<unsigned>(ymd.day())});
|
||||
const auto local_time_part = LocalTime(result.SubDaysAsMicroseconds());
|
||||
return LocalDateTime(date_part, local_time_part);
|
||||
}
|
||||
|
||||
friend LocalDateTime operator+(const Duration &dur, const LocalDateTime &dt) { return dt + dur; }
|
||||
|
||||
friend LocalDateTime operator-(const LocalDateTime &dt, const Duration &dur) { return dt + (-dur); }
|
||||
|
||||
friend Duration operator-(const LocalDateTime &lhs, const LocalDateTime &rhs) {
|
||||
return Duration(lhs.MicrosecondsSinceEpoch()) - Duration(rhs.MicrosecondsSinceEpoch());
|
||||
}
|
||||
|
||||
Date date;
|
||||
LocalTime local_time;
|
||||
};
|
||||
@ -132,58 +294,6 @@ struct LocalDateTimeHash {
|
||||
size_t operator()(const LocalDateTime &local_date_time) const;
|
||||
};
|
||||
|
||||
struct DurationParameters {
|
||||
double years{0};
|
||||
double months{0};
|
||||
double days{0};
|
||||
double hours{0};
|
||||
double minutes{0};
|
||||
double seconds{0};
|
||||
double milliseconds{0};
|
||||
double microseconds{0};
|
||||
};
|
||||
|
||||
DurationParameters ParseDurationParameters(std::string_view string);
|
||||
|
||||
struct Duration {
|
||||
explicit Duration(int64_t microseconds);
|
||||
explicit Duration(const DurationParameters ¶meters);
|
||||
|
||||
auto operator<=>(const Duration &) const = default;
|
||||
|
||||
int64_t Months() const;
|
||||
int64_t SubMonthsAsDays() const;
|
||||
int64_t SubDaysAsSeconds() const;
|
||||
int64_t SubSecondsAsNanoseconds() const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const Duration &dur) {
|
||||
// ISO 8601 extended format: P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss].
|
||||
namespace chrono = std::chrono;
|
||||
auto micros = chrono::microseconds(dur.microseconds);
|
||||
const auto y = GetAndSubtractDuration<chrono::years>(micros);
|
||||
const auto mo = GetAndSubtractDuration<chrono::months>(micros);
|
||||
const auto dd = GetAndSubtractDuration<chrono::days>(micros);
|
||||
const auto h = GetAndSubtractDuration<chrono::hours>(micros);
|
||||
const auto m = GetAndSubtractDuration<chrono::minutes>(micros);
|
||||
const auto s = GetAndSubtractDuration<chrono::seconds>(micros);
|
||||
return os << fmt::format("P{:0>4}-{:0>2}-{:0>2}T{:0>2}:{:0>2}:{:0>2}.{:0>6}", y, mo, dd, h, m, s, micros.count());
|
||||
}
|
||||
|
||||
Duration operator-() const;
|
||||
|
||||
int64_t microseconds;
|
||||
};
|
||||
|
||||
struct DurationHash {
|
||||
size_t operator()(const Duration &duration) const;
|
||||
};
|
||||
|
||||
constexpr std::chrono::days DaysSinceEpoch(uint16_t years, uint8_t months, uint8_t days) {
|
||||
namespace chrono = std::chrono;
|
||||
const auto ymd = chrono::year_month_day(chrono::year(years), chrono::month(months), chrono::day(days));
|
||||
return chrono::sys_days{ymd}.time_since_epoch();
|
||||
}
|
||||
|
||||
Date UtcToday();
|
||||
LocalTime UtcLocalTime();
|
||||
LocalDateTime UtcLocalDateTime();
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
@ -343,3 +344,205 @@ TEST(TemporalTest, PrintLocalDateTime) {
|
||||
ASSERT_TRUE(stream);
|
||||
ASSERT_EQ(stream.view(), "1970-01-01T13:02:40.100050");
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DurationAddition) {
|
||||
// a >= 0 && b >= 0
|
||||
const auto zero = utils::Duration(0);
|
||||
const auto one = utils::Duration(1);
|
||||
const auto two = one + one;
|
||||
const auto four = two + two;
|
||||
ASSERT_EQ(two.microseconds, 2);
|
||||
ASSERT_EQ(four.microseconds, 4);
|
||||
const auto max = utils::Duration(std::numeric_limits<int64_t>::max());
|
||||
ASSERT_THROW(max + one, utils::BasicException);
|
||||
ASSERT_EQ(zero + zero, zero);
|
||||
ASSERT_EQ(max + zero, max);
|
||||
|
||||
// a < 0 && b < 0
|
||||
const auto neg_one = -one;
|
||||
const auto neg_two = neg_one + neg_one;
|
||||
const auto neg_four = neg_two + neg_two;
|
||||
ASSERT_EQ(neg_two.microseconds, -2);
|
||||
ASSERT_EQ(neg_four.microseconds, -4);
|
||||
const auto min = utils::Duration(std::numeric_limits<int64_t>::min());
|
||||
ASSERT_THROW(min + neg_one, utils::BasicException);
|
||||
ASSERT_EQ(min + zero, min);
|
||||
|
||||
// a < 0, b > 0 && a > 0 , b < 0
|
||||
ASSERT_EQ(neg_one + one, zero);
|
||||
ASSERT_EQ(neg_two + one, neg_one);
|
||||
ASSERT_EQ(neg_two + four, two);
|
||||
ASSERT_EQ(four + neg_two, two);
|
||||
|
||||
// a {min, max} && b {min, max}
|
||||
ASSERT_EQ(min + max, neg_one);
|
||||
ASSERT_EQ(max + min, neg_one);
|
||||
ASSERT_THROW(min + min, utils::BasicException);
|
||||
ASSERT_THROW(max + max, utils::BasicException);
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DurationSubtraction) {
|
||||
// a >= 0 && b >= 0
|
||||
const auto one = utils::Duration(1);
|
||||
const auto two = one + one;
|
||||
const auto zero = one - one;
|
||||
ASSERT_EQ(zero.microseconds, 0);
|
||||
const auto neg_one = zero - one;
|
||||
const auto neg_two = zero - two;
|
||||
ASSERT_EQ(neg_one.microseconds, -1);
|
||||
ASSERT_EQ(neg_two.microseconds, -2);
|
||||
const auto max = utils::Duration(std::numeric_limits<int64_t>::max());
|
||||
const auto min_minus_one = utils::Duration(std::numeric_limits<int64_t>::min() + 1);
|
||||
ASSERT_EQ(max - zero, max);
|
||||
ASSERT_EQ(zero - max, min_minus_one);
|
||||
|
||||
// a < 0 && b < 0
|
||||
ASSERT_EQ(neg_two - neg_two, zero);
|
||||
const auto min = utils::Duration(std::numeric_limits<int64_t>::min());
|
||||
ASSERT_THROW(min - one, utils::BasicException);
|
||||
ASSERT_EQ(min - zero, min);
|
||||
|
||||
// a < 0, b > 0 && a > 0 , b < 0
|
||||
ASSERT_EQ(neg_one - one, neg_two);
|
||||
ASSERT_EQ(one - neg_one, two);
|
||||
const auto neg_three = utils::Duration(-3);
|
||||
const auto three = -neg_three;
|
||||
ASSERT_EQ(neg_two - one, neg_three);
|
||||
ASSERT_EQ(one - neg_two, three);
|
||||
|
||||
// a {min, max} && b {min, max}
|
||||
ASSERT_THROW(min - max, utils::BasicException);
|
||||
ASSERT_THROW(max - min, utils::BasicException);
|
||||
// There is no positive representation of min
|
||||
ASSERT_THROW(min - min, utils::BasicException);
|
||||
ASSERT_EQ(max - max, zero);
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalTimeAndDurationAddition) {
|
||||
const auto half_past_one = utils::LocalTime({1, 30, 10});
|
||||
const auto three = half_past_one + utils::Duration({1994, 2, 10, 1, 30, 2, 22, 45});
|
||||
const auto three_symmetrical = utils::Duration({1994, 2, 10, 1, 30, 2, 22, 45}) + half_past_one;
|
||||
ASSERT_EQ(three, utils::LocalTime({3, 0, 12, 22, 45}));
|
||||
ASSERT_EQ(three_symmetrical, utils::LocalTime({3, 0, 12, 22, 45}));
|
||||
|
||||
const auto half_an_hour_before_midnight = utils::LocalTime({23, 30, 10});
|
||||
{
|
||||
const auto half_past_midnight = half_an_hour_before_midnight + utils::Duration({1994, 1, 10, 1});
|
||||
ASSERT_EQ(half_past_midnight, utils::LocalTime({.minutes = 30, .seconds = 10}));
|
||||
}
|
||||
const auto identity = half_an_hour_before_midnight + utils::Duration({.days = 1});
|
||||
ASSERT_EQ(identity, half_an_hour_before_midnight);
|
||||
ASSERT_EQ(identity, half_an_hour_before_midnight + utils::Duration({.days = 1, .hours = 24}));
|
||||
const auto an_hour_and_a_half_before_midnight = utils::LocalTime({22, 30, 10});
|
||||
ASSERT_EQ(half_an_hour_before_midnight + utils::Duration({.hours = 23}), an_hour_and_a_half_before_midnight);
|
||||
|
||||
const auto minus_one_hour = utils::Duration({-1994, -2, -10, -1, 0, 0, -20, -20});
|
||||
const auto minus_one_hour_exact = utils::Duration({-1994, -2, -10, -1});
|
||||
{
|
||||
const auto half_past_midnight = half_past_one + minus_one_hour;
|
||||
ASSERT_EQ(half_past_midnight, utils::LocalTime({0, 30, 9, 979, 980}));
|
||||
ASSERT_EQ(half_past_midnight + minus_one_hour_exact, utils::LocalTime({23, 30, 9, 979, 980}));
|
||||
|
||||
const auto minus_two_hours_thirty_mins = utils::Duration({-1994, -2, -10, -2, -30, -9});
|
||||
ASSERT_EQ(half_past_midnight + minus_two_hours_thirty_mins, utils::LocalTime({22, 0, 0, 979, 980}));
|
||||
|
||||
ASSERT_NO_THROW(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::max())));
|
||||
ASSERT_EQ(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::max())),
|
||||
utils::LocalTime({0, 22, 40, 755, 787}));
|
||||
ASSERT_NO_THROW(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::min())));
|
||||
ASSERT_EQ(half_past_midnight + (utils::Duration(std::numeric_limits<int64_t>::min())),
|
||||
utils::LocalTime({0, 37, 39, 204, 172}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalTimeAndDurationSubtraction) {
|
||||
const auto half_past_one = utils::LocalTime({1, 30, 10});
|
||||
const auto midnight = half_past_one - utils::Duration({1994, 2, 10, 1, 30, 10});
|
||||
ASSERT_EQ(midnight, utils::LocalTime());
|
||||
ASSERT_EQ(midnight - utils::Duration({-1994, -2, -10, -1, -30, -10}), utils::LocalTime({1, 30, 10}));
|
||||
|
||||
const auto almost_an_hour_and_a_half_before_midnight = midnight - utils::Duration({1994, 2, 10, 1, 30, 1, 20, 20});
|
||||
ASSERT_EQ(almost_an_hour_and_a_half_before_midnight, utils::LocalTime({22, 29, 58, 979, 980}));
|
||||
|
||||
ASSERT_NO_THROW(midnight - (utils::Duration(std::numeric_limits<int64_t>::max())));
|
||||
ASSERT_EQ(midnight - (utils::Duration(std::numeric_limits<int64_t>::max())), utils::LocalTime({0, 7, 29, 224, 193}));
|
||||
ASSERT_THROW(midnight - utils::Duration(std::numeric_limits<int64_t>::min()), utils::BasicException);
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalTimeDeltaDuration) {
|
||||
const auto half_past_one = utils::LocalTime({1, 30, 10});
|
||||
const auto half_past_two = utils::LocalTime({2, 30, 10});
|
||||
const auto an_hour_negative = half_past_one - half_past_two;
|
||||
ASSERT_EQ(an_hour_negative, utils::Duration({.hours = -1}));
|
||||
const auto an_hour = half_past_two - half_past_one;
|
||||
ASSERT_EQ(an_hour, utils::Duration({.hours = 1}));
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DateAddition) {
|
||||
const auto unix_epoch = utils::Date({1970, 1, 1});
|
||||
const auto one_day_after_unix_epoch = unix_epoch + utils::Duration({.days = 1});
|
||||
const auto one_day_after_unix_epoch_symmetrical = utils::Duration({.days = 1}) + unix_epoch;
|
||||
ASSERT_EQ(one_day_after_unix_epoch, utils::Date({1970, 1, 2}));
|
||||
ASSERT_EQ(one_day_after_unix_epoch_symmetrical, one_day_after_unix_epoch);
|
||||
|
||||
const auto one_month_after_unix_epoch = unix_epoch + utils::Duration({.days = 31});
|
||||
ASSERT_EQ(one_month_after_unix_epoch, utils::Date({1970, 2, 1}));
|
||||
|
||||
const auto one_year_after_unix_epoch = unix_epoch + utils::Duration({.days = 365});
|
||||
ASSERT_EQ(one_year_after_unix_epoch, utils::Date({1971, 1, 1}));
|
||||
|
||||
const auto last_day_of_unix_epoch = one_year_after_unix_epoch + utils::Duration({.days = -1});
|
||||
ASSERT_EQ(last_day_of_unix_epoch, utils::Date({1970, 12, 31}));
|
||||
|
||||
const auto one_day_before_unix_epoch = unix_epoch + utils::Duration({.days = -1});
|
||||
ASSERT_EQ(one_day_before_unix_epoch, utils::Date({1969, 12, 31}));
|
||||
|
||||
ASSERT_EQ(last_day_of_unix_epoch + utils::Duration({.days = -31}), utils::Date({1970, 11, 30}));
|
||||
ASSERT_THROW(unix_epoch + utils::Duration(std::numeric_limits<int64_t>::max()), utils::BasicException);
|
||||
ASSERT_THROW(unix_epoch + utils::Duration(std::numeric_limits<int64_t>::min()), utils::BasicException);
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DateSubstraction) {
|
||||
const auto day_after_unix_epoch = utils::Date({1970, 1, 2});
|
||||
const auto unix_epoch = day_after_unix_epoch - utils::Duration({.days = 1});
|
||||
ASSERT_EQ(unix_epoch, utils::Date({1970, 1, 1}));
|
||||
ASSERT_EQ(utils::Date({1971, 1, 1}) - utils::Duration({.days = 1}), utils::Date({1970, 12, 31}));
|
||||
ASSERT_EQ(utils::Date({1971, 1, 1}) - utils::Duration({.days = -1}), utils::Date({1971, 1, 2}));
|
||||
ASSERT_THROW(unix_epoch - utils::Duration(std::numeric_limits<int64_t>::max()), utils::BasicException);
|
||||
ASSERT_THROW(unix_epoch - utils::Duration(std::numeric_limits<int64_t>::min()), utils::BasicException);
|
||||
}
|
||||
|
||||
TEST(TemporalTest, DateDelta) {
|
||||
const auto unix_epoch = utils::Date({1970, 1, 1});
|
||||
const auto one_year_after_unix_epoch = utils::Date({1971, 1, 1});
|
||||
ASSERT_EQ(one_year_after_unix_epoch - unix_epoch, utils::Duration({.days = 365}));
|
||||
ASSERT_EQ(unix_epoch - one_year_after_unix_epoch, utils::Duration({.days = -365}));
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalDateTimeAdditionSubtraction) {
|
||||
const auto unix_epoch = utils::LocalDateTime({1970, 1, 1}, {.hours = 12});
|
||||
auto one_day_after_unix_epoch = unix_epoch + utils::Duration({.hours = 24});
|
||||
auto one_day_after_unix_epoch_symmetrical = utils::Duration({.hours = 24}) + unix_epoch;
|
||||
ASSERT_EQ(one_day_after_unix_epoch, utils::LocalDateTime({1970, 1, 2}, {.hours = 12}));
|
||||
ASSERT_EQ(one_day_after_unix_epoch_symmetrical, one_day_after_unix_epoch);
|
||||
|
||||
one_day_after_unix_epoch = unix_epoch + utils::Duration({.days = 1});
|
||||
ASSERT_EQ(one_day_after_unix_epoch, utils::LocalDateTime({1970, 1, 2}, {.hours = 12}));
|
||||
|
||||
ASSERT_EQ(one_day_after_unix_epoch + utils::Duration({.days = -1}), unix_epoch);
|
||||
ASSERT_EQ(one_day_after_unix_epoch - utils::Duration({.days = 1}), unix_epoch);
|
||||
ASSERT_THROW(one_day_after_unix_epoch + utils::Duration(std::numeric_limits<int64_t>::max()), utils::BasicException);
|
||||
ASSERT_THROW(one_day_after_unix_epoch + utils::Duration(std::numeric_limits<int64_t>::min()), utils::BasicException);
|
||||
ASSERT_THROW(one_day_after_unix_epoch - utils::Duration(std::numeric_limits<int64_t>::max()), utils::BasicException);
|
||||
ASSERT_THROW(one_day_after_unix_epoch - utils::Duration(std::numeric_limits<int64_t>::min()), utils::BasicException);
|
||||
}
|
||||
|
||||
TEST(TemporalTest, LocalDateTimeDelta) {
|
||||
const auto unix_epoch = utils::LocalDateTime({1970, 1, 1}, {1, 1, 1});
|
||||
const auto one_year_after_unix_epoch = utils::LocalDateTime({1971, 2, 1}, {12, 1, 1});
|
||||
const auto two_years_after_unix_epoch = utils::LocalDateTime({1972, 2, 1}, {1, 1, 1, 20, 34});
|
||||
ASSERT_EQ(one_year_after_unix_epoch - unix_epoch, utils::Duration({.days = 396, .hours = 11}));
|
||||
ASSERT_EQ(unix_epoch - one_year_after_unix_epoch, utils::Duration({.days = -396, .hours = -11}));
|
||||
ASSERT_EQ(two_years_after_unix_epoch - unix_epoch,
|
||||
utils::Duration({.days = 761, .milliseconds = 20, .microseconds = 34}));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user