date_time.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/common/date_time.h>
  6. #include <aws/common/array_list.h>
  7. #include <aws/common/byte_buf.h>
  8. #include <aws/common/byte_order.h>
  9. #include <aws/common/clock.h>
  10. #include <aws/common/string.h>
  11. #include <aws/common/time.h>
  12. #include <ctype.h>
  13. #include <math.h>
  14. static const char *RFC822_DATE_FORMAT_STR_MINUS_Z = "%a, %d %b %Y %H:%M:%S GMT";
  15. static const char *RFC822_DATE_FORMAT_STR_WITH_Z = "%a, %d %b %Y %H:%M:%S %Z";
  16. static const char *RFC822_SHORT_DATE_FORMAT_STR = "%a, %d %b %Y";
  17. static const char *ISO_8601_LONG_DATE_FORMAT_STR = "%Y-%m-%dT%H:%M:%SZ";
  18. static const char *ISO_8601_SHORT_DATE_FORMAT_STR = "%Y-%m-%d";
  19. static const char *ISO_8601_LONG_BASIC_DATE_FORMAT_STR = "%Y%m%dT%H%M%SZ";
  20. static const char *ISO_8601_SHORT_BASIC_DATE_FORMAT_STR = "%Y%m%d";
  21. #define STR_TRIPLET_TO_INDEX(str) \
  22. (((uint32_t)tolower((uint8_t)((str)[0])) << 0) | ((uint32_t)tolower((uint8_t)((str)[1])) << 8) | \
  23. ((uint32_t)tolower((uint8_t)((str)[2])) << 16))
  24. static uint32_t s_jan = 0;
  25. static uint32_t s_feb = 0;
  26. static uint32_t s_mar = 0;
  27. static uint32_t s_apr = 0;
  28. static uint32_t s_may = 0;
  29. static uint32_t s_jun = 0;
  30. static uint32_t s_jul = 0;
  31. static uint32_t s_aug = 0;
  32. static uint32_t s_sep = 0;
  33. static uint32_t s_oct = 0;
  34. static uint32_t s_nov = 0;
  35. static uint32_t s_dec = 0;
  36. static uint32_t s_utc = 0;
  37. static uint32_t s_gmt = 0;
  38. static void s_check_init_str_to_int(void) {
  39. if (!s_jan) {
  40. s_jan = STR_TRIPLET_TO_INDEX("jan");
  41. s_feb = STR_TRIPLET_TO_INDEX("feb");
  42. s_mar = STR_TRIPLET_TO_INDEX("mar");
  43. s_apr = STR_TRIPLET_TO_INDEX("apr");
  44. s_may = STR_TRIPLET_TO_INDEX("may");
  45. s_jun = STR_TRIPLET_TO_INDEX("jun");
  46. s_jul = STR_TRIPLET_TO_INDEX("jul");
  47. s_aug = STR_TRIPLET_TO_INDEX("aug");
  48. s_sep = STR_TRIPLET_TO_INDEX("sep");
  49. s_oct = STR_TRIPLET_TO_INDEX("oct");
  50. s_nov = STR_TRIPLET_TO_INDEX("nov");
  51. s_dec = STR_TRIPLET_TO_INDEX("dec");
  52. s_utc = STR_TRIPLET_TO_INDEX("utc");
  53. s_gmt = STR_TRIPLET_TO_INDEX("gmt");
  54. }
  55. }
  56. /* Get the 0-11 monthly number from a string representing Month. Case insensitive and will stop on abbreviation*/
  57. static int get_month_number_from_str(const char *time_string, size_t start_index, size_t stop_index) {
  58. s_check_init_str_to_int();
  59. if (stop_index - start_index < 3) {
  60. return -1;
  61. }
  62. /* This AND forces the string to lowercase (assuming ASCII) */
  63. uint32_t comp_val = STR_TRIPLET_TO_INDEX(time_string + start_index);
  64. /* this can't be a switch, because I can't make it a constant expression. */
  65. if (s_jan == comp_val) {
  66. return 0;
  67. }
  68. if (s_feb == comp_val) {
  69. return 1;
  70. }
  71. if (s_mar == comp_val) {
  72. return 2;
  73. }
  74. if (s_apr == comp_val) {
  75. return 3;
  76. }
  77. if (s_may == comp_val) {
  78. return 4;
  79. }
  80. if (s_jun == comp_val) {
  81. return 5;
  82. }
  83. if (s_jul == comp_val) {
  84. return 6;
  85. }
  86. if (s_aug == comp_val) {
  87. return 7;
  88. }
  89. if (s_sep == comp_val) {
  90. return 8;
  91. }
  92. if (s_oct == comp_val) {
  93. return 9;
  94. }
  95. if (s_nov == comp_val) {
  96. return 10;
  97. }
  98. if (s_dec == comp_val) {
  99. return 11;
  100. }
  101. return -1;
  102. }
  103. /* Detects whether or not the passed in timezone string is a UTC zone. */
  104. static bool is_utc_time_zone(const char *str) {
  105. s_check_init_str_to_int();
  106. size_t len = strlen(str);
  107. if (len > 0) {
  108. if (str[0] == 'Z') {
  109. return true;
  110. }
  111. /* offsets count since their usable */
  112. if (len == 5 && (str[0] == '+' || str[0] == '-')) {
  113. return true;
  114. }
  115. if (len == 2) {
  116. return tolower((uint8_t)str[0]) == 'u' && tolower((uint8_t)str[1]) == 't';
  117. }
  118. if (len < 3) {
  119. return false;
  120. }
  121. uint32_t comp_val = STR_TRIPLET_TO_INDEX(str);
  122. if (comp_val == s_utc || comp_val == s_gmt) {
  123. return true;
  124. }
  125. }
  126. return false;
  127. }
  128. struct tm s_get_time_struct(struct aws_date_time *dt, bool local_time) {
  129. struct tm time;
  130. AWS_ZERO_STRUCT(time);
  131. if (local_time) {
  132. aws_localtime(dt->timestamp, &time);
  133. } else {
  134. aws_gmtime(dt->timestamp, &time);
  135. }
  136. return time;
  137. }
  138. void aws_date_time_init_now(struct aws_date_time *dt) {
  139. uint64_t current_time_ns = 0;
  140. aws_sys_clock_get_ticks(&current_time_ns);
  141. aws_date_time_init_epoch_millis(
  142. dt, aws_timestamp_convert(current_time_ns, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_MILLIS, NULL));
  143. }
  144. void aws_date_time_init_epoch_millis(struct aws_date_time *dt, uint64_t ms_since_epoch) {
  145. uint64_t milliseconds = 0;
  146. dt->timestamp =
  147. (time_t)aws_timestamp_convert(ms_since_epoch, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, &milliseconds);
  148. dt->milliseconds = (uint16_t)milliseconds;
  149. dt->gmt_time = s_get_time_struct(dt, false);
  150. dt->local_time = s_get_time_struct(dt, true);
  151. }
  152. void aws_date_time_init_epoch_secs(struct aws_date_time *dt, double sec_ms) {
  153. double integral = 0;
  154. dt->milliseconds = (uint16_t)(round(modf(sec_ms, &integral) * AWS_TIMESTAMP_MILLIS));
  155. dt->timestamp = (time_t)integral;
  156. dt->gmt_time = s_get_time_struct(dt, false);
  157. dt->local_time = s_get_time_struct(dt, true);
  158. }
  159. enum parser_state {
  160. ON_WEEKDAY,
  161. ON_SPACE_DELIM,
  162. ON_YEAR,
  163. ON_MONTH,
  164. ON_MONTH_DAY,
  165. ON_HOUR,
  166. ON_MINUTE,
  167. ON_SECOND,
  168. ON_TZ,
  169. FINISHED,
  170. };
  171. static int s_parse_iso_8601_basic(const struct aws_byte_cursor *date_str_cursor, struct tm *parsed_time) {
  172. size_t index = 0;
  173. size_t state_start_index = 0;
  174. enum parser_state state = ON_YEAR;
  175. bool error = false;
  176. AWS_ZERO_STRUCT(*parsed_time);
  177. while (state < FINISHED && !error && index < date_str_cursor->len) {
  178. char c = date_str_cursor->ptr[index];
  179. size_t sub_index = index - state_start_index;
  180. switch (state) {
  181. case ON_YEAR:
  182. if (aws_isdigit(c)) {
  183. parsed_time->tm_year = parsed_time->tm_year * 10 + (c - '0');
  184. if (sub_index == 3) {
  185. state = ON_MONTH;
  186. state_start_index = index + 1;
  187. parsed_time->tm_year -= 1900;
  188. }
  189. } else {
  190. error = true;
  191. }
  192. break;
  193. case ON_MONTH:
  194. if (aws_isdigit(c)) {
  195. parsed_time->tm_mon = parsed_time->tm_mon * 10 + (c - '0');
  196. if (sub_index == 1) {
  197. state = ON_MONTH_DAY;
  198. state_start_index = index + 1;
  199. parsed_time->tm_mon -= 1;
  200. }
  201. } else {
  202. error = true;
  203. }
  204. break;
  205. case ON_MONTH_DAY:
  206. if (c == 'T' && sub_index == 2) {
  207. state = ON_HOUR;
  208. state_start_index = index + 1;
  209. } else if (aws_isdigit(c)) {
  210. parsed_time->tm_mday = parsed_time->tm_mday * 10 + (c - '0');
  211. } else {
  212. error = true;
  213. }
  214. break;
  215. case ON_HOUR:
  216. if (aws_isdigit(c)) {
  217. parsed_time->tm_hour = parsed_time->tm_hour * 10 + (c - '0');
  218. if (sub_index == 1) {
  219. state = ON_MINUTE;
  220. state_start_index = index + 1;
  221. }
  222. } else {
  223. error = true;
  224. }
  225. break;
  226. case ON_MINUTE:
  227. if (aws_isdigit(c)) {
  228. parsed_time->tm_min = parsed_time->tm_min * 10 + (c - '0');
  229. if (sub_index == 1) {
  230. state = ON_SECOND;
  231. state_start_index = index + 1;
  232. }
  233. } else {
  234. error = true;
  235. }
  236. break;
  237. case ON_SECOND:
  238. if (aws_isdigit(c)) {
  239. parsed_time->tm_sec = parsed_time->tm_sec * 10 + (c - '0');
  240. if (sub_index == 1) {
  241. state = ON_TZ;
  242. state_start_index = index + 1;
  243. }
  244. } else {
  245. error = true;
  246. }
  247. break;
  248. case ON_TZ:
  249. if (c == 'Z' && (sub_index == 0 || sub_index == 3)) {
  250. state = FINISHED;
  251. } else if (!aws_isdigit(c) || sub_index > 3) {
  252. error = true;
  253. }
  254. break;
  255. default:
  256. error = true;
  257. break;
  258. }
  259. index++;
  260. }
  261. /* ISO8601 supports date only with no time portion. state ==ON_MONTH_DAY catches this case. */
  262. return (state == FINISHED || state == ON_MONTH_DAY) && !error ? AWS_OP_SUCCESS : AWS_OP_ERR;
  263. }
  264. static int s_parse_iso_8601(const struct aws_byte_cursor *date_str_cursor, struct tm *parsed_time) {
  265. size_t index = 0;
  266. size_t state_start_index = 0;
  267. enum parser_state state = ON_YEAR;
  268. bool error = false;
  269. bool advance = true;
  270. AWS_ZERO_STRUCT(*parsed_time);
  271. while (state < FINISHED && !error && index < date_str_cursor->len) {
  272. char c = date_str_cursor->ptr[index];
  273. switch (state) {
  274. case ON_YEAR:
  275. if (c == '-' && index - state_start_index == 4) {
  276. state = ON_MONTH;
  277. state_start_index = index + 1;
  278. parsed_time->tm_year -= 1900;
  279. } else if (aws_isdigit(c)) {
  280. parsed_time->tm_year = parsed_time->tm_year * 10 + (c - '0');
  281. } else {
  282. error = true;
  283. }
  284. break;
  285. case ON_MONTH:
  286. if (c == '-' && index - state_start_index == 2) {
  287. state = ON_MONTH_DAY;
  288. state_start_index = index + 1;
  289. parsed_time->tm_mon -= 1;
  290. } else if (aws_isdigit(c)) {
  291. parsed_time->tm_mon = parsed_time->tm_mon * 10 + (c - '0');
  292. } else {
  293. error = true;
  294. }
  295. break;
  296. case ON_MONTH_DAY:
  297. if (c == 'T' && index - state_start_index == 2) {
  298. state = ON_HOUR;
  299. state_start_index = index + 1;
  300. } else if (aws_isdigit(c)) {
  301. parsed_time->tm_mday = parsed_time->tm_mday * 10 + (c - '0');
  302. } else {
  303. error = true;
  304. }
  305. break;
  306. /* note: no time portion is spec compliant. */
  307. case ON_HOUR:
  308. /* time parts can be delimited by ':' or just concatenated together, but must always be 2 digits. */
  309. if (index - state_start_index == 2) {
  310. state = ON_MINUTE;
  311. state_start_index = index + 1;
  312. if (aws_isdigit(c)) {
  313. state_start_index = index;
  314. advance = false;
  315. } else if (c != ':') {
  316. error = true;
  317. }
  318. } else if (aws_isdigit(c)) {
  319. parsed_time->tm_hour = parsed_time->tm_hour * 10 + (c - '0');
  320. } else {
  321. error = true;
  322. }
  323. break;
  324. case ON_MINUTE:
  325. /* time parts can be delimited by ':' or just concatenated together, but must always be 2 digits. */
  326. if (index - state_start_index == 2) {
  327. state = ON_SECOND;
  328. state_start_index = index + 1;
  329. if (aws_isdigit(c)) {
  330. state_start_index = index;
  331. advance = false;
  332. } else if (c != ':') {
  333. error = true;
  334. }
  335. } else if (aws_isdigit(c)) {
  336. parsed_time->tm_min = parsed_time->tm_min * 10 + (c - '0');
  337. } else {
  338. error = true;
  339. }
  340. break;
  341. case ON_SECOND:
  342. if (c == 'Z' && index - state_start_index == 2) {
  343. state = FINISHED;
  344. state_start_index = index + 1;
  345. } else if (c == '.' && index - state_start_index == 2) {
  346. state = ON_TZ;
  347. state_start_index = index + 1;
  348. } else if (aws_isdigit(c)) {
  349. parsed_time->tm_sec = parsed_time->tm_sec * 10 + (c - '0');
  350. } else {
  351. error = true;
  352. }
  353. break;
  354. case ON_TZ:
  355. if (c == 'Z') {
  356. state = FINISHED;
  357. state_start_index = index + 1;
  358. } else if (!aws_isdigit(c)) {
  359. error = true;
  360. }
  361. break;
  362. default:
  363. error = true;
  364. break;
  365. }
  366. if (advance) {
  367. index++;
  368. } else {
  369. advance = true;
  370. }
  371. }
  372. /* ISO8601 supports date only with no time portion. state ==ON_MONTH_DAY catches this case. */
  373. return (state == FINISHED || state == ON_MONTH_DAY) && !error ? AWS_OP_SUCCESS : AWS_OP_ERR;
  374. }
  375. static int s_parse_rfc_822(
  376. const struct aws_byte_cursor *date_str_cursor,
  377. struct tm *parsed_time,
  378. struct aws_date_time *dt) {
  379. size_t len = date_str_cursor->len;
  380. size_t index = 0;
  381. size_t state_start_index = 0;
  382. int state = ON_WEEKDAY;
  383. bool error = false;
  384. AWS_ZERO_STRUCT(*parsed_time);
  385. while (!error && index < len) {
  386. char c = date_str_cursor->ptr[index];
  387. switch (state) {
  388. /* week day abbr is optional. */
  389. case ON_WEEKDAY:
  390. if (c == ',') {
  391. state = ON_SPACE_DELIM;
  392. state_start_index = index + 1;
  393. } else if (aws_isdigit(c)) {
  394. state = ON_MONTH_DAY;
  395. } else if (!aws_isalpha(c)) {
  396. error = true;
  397. }
  398. break;
  399. case ON_SPACE_DELIM:
  400. if (aws_isspace(c)) {
  401. state = ON_MONTH_DAY;
  402. state_start_index = index + 1;
  403. } else {
  404. error = true;
  405. }
  406. break;
  407. case ON_MONTH_DAY:
  408. if (aws_isdigit(c)) {
  409. parsed_time->tm_mday = parsed_time->tm_mday * 10 + (c - '0');
  410. } else if (aws_isspace(c)) {
  411. state = ON_MONTH;
  412. state_start_index = index + 1;
  413. } else {
  414. error = true;
  415. }
  416. break;
  417. case ON_MONTH:
  418. if (aws_isspace(c)) {
  419. int monthNumber =
  420. get_month_number_from_str((const char *)date_str_cursor->ptr, state_start_index, index + 1);
  421. if (monthNumber > -1) {
  422. state = ON_YEAR;
  423. state_start_index = index + 1;
  424. parsed_time->tm_mon = monthNumber;
  425. } else {
  426. error = true;
  427. }
  428. } else if (!aws_isalpha(c)) {
  429. error = true;
  430. }
  431. break;
  432. /* year can be 4 or 2 digits. */
  433. case ON_YEAR:
  434. if (aws_isspace(c) && index - state_start_index == 4) {
  435. state = ON_HOUR;
  436. state_start_index = index + 1;
  437. parsed_time->tm_year -= 1900;
  438. } else if (aws_isspace(c) && index - state_start_index == 2) {
  439. state = 5;
  440. state_start_index = index + 1;
  441. parsed_time->tm_year += 2000 - 1900;
  442. } else if (aws_isdigit(c)) {
  443. parsed_time->tm_year = parsed_time->tm_year * 10 + (c - '0');
  444. } else {
  445. error = true;
  446. }
  447. break;
  448. case ON_HOUR:
  449. if (c == ':' && index - state_start_index == 2) {
  450. state = ON_MINUTE;
  451. state_start_index = index + 1;
  452. } else if (aws_isdigit(c)) {
  453. parsed_time->tm_hour = parsed_time->tm_hour * 10 + (c - '0');
  454. } else {
  455. error = true;
  456. }
  457. break;
  458. case ON_MINUTE:
  459. if (c == ':' && index - state_start_index == 2) {
  460. state = ON_SECOND;
  461. state_start_index = index + 1;
  462. } else if (aws_isdigit(c)) {
  463. parsed_time->tm_min = parsed_time->tm_min * 10 + (c - '0');
  464. } else {
  465. error = true;
  466. }
  467. break;
  468. case ON_SECOND:
  469. if (aws_isspace(c) && index - state_start_index == 2) {
  470. state = ON_TZ;
  471. state_start_index = index + 1;
  472. } else if (aws_isdigit(c)) {
  473. parsed_time->tm_sec = parsed_time->tm_sec * 10 + (c - '0');
  474. } else {
  475. error = true;
  476. }
  477. break;
  478. case ON_TZ:
  479. if ((aws_isalnum(c) || c == '-' || c == '+') && (index - state_start_index) < 5) {
  480. dt->tz[index - state_start_index] = c;
  481. } else {
  482. error = true;
  483. }
  484. break;
  485. default:
  486. error = true;
  487. break;
  488. }
  489. index++;
  490. }
  491. if (dt->tz[0] != 0) {
  492. if (is_utc_time_zone(dt->tz)) {
  493. dt->utc_assumed = true;
  494. } else {
  495. error = true;
  496. }
  497. }
  498. return error || state != ON_TZ ? AWS_OP_ERR : AWS_OP_SUCCESS;
  499. }
  500. int aws_date_time_init_from_str_cursor(
  501. struct aws_date_time *dt,
  502. const struct aws_byte_cursor *date_str_cursor,
  503. enum aws_date_format fmt) {
  504. AWS_ERROR_PRECONDITION(date_str_cursor->len <= AWS_DATE_TIME_STR_MAX_LEN, AWS_ERROR_OVERFLOW_DETECTED);
  505. AWS_ZERO_STRUCT(*dt);
  506. struct tm parsed_time;
  507. bool successfully_parsed = false;
  508. time_t seconds_offset = 0;
  509. if (fmt == AWS_DATE_FORMAT_ISO_8601 || fmt == AWS_DATE_FORMAT_AUTO_DETECT) {
  510. if (!s_parse_iso_8601(date_str_cursor, &parsed_time)) {
  511. dt->utc_assumed = true;
  512. successfully_parsed = true;
  513. }
  514. }
  515. if (fmt == AWS_DATE_FORMAT_ISO_8601_BASIC || (fmt == AWS_DATE_FORMAT_AUTO_DETECT && !successfully_parsed)) {
  516. if (!s_parse_iso_8601_basic(date_str_cursor, &parsed_time)) {
  517. dt->utc_assumed = true;
  518. successfully_parsed = true;
  519. }
  520. }
  521. if (fmt == AWS_DATE_FORMAT_RFC822 || (fmt == AWS_DATE_FORMAT_AUTO_DETECT && !successfully_parsed)) {
  522. if (!s_parse_rfc_822(date_str_cursor, &parsed_time, dt)) {
  523. successfully_parsed = true;
  524. if (dt->utc_assumed) {
  525. if (dt->tz[0] == '+' || dt->tz[0] == '-') {
  526. /* in this format, the offset is in format +/-HHMM so convert that to seconds and we'll use
  527. * the offset later. */
  528. char min_str[3] = {0};
  529. char hour_str[3] = {0};
  530. hour_str[0] = dt->tz[1];
  531. hour_str[1] = dt->tz[2];
  532. min_str[0] = dt->tz[3];
  533. min_str[1] = dt->tz[4];
  534. long hour = strtol(hour_str, NULL, 10);
  535. long min = strtol(min_str, NULL, 10);
  536. seconds_offset = (time_t)(hour * 3600 + min * 60);
  537. if (dt->tz[0] == '-') {
  538. seconds_offset = -seconds_offset;
  539. }
  540. }
  541. }
  542. }
  543. }
  544. if (!successfully_parsed) {
  545. return aws_raise_error(AWS_ERROR_INVALID_DATE_STR);
  546. }
  547. if (dt->utc_assumed || seconds_offset) {
  548. dt->timestamp = aws_timegm(&parsed_time);
  549. } else {
  550. dt->timestamp = mktime(&parsed_time);
  551. }
  552. /* negative means we need to move west (increase the timestamp), positive means head east, so decrease the
  553. * timestamp. */
  554. dt->timestamp -= seconds_offset;
  555. dt->milliseconds = 0U;
  556. dt->gmt_time = s_get_time_struct(dt, false);
  557. dt->local_time = s_get_time_struct(dt, true);
  558. return AWS_OP_SUCCESS;
  559. }
  560. int aws_date_time_init_from_str(
  561. struct aws_date_time *dt,
  562. const struct aws_byte_buf *date_str,
  563. enum aws_date_format fmt) {
  564. AWS_ERROR_PRECONDITION(date_str->len <= AWS_DATE_TIME_STR_MAX_LEN, AWS_ERROR_OVERFLOW_DETECTED);
  565. struct aws_byte_cursor date_cursor = aws_byte_cursor_from_buf(date_str);
  566. return aws_date_time_init_from_str_cursor(dt, &date_cursor, fmt);
  567. }
  568. static inline int s_date_to_str(const struct tm *tm, const char *format_str, struct aws_byte_buf *output_buf) {
  569. size_t remaining_space = output_buf->capacity - output_buf->len;
  570. size_t bytes_written = strftime((char *)output_buf->buffer + output_buf->len, remaining_space, format_str, tm);
  571. if (bytes_written == 0) {
  572. return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
  573. }
  574. output_buf->len += bytes_written;
  575. return AWS_OP_SUCCESS;
  576. }
  577. int aws_date_time_to_local_time_str(
  578. const struct aws_date_time *dt,
  579. enum aws_date_format fmt,
  580. struct aws_byte_buf *output_buf) {
  581. AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
  582. switch (fmt) {
  583. case AWS_DATE_FORMAT_RFC822:
  584. return s_date_to_str(&dt->local_time, RFC822_DATE_FORMAT_STR_WITH_Z, output_buf);
  585. case AWS_DATE_FORMAT_ISO_8601:
  586. return s_date_to_str(&dt->local_time, ISO_8601_LONG_DATE_FORMAT_STR, output_buf);
  587. case AWS_DATE_FORMAT_ISO_8601_BASIC:
  588. return s_date_to_str(&dt->local_time, ISO_8601_LONG_BASIC_DATE_FORMAT_STR, output_buf);
  589. default:
  590. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  591. }
  592. }
  593. int aws_date_time_to_utc_time_str(
  594. const struct aws_date_time *dt,
  595. enum aws_date_format fmt,
  596. struct aws_byte_buf *output_buf) {
  597. AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
  598. switch (fmt) {
  599. case AWS_DATE_FORMAT_RFC822:
  600. return s_date_to_str(&dt->gmt_time, RFC822_DATE_FORMAT_STR_MINUS_Z, output_buf);
  601. case AWS_DATE_FORMAT_ISO_8601:
  602. return s_date_to_str(&dt->gmt_time, ISO_8601_LONG_DATE_FORMAT_STR, output_buf);
  603. case AWS_DATE_FORMAT_ISO_8601_BASIC:
  604. return s_date_to_str(&dt->gmt_time, ISO_8601_LONG_BASIC_DATE_FORMAT_STR, output_buf);
  605. default:
  606. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  607. }
  608. }
  609. int aws_date_time_to_local_time_short_str(
  610. const struct aws_date_time *dt,
  611. enum aws_date_format fmt,
  612. struct aws_byte_buf *output_buf) {
  613. AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
  614. switch (fmt) {
  615. case AWS_DATE_FORMAT_RFC822:
  616. return s_date_to_str(&dt->local_time, RFC822_SHORT_DATE_FORMAT_STR, output_buf);
  617. case AWS_DATE_FORMAT_ISO_8601:
  618. return s_date_to_str(&dt->local_time, ISO_8601_SHORT_DATE_FORMAT_STR, output_buf);
  619. case AWS_DATE_FORMAT_ISO_8601_BASIC:
  620. return s_date_to_str(&dt->local_time, ISO_8601_SHORT_BASIC_DATE_FORMAT_STR, output_buf);
  621. default:
  622. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  623. }
  624. }
  625. int aws_date_time_to_utc_time_short_str(
  626. const struct aws_date_time *dt,
  627. enum aws_date_format fmt,
  628. struct aws_byte_buf *output_buf) {
  629. AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
  630. switch (fmt) {
  631. case AWS_DATE_FORMAT_RFC822:
  632. return s_date_to_str(&dt->gmt_time, RFC822_SHORT_DATE_FORMAT_STR, output_buf);
  633. case AWS_DATE_FORMAT_ISO_8601:
  634. return s_date_to_str(&dt->gmt_time, ISO_8601_SHORT_DATE_FORMAT_STR, output_buf);
  635. case AWS_DATE_FORMAT_ISO_8601_BASIC:
  636. return s_date_to_str(&dt->gmt_time, ISO_8601_SHORT_BASIC_DATE_FORMAT_STR, output_buf);
  637. default:
  638. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  639. }
  640. }
  641. double aws_date_time_as_epoch_secs(const struct aws_date_time *dt) {
  642. return (double)dt->timestamp + (double)(dt->milliseconds / 1000.0);
  643. }
  644. uint64_t aws_date_time_as_nanos(const struct aws_date_time *dt) {
  645. return aws_timestamp_convert((uint64_t)dt->timestamp, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL) +
  646. aws_timestamp_convert((uint64_t)dt->milliseconds, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL);
  647. }
  648. uint64_t aws_date_time_as_millis(const struct aws_date_time *dt) {
  649. return aws_timestamp_convert((uint64_t)dt->timestamp, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL) +
  650. (uint64_t)dt->milliseconds;
  651. }
  652. uint16_t aws_date_time_year(const struct aws_date_time *dt, bool local_time) {
  653. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  654. return (uint16_t)(time->tm_year + 1900);
  655. }
  656. enum aws_date_month aws_date_time_month(const struct aws_date_time *dt, bool local_time) {
  657. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  658. return time->tm_mon;
  659. }
  660. uint8_t aws_date_time_month_day(const struct aws_date_time *dt, bool local_time) {
  661. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  662. return (uint8_t)time->tm_mday;
  663. }
  664. enum aws_date_day_of_week aws_date_time_day_of_week(const struct aws_date_time *dt, bool local_time) {
  665. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  666. return time->tm_wday;
  667. }
  668. uint8_t aws_date_time_hour(const struct aws_date_time *dt, bool local_time) {
  669. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  670. return (uint8_t)time->tm_hour;
  671. }
  672. uint8_t aws_date_time_minute(const struct aws_date_time *dt, bool local_time) {
  673. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  674. return (uint8_t)time->tm_min;
  675. }
  676. uint8_t aws_date_time_second(const struct aws_date_time *dt, bool local_time) {
  677. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  678. return (uint8_t)time->tm_sec;
  679. }
  680. bool aws_date_time_dst(const struct aws_date_time *dt, bool local_time) {
  681. const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
  682. return (bool)time->tm_isdst;
  683. }
  684. time_t aws_date_time_diff(const struct aws_date_time *a, const struct aws_date_time *b) {
  685. return a->timestamp - b->timestamp;
  686. }