civil.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #include "civil.h"
  2. #include <util/stream/output.h>
  3. #include <util/stream/format.h>
  4. #include <util/string/ascii.h>
  5. namespace {
  6. bool TryParseInt(TStringBuf& s, int& dst, size_t maxDigits) {
  7. int res = 0;
  8. size_t i = 0;
  9. while (i < maxDigits && !s.empty() && IsAsciiDigit(s[0])) {
  10. res = res * 10 + (s[0] - '0');
  11. ++i;
  12. s.Skip(1);
  13. }
  14. if (i == 0) {
  15. return false;
  16. }
  17. dst = res;
  18. return true;
  19. }
  20. bool TryParseUTCOffsetTimezone(TStringBuf name, int& offset) {
  21. static constexpr TStringBuf OFFSET_PREFIX = "UTC";
  22. if (!name.SkipPrefix(OFFSET_PREFIX)) {
  23. return false;
  24. }
  25. if (name.empty()) {
  26. return false;
  27. }
  28. bool negative;
  29. if (name[0] == '+') {
  30. negative = false;
  31. } else if (name[0] == '-') {
  32. negative = true;
  33. } else {
  34. return false;
  35. }
  36. name.Skip(1);
  37. int hour;
  38. int minute = 0;
  39. if (!TryParseInt(name, hour, 2) || hour > 24) {
  40. return false;
  41. }
  42. if (!name.empty()) {
  43. if (name[0] == ':') {
  44. name.Skip(1);
  45. }
  46. if (!TryParseInt(name, minute, 2) || minute >= 60) {
  47. return false;
  48. }
  49. if (!name.empty()) {
  50. return false;
  51. }
  52. }
  53. if (hour == 24 && minute != 0) {
  54. return false;
  55. }
  56. offset = (hour * 60 + minute) * 60;
  57. if (negative)
  58. offset = -offset;
  59. return true;
  60. }
  61. } // anonymous namespace
  62. namespace NDatetime {
  63. TTimeZone GetTimeZone(TStringBuf name) {
  64. int offset;
  65. if (TryParseUTCOffsetTimezone(name, offset)) {
  66. return GetFixedTimeZone(offset);
  67. }
  68. TTimeZone result;
  69. if (!cctz::load_time_zone(static_cast<std::string>(name), &result)) {
  70. ythrow TInvalidTimezone() << "Failed to load time zone " << name << ", " << result.name();
  71. }
  72. return result;
  73. }
  74. TTimeZone GetFixedTimeZone(const long offset) {
  75. return cctz::fixed_time_zone(std::chrono::seconds(offset));
  76. }
  77. TCivilSecond Convert(const TInstant& absTime, const TTimeZone& tz) {
  78. return cctz::convert(TSystemClock::from_time_t(absTime.TimeT()), tz);
  79. }
  80. TCivilSecond Convert(const TInstant& absTime, TStringBuf tzName) {
  81. TTimeZone tz = GetTimeZone(tzName);
  82. return cctz::convert(TSystemClock::from_time_t(absTime.TimeT()), tz);
  83. }
  84. TInstant Convert(const TCivilSecond& tp, const TTimeZone& tz) {
  85. return TInstant::Seconds(cctz::convert(tp, tz).time_since_epoch().count());
  86. }
  87. TCivilSecond AddYears(const TCivilSecond& tp, TDiff diff) {
  88. TCivilYear newYear = Calc<TCivilYear>(tp, diff);
  89. return NDatetime::TCivilSecond(newYear.year(), tp.month(), tp.day(), tp.hour(), tp.minute(), tp.second());
  90. }
  91. TCivilSecond AddMonths(const TCivilSecond& tp, TDiff diff) {
  92. TCivilMonth newMonth = Calc<TCivilMonth>(tp, diff);
  93. return NDatetime::TCivilSecond(newMonth.year(), newMonth.month(), tp.day(), tp.hour(), tp.minute(), tp.second());
  94. }
  95. TCivilSecond AddDays(const TCivilSecond& tp, TDiff diff) {
  96. TCivilDay newDay = Calc<TCivilDay>(tp, diff);
  97. return NDatetime::TCivilSecond(newDay.year(), newDay.month(), newDay.day(), tp.hour(), tp.minute(), tp.second());
  98. }
  99. TCivilSecond AddHours(const TCivilSecond& tp, TDiff diff) {
  100. TCivilHour newHour = Calc<TCivilHour>(tp, diff);
  101. return NDatetime::TCivilSecond(newHour.year(), newHour.month(), newHour.day(), newHour.hour(), tp.minute(), tp.second());
  102. }
  103. TCivilSecond AddMinutes(const TCivilSecond& tp, TDiff diff) {
  104. TCivilMinute newMinute = Calc<TCivilMinute>(tp, diff);
  105. return NDatetime::TCivilSecond(newMinute.year(), newMinute.month(), newMinute.day(), newMinute.hour(), newMinute.minute(), tp.second());
  106. }
  107. TCivilSecond AddSeconds(const TCivilSecond& tp, TDiff diff) {
  108. return Calc<TCivilSecond>(tp, diff);
  109. }
  110. TCivilSecond AddCivil(const TCivilSecond& tp, TCivilDiff diff) {
  111. switch (diff.Unit) {
  112. case ECivilUnit::Second: {
  113. return AddSeconds(tp, diff.Value);
  114. }
  115. case ECivilUnit::Minute: {
  116. return AddMinutes(tp, diff.Value);
  117. }
  118. case ECivilUnit::Hour: {
  119. return AddHours(tp, diff.Value);
  120. }
  121. case ECivilUnit::Day: {
  122. return AddDays(tp, diff.Value);
  123. }
  124. case ECivilUnit::Month: {
  125. return AddMonths(tp, diff.Value);
  126. }
  127. case ECivilUnit::Year: {
  128. return AddYears(tp, diff.Value);
  129. }
  130. default: {
  131. ythrow yexception() << "Unexpected civil unit value " << static_cast<int>(diff.Unit);
  132. }
  133. }
  134. }
  135. TCivilDiff GetCivilDiff(const TCivilSecond& tpX, const TCivilSecond& tpY, ECivilUnit unit) {
  136. switch (unit) {
  137. case ECivilUnit::Second: {
  138. return {tpX - tpY, unit};
  139. }
  140. case ECivilUnit::Minute: {
  141. return {static_cast<TCivilMinute>(tpX) - static_cast<TCivilMinute>(tpY), unit};
  142. }
  143. case ECivilUnit::Hour: {
  144. return {static_cast<TCivilHour>(tpX) - static_cast<TCivilHour>(tpY), unit};
  145. }
  146. case ECivilUnit::Day: {
  147. return {static_cast<TCivilDay>(tpX) - static_cast<TCivilDay>(tpY), unit};
  148. }
  149. case ECivilUnit::Month: {
  150. return {static_cast<TCivilMonth>(tpX) - static_cast<TCivilMonth>(tpY), unit};
  151. }
  152. case ECivilUnit::Year: {
  153. return {static_cast<TCivilYear>(tpX) - static_cast<TCivilYear>(tpY), unit};
  154. }
  155. default: {
  156. ythrow yexception() << "Unexpected civil unit value " << static_cast<int>(unit);
  157. }
  158. }
  159. }
  160. }
  161. template <>
  162. void Out<NDatetime::TCivilYear>(IOutputStream& out, const NDatetime::TCivilYear& y) {
  163. out << y.year();
  164. }
  165. template <>
  166. void Out<NDatetime::TCivilMonth>(IOutputStream& out, const NDatetime::TCivilMonth& m) {
  167. out << NDatetime::TCivilYear(m) << '-' << LeftPad(m.month(), 2, '0');
  168. }
  169. template <>
  170. void Out<NDatetime::TCivilDay>(IOutputStream& out, const NDatetime::TCivilDay& d) {
  171. out << NDatetime::TCivilMonth(d) << '-' << LeftPad(d.day(), 2, '0');
  172. }
  173. template <>
  174. void Out<NDatetime::TCivilHour>(IOutputStream& out, const NDatetime::TCivilHour& h) {
  175. out << NDatetime::TCivilDay(h) << 'T' << LeftPad(h.hour(), 2, '0');
  176. }
  177. template <>
  178. void Out<NDatetime::TCivilMinute>(IOutputStream& out, const NDatetime::TCivilMinute& m) {
  179. out << NDatetime::TCivilHour(m) << ':' << LeftPad(m.minute(), 2, '0');
  180. }
  181. template <>
  182. void Out<NDatetime::TCivilSecond>(IOutputStream& out, const NDatetime::TCivilSecond& s) {
  183. out << NDatetime::TCivilMinute(s) << ':' << LeftPad(s.second(), 2, '0');
  184. }
  185. template <>
  186. void Out<NDatetime::TWeekday>(IOutputStream& out, NDatetime::TWeekday wd) {
  187. using namespace cctz;
  188. switch (wd) {
  189. case weekday::monday:
  190. out << TStringBuf("Monday");
  191. break;
  192. case weekday::tuesday:
  193. out << TStringBuf("Tuesday");
  194. break;
  195. case weekday::wednesday:
  196. out << TStringBuf("Wednesday");
  197. break;
  198. case weekday::thursday:
  199. out << TStringBuf("Thursday");
  200. break;
  201. case weekday::friday:
  202. out << TStringBuf("Friday");
  203. break;
  204. case weekday::saturday:
  205. out << TStringBuf("Saturday");
  206. break;
  207. case weekday::sunday:
  208. out << TStringBuf("Sunday");
  209. break;
  210. }
  211. }