civil.cpp 7.8 KB

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