aboutsummaryrefslogtreecommitdiff
path: root/src/openvic/Date.cpp
blob: 109b82d045384e7a0507855dd3a4248b1882b3d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "Date.hpp"

#include <algorithm>
#include <cassert>
#include <cctype>

#include "utility/Logger.hpp"

using namespace OpenVic;

Timespan::Timespan(day_t value) : days { value } {}

bool Timespan::operator<(Timespan other) const { return days < other.days; };
bool Timespan::operator>(Timespan other) const { return days > other.days; };
bool Timespan::operator<=(Timespan other) const { return days <= other.days; };
bool Timespan::operator>=(Timespan other) const { return days >= other.days; };
bool Timespan::operator==(Timespan other) const { return days == other.days; };
bool Timespan::operator!=(Timespan other) const { return days != other.days; };

Timespan Timespan::operator+(Timespan other) const { return days + other.days; }

Timespan Timespan::operator-(Timespan other) const { return days - other.days; }

Timespan Timespan::operator*(day_t factor) const { return days * factor; }

Timespan Timespan::operator/(day_t factor) const { return days / factor; }

Timespan& Timespan::operator+=(Timespan other) {
   days += other.days;
   return *this;
}

Timespan& Timespan::operator-=(Timespan other) {
   days -= other.days;
   return *this;
}

Timespan& Timespan::operator++() {
   days++;
   return *this;
}

Timespan Timespan::operator++(int) {
   Timespan old = *this;
   ++(*this);
   return old;
}

Timespan::operator day_t() const {
   return days;
}

Timespan::operator double() const {
   return days;
}

Timespan::operator std::string() const {
   return std::to_string(days);
}

std::ostream& OpenVic::operator<<(std::ostream& out, Timespan const& timespan) {
   return out << static_cast<std::string>(timespan);
}

Timespan Date::_dateToTimespan(year_t year, month_t month, day_t day) {
   month = std::clamp<month_t>(month, 1, MONTHS_IN_YEAR);
   day = std::clamp<day_t>(day, 1, DAYS_IN_MONTH[month - 1]);
   return year * DAYS_IN_YEAR + DAYS_UP_TO_MONTH[month - 1] + day - 1;
}

Timespan::day_t const* Date::DAYS_UP_TO_MONTH = generate_days_up_to_month();

Timespan::day_t const* Date::generate_days_up_to_month() {
   static Timespan::day_t days_up_to_month[MONTHS_IN_YEAR];
   Timespan::day_t days = 0;
   for (int month = 0; month < MONTHS_IN_YEAR;
      days_up_to_month[month] = days, days += DAYS_IN_MONTH[month++]);
   assert(days == DAYS_IN_YEAR);
   return days_up_to_month;
}

Date::month_t const* Date::MONTH_FROM_DAY_IN_YEAR = generate_month_from_day_in_year();

Date::month_t const* Date::generate_month_from_day_in_year() {
   static month_t month_from_day_in_year[DAYS_IN_YEAR];
   Timespan::day_t days_left = 0;
   for (int day = 0, month = 0; day < DAYS_IN_YEAR;
      days_left = (days_left > 0 ? days_left : DAYS_IN_MONTH[month++]) - 1,
      month_from_day_in_year[day++] = month);
   assert(days_left == 0);
   assert(month_from_day_in_year[DAYS_IN_YEAR - 1] == MONTHS_IN_YEAR);
   return month_from_day_in_year;
}

Date::Date(Timespan total_days) : timespan { total_days } {
   if (timespan < 0) {
      Logger::error("Invalid timespan for date: ", timespan, " (cannot be negative)");
      timespan = 0;
   }
}

Date::Date(year_t year, month_t month, day_t day) : timespan { _dateToTimespan(year, month, day) } {}

Date::year_t Date::getYear() const {
   return static_cast<Timespan::day_t>(timespan) / DAYS_IN_YEAR;
}

Date::month_t Date::getMonth() const {
   return MONTH_FROM_DAY_IN_YEAR[static_cast<Timespan::day_t>(timespan) % DAYS_IN_YEAR];
}

Date::day_t Date::getDay() const {
   return (static_cast<Timespan::day_t>(timespan) % DAYS_IN_YEAR) - DAYS_UP_TO_MONTH[getMonth() - 1] + 1;
}

bool Date::operator<(Date other) const { return timespan < other.timespan; };
bool Date::operator>(Date other) const { return timespan > other.timespan; };
bool Date::operator<=(Date other) const { return timespan <= other.timespan; };
bool Date::operator>=(Date other) const { return timespan >= other.timespan; };
bool Date::operator==(Date other) const { return timespan == other.timespan; };
bool Date::operator!=(Date other) const { return timespan != other.timespan; };

Date Date::operator+(Timespan other) const { return timespan + other; }

Timespan Date::operator-(Date other) const { return timespan - other.timespan; }

Date& Date::operator+=(Timespan other) {
   timespan += other;
   return *this;
}

Date& Date::operator-=(Timespan other) {
   timespan -= other;
   return *this;
}

Date& Date::operator++() {
   timespan++;
   return *this;
}

Date Date::operator++(int) {
   Date old = *this;
   ++(*this);
   return old;
}

Date::operator std::string() const {
   std::stringstream ss;
   ss << *this;
   return ss.str();
}

std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) {
   return out << static_cast<int>(date.getYear()) << '.' << static_cast<int>(date.getMonth()) << '.' << static_cast<int>(date.getDay());
}

// Parsed from string of the form YYYY.MM.DD
Date Date::from_string(std::string const& date) {
   year_t year = 0;
   month_t month = 1;
   day_t day = 1;

   size_t first_pos = 0;
   while (first_pos < date.length() && std::isdigit(date[first_pos++]));
   year = stoi(date.substr(0, first_pos));
   if (first_pos < date.length()) {
      if (date[first_pos] == '.') {
         size_t second_pos = first_pos + 1;
         while (second_pos < date.length() && std::isdigit(date[second_pos++]));
         month = stoi(date.substr(first_pos, second_pos - first_pos));
         if (second_pos < date.length()) {
            if (date[second_pos] == '.') {
               size_t third_pos = second_pos + 1;
               while (third_pos < date.length() && std::isdigit(date[third_pos++]));
               day = stoi(date.substr(second_pos, third_pos - second_pos));
               if (third_pos < date.length())
                  Logger::error("Unexpected string \"", date.substr(third_pos), "\" at the end of date ", date);
            } else Logger::error("Unexpected character \"", date[second_pos], "\" in date ", date);
         }
      } else Logger::error("Unexpected character \"", date[first_pos], "\" in date ", date);
   }
   return _dateToTimespan(year, month, day);
};