PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/lib/timestamp.c

https://github.com/Haxe/gtk-gnutella
C | 399 lines | 207 code | 58 blank | 134 comment | 38 complexity | 51b88e6682f1dd490f03ed4c85af710e MD5 | raw file
  1. /*
  2. * Copyright (c) 2009-2010, Raphael Manfredi
  3. * Copyright (c) 2006-2008, Christian Biere
  4. *
  5. *----------------------------------------------------------------------
  6. * This file is part of gtk-gnutella.
  7. *
  8. * gtk-gnutella is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * gtk-gnutella is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with gtk-gnutella; if not, write to the Free Software
  20. * Foundation, Inc.:
  21. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22. *----------------------------------------------------------------------
  23. */
  24. /**
  25. * @ingroup lib
  26. * @file
  27. *
  28. * Timestamp functions.
  29. *
  30. * @author Raphael Manfredi
  31. * @date 2009-2010
  32. * @author Christian Biere
  33. * @date 2006-2008
  34. */
  35. #include "common.h"
  36. #include "timestamp.h"
  37. #include "ascii.h"
  38. #include "offtime.h"
  39. #include "parse.h"
  40. #include "stringify.h"
  41. #include "glib-missing.h"
  42. #include "override.h" /* Must be the last header included */
  43. /**
  44. * Compute the difference in seconds between two tm structs (a - b).
  45. * Comes from glibc-2.2.5.
  46. */
  47. time_delta_t
  48. diff_tm(const struct tm *a, const struct tm * b)
  49. {
  50. /*
  51. * Compute intervening leap days correctly even if year is negative.
  52. * Take care to avoid int overflow in leap day calculations,
  53. * but it's OK to assume that A and B are close to each other.
  54. */
  55. int a4 = (a->tm_year >> 2) + (TM_YEAR_ORIGIN >> 2) - ! (a->tm_year & 3);
  56. int b4 = (b->tm_year >> 2) + (TM_YEAR_ORIGIN >> 2) - ! (b->tm_year & 3);
  57. int a100 = a4 / 25 - (a4 % 25 < 0);
  58. int b100 = b4 / 25 - (b4 % 25 < 0);
  59. int a400 = a100 >> 2;
  60. int b400 = b100 >> 2;
  61. int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
  62. int years = a->tm_year - b->tm_year;
  63. int days = (365 * years + intervening_leap_days
  64. + (a->tm_yday - b->tm_yday));
  65. return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
  66. + (a->tm_min - b->tm_min))
  67. + (a->tm_sec - b->tm_sec));
  68. }
  69. static const char days[7][4] =
  70. { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  71. static const char months[12][4] = {
  72. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  73. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
  74. };
  75. /**
  76. * Convert time to ISO 8601 date plus time, e.g. "2002-06-09 14:54:42Z".
  77. *
  78. * @return The length of the created string.
  79. */
  80. size_t
  81. timestamp_utc_to_string_buf(time_t date, char *dst, size_t size)
  82. {
  83. const struct tm *tm;
  84. size_t len;
  85. g_assert(size > 0);
  86. tm = gmtime(&date);
  87. len = strftime(dst, size, "%Y-%m-%d %H:%M:%SZ", tm);
  88. dst[len] = '\0'; /* Be really sure */
  89. return len;
  90. }
  91. /**
  92. * Convert time to an ISO 8601 timestamp, e.g. "2002-06-09T14:54:42Z".
  93. *
  94. * @return pointer to static data.
  95. */
  96. const char *
  97. timestamp_utc_to_string(time_t date)
  98. {
  99. static char buf[32];
  100. timestamp_utc_to_string_buf(date, buf, sizeof buf);
  101. return buf;
  102. }
  103. /**
  104. * Convert time to an ISO 8601 timestamp, e.g. "2002-06-09T14:54:42Z".
  105. *
  106. * @return pointer to static data.
  107. */
  108. const char *
  109. timestamp_utc_to_string2(time_t date)
  110. {
  111. static char buf[32];
  112. timestamp_utc_to_string_buf(date, buf, sizeof buf);
  113. return buf;
  114. }
  115. /**
  116. * Convert time to ISO 8601 date plus time, e.g. "2002-06-09 14:54:42".
  117. *
  118. * @return The length of the created string.
  119. */
  120. size_t
  121. timestamp_to_string_buf(time_t date, char *dst, size_t size)
  122. {
  123. const struct tm *tm;
  124. size_t len;
  125. g_assert(size > 0);
  126. tm = localtime(&date);
  127. len = strftime(dst, size, "%Y-%m-%d %H:%M:%S", tm);
  128. dst[len] = '\0'; /* Be really sure */
  129. return len;
  130. }
  131. short_string_t
  132. timestamp_get_string(time_t date)
  133. {
  134. short_string_t buf;
  135. timestamp_to_string_buf(date, buf.str, sizeof buf.str);
  136. return buf;
  137. }
  138. /**
  139. * Convert time to ISO 8601 date plus time, e.g. "2005-11-10 20:21:57".
  140. *
  141. * @return pointer to static data.
  142. */
  143. const char *
  144. timestamp_to_string(time_t date)
  145. {
  146. static char buf[TIMESTAMP_BUF_LEN];
  147. timestamp_to_string_buf(date, buf, sizeof buf);
  148. return buf;
  149. }
  150. /**
  151. * Compute offset of local timezone to GMT, in seconds.
  152. *
  153. * @param date the current timestamp for which we want the offset
  154. * @param tm_ptr if non-NULL, written with the localtime() struct.
  155. */
  156. time_delta_t
  157. timestamp_gmt_offset(time_t date, struct tm **tm_ptr)
  158. {
  159. struct tm *tm;
  160. struct tm gmt_tm;
  161. tm = gmtime(&date);
  162. gmt_tm = *tm; /* struct copy */
  163. tm = localtime(&date);
  164. if (tm_ptr != NULL)
  165. *tm_ptr = tm;
  166. return diff_tm(tm, &gmt_tm); /* in seconds */
  167. }
  168. /**
  169. * Convert time to RFC-822 style date, into supplied string buffer.
  170. *
  171. * @param date The timestamp.
  172. * @param buf The destination buffer to hold the resulting string. Must be
  173. * greater than zero.
  174. * @param size The size of of "buf".
  175. * @return The length of the created string.
  176. */
  177. static size_t
  178. timestamp_rfc822_to_string_buf(time_t date, char *buf, size_t size)
  179. {
  180. struct tm *tm;
  181. int gmt_off;
  182. char sign;
  183. g_assert(size > 0);
  184. /*
  185. * We used to do:
  186. *
  187. * strftime(buf, len, "%a, %d %b %Y %H:%M:%S %z", tm);
  188. *
  189. * but doing both:
  190. *
  191. * putenv("LC_TIME=C");
  192. * setlocale(LC_TIME, "C");
  193. *
  194. * did not seem to force that routine to emit English. Let's do it
  195. * ourselves.
  196. *
  197. * We also used to rely on strftime()'s "%z" to compute the GMT offset,
  198. * but this is GNU-specific.
  199. */
  200. gmt_off = timestamp_gmt_offset(date, &tm) / 60; /* in minutes */
  201. if (gmt_off < 0) {
  202. sign = '-';
  203. gmt_off = -gmt_off;
  204. } else
  205. sign = '+';
  206. return gm_snprintf(buf, size, "%s, %02d %s %04d %02d:%02d:%02d %c%04d",
  207. days[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900,
  208. tm->tm_hour, tm->tm_min, tm->tm_sec,
  209. sign, gmt_off / 60 * 100 + gmt_off % 60);
  210. }
  211. /**
  212. * Convert time to RFC-822 style date.
  213. *
  214. * @return pointer to static data.
  215. */
  216. const char *
  217. timestamp_rfc822_to_string(time_t date)
  218. {
  219. static char buf[80];
  220. timestamp_rfc822_to_string_buf(date, buf, sizeof buf);
  221. return buf;
  222. }
  223. /**
  224. * Same as date_to_rfc822_gchar(), to be able to use the two in the same
  225. * printf() line.
  226. */
  227. const char *
  228. timestamp_rfc822_to_string2(time_t date)
  229. {
  230. static char buf[80];
  231. timestamp_rfc822_to_string_buf(date, buf, sizeof buf);
  232. return buf;
  233. }
  234. /**
  235. * Convert time to RFC-1123 style date, into supplied string buffer.
  236. *
  237. * @param date The timestamp.
  238. * @param buf The destination buffer to hold the resulting string. Must be
  239. * greater than zero.
  240. * @param size The size of of "buf".
  241. * @return The length of the created string.
  242. */
  243. static size_t
  244. timestamp_rfc1123_to_string_buf(time_t date, char *buf, size_t size)
  245. {
  246. const struct tm *tm;
  247. g_assert(size > 0);
  248. tm = gmtime(&date);
  249. return gm_snprintf(buf, size, "%s, %02d %s %04d %02d:%02d:%02d GMT",
  250. days[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900,
  251. tm->tm_hour, tm->tm_min, tm->tm_sec);
  252. }
  253. /**
  254. * Convert time to RFC-1123 style date.
  255. *
  256. * @returns pointer to static data.
  257. */
  258. const char *
  259. timestamp_rfc1123_to_string(time_t date)
  260. {
  261. static char buf[80];
  262. timestamp_rfc1123_to_string_buf(date, buf, sizeof buf);
  263. return buf;
  264. }
  265. /**
  266. * Parse an ISO 8601 UTC timestamp, e.g. "2002-06-09T14:54:42Z", converting
  267. * it to a time_t. The middle 'T' can be a ' ' (space) and the trailing 'Z'
  268. * is optional, so we can parse "2002-06-09 14:54:42" equally well.
  269. *
  270. * @return TRUE if we parsed the string correctly, FALSE if it did not
  271. * look like a valid ISO timestamp.
  272. *
  273. * @attention
  274. * The date is returned in ``stamp'' as local time, not UTC time.
  275. */
  276. gboolean
  277. string_to_timestamp_utc(const char *str, const char **endptr, time_t *stamp)
  278. {
  279. const char *ep;
  280. struct tm tm;
  281. int error;
  282. ep = skip_ascii_spaces(str);
  283. tm.tm_year = parse_uint16(str, &ep, 10, &error);
  284. if (error)
  285. return FALSE;
  286. if (tm.tm_year < TM_YEAR_ORIGIN)
  287. return FALSE;
  288. tm.tm_year -= TM_YEAR_ORIGIN;
  289. if (*ep++ != '-')
  290. return FALSE;
  291. tm.tm_mon = parse_uint8(ep, &ep, 10, &error);
  292. if (error)
  293. return FALSE;
  294. if (tm.tm_mon < 1 || tm.tm_mon > 12)
  295. return FALSE;
  296. tm.tm_mon--;
  297. if (*ep++ != '-')
  298. return FALSE;
  299. tm.tm_mday = parse_uint8(ep, &ep, 10, &error);
  300. if (error)
  301. return FALSE;
  302. if (tm.tm_mday < 1 || tm.tm_mday > 31)
  303. return FALSE;
  304. if (*ep != ' ' && *ep != 'T')
  305. return FALSE;
  306. ep++;
  307. tm.tm_hour = parse_uint8(ep, &ep, 10, &error);
  308. if (error)
  309. return FALSE;
  310. if (tm.tm_hour < 0 || tm.tm_hour > 23)
  311. return FALSE;
  312. if (*ep++ != ':')
  313. return FALSE;
  314. tm.tm_min = parse_uint8(ep, &ep, 10, &error);
  315. if (error)
  316. return FALSE;
  317. if (tm.tm_min < 0 || tm.tm_min > 59)
  318. return FALSE;
  319. if (*ep++ != ':')
  320. return FALSE;
  321. tm.tm_sec = parse_uint8(ep, &ep, 10, &error);
  322. if (error)
  323. return FALSE;
  324. if (tm.tm_sec < 0 || tm.tm_sec > 59)
  325. return FALSE;
  326. if (*ep == 'Z')
  327. ep++;
  328. if (endptr != NULL)
  329. *endptr = ep;
  330. if (stamp != NULL) {
  331. time_t date;
  332. time_delta_t gmt_off;
  333. tm.tm_isdst = -1;
  334. tm.tm_yday = tm.tm_wday = 0;
  335. date = mktime(&tm); /* UTC */
  336. gmt_off = timestamp_gmt_offset(date, NULL);
  337. *stamp = date + gmt_off; /* Local time */
  338. }
  339. return TRUE;
  340. }
  341. /* vi: set ts=4 sw=4 cindent: */