date.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <auth.h>
  5. #include "imap4d.h"
  6. char *
  7. wdayname[7] =
  8. {
  9. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  10. };
  11. char *
  12. monname[12] =
  13. {
  14. "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  15. };
  16. static void time2tm(Tm *tm, char *s);
  17. static void zone2tm(Tm *tm, char *s);
  18. static int dateindex(char *d, char **tab, int n);
  19. int
  20. rfc822date(char *s, int n, Tm *tm)
  21. {
  22. char *plus;
  23. int m;
  24. plus = "+";
  25. if(tm->tzoff < 0)
  26. plus = "";
  27. m = 0;
  28. if(tm->wday != 8){
  29. m = snprint(s, n, "%s, ", wdayname[tm->wday]);
  30. if(m < 0)
  31. return m;
  32. }
  33. return snprint(s+m, n-m, "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d",
  34. tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec,
  35. plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
  36. }
  37. int
  38. imap4date(char *s, int n, Tm *tm)
  39. {
  40. char *plus;
  41. plus = "+";
  42. if(tm->tzoff < 0)
  43. plus = "";
  44. return snprint(s, n, "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d",
  45. tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
  46. }
  47. int
  48. imap4Date(Tm *tm, char *date)
  49. {
  50. char *flds[4];
  51. if(getfields(date, flds, 3, 0, "-") != 3)
  52. return 0;
  53. tm->mday = strtol(flds[0], nil, 10);
  54. tm->mon = dateindex(flds[1], monname, 12);
  55. tm->year = strtol(flds[2], nil, 10) - 1900;
  56. return 1;
  57. }
  58. /*
  59. * parse imap4 dates
  60. */
  61. ulong
  62. imap4DateTime(char *date)
  63. {
  64. Tm tm;
  65. char *flds[4], *sflds[4];
  66. ulong t;
  67. if(getfields(date, flds, 4, 0, " ") != 3)
  68. return ~0;
  69. if(!imap4Date(&tm, flds[0]))
  70. return ~0;
  71. if(getfields(flds[1], sflds, 3, 0, ":") != 3)
  72. return ~0;
  73. tm.hour = strtol(sflds[0], nil, 10);
  74. tm.min = strtol(sflds[1], nil, 10);
  75. tm.sec = strtol(sflds[2], nil, 10);
  76. strcpy(tm.zone, "GMT");
  77. t = tm2sec(&tm);
  78. zone2tm(&tm, flds[2]);
  79. t -= tm.tzoff;
  80. return t;
  81. }
  82. /*
  83. * parse dates of formats
  84. * [Wkd[,]] DD Mon YYYY HH:MM:SS zone
  85. * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY
  86. * plus anything similar
  87. * return nil for a failure
  88. */
  89. Tm*
  90. date2tm(Tm *tm, char *date)
  91. {
  92. Tm gmt, *atm;
  93. char *flds[7], *s, dstr[64];
  94. int n;
  95. /*
  96. * default date is Thu Jan 1 00:00:00 GMT 1970
  97. */
  98. tm->wday = 4;
  99. tm->mday = 1;
  100. tm->mon = 1;
  101. tm->hour = 0;
  102. tm->min = 0;
  103. tm->sec = 0;
  104. tm->year = 70;
  105. strcpy(tm->zone, "GMT");
  106. tm->tzoff = 0;
  107. strncpy(dstr, date, sizeof(dstr));
  108. dstr[sizeof(dstr)-1] = '\0';
  109. n = tokenize(dstr, flds, 7);
  110. if(n != 6 && n != 5)
  111. return nil;
  112. if(n == 5){
  113. for(n = 5; n >= 0; n--)
  114. flds[n] = flds[n - 1];
  115. n = 5;
  116. }else{
  117. /*
  118. * Wday[,]
  119. */
  120. s = strchr(flds[0], ',');
  121. if(s != nil)
  122. *s = '\0';
  123. tm->wday = dateindex(flds[0], wdayname, 7);
  124. if(tm->wday < 0)
  125. return nil;
  126. }
  127. /*
  128. * check for the two major formats:
  129. * Month first or day first
  130. */
  131. tm->mon = dateindex(flds[1], monname, 12);
  132. if(tm->mon >= 0){
  133. tm->mday = strtoul(flds[2], nil, 10);
  134. time2tm(tm, flds[3]);
  135. zone2tm(tm, flds[4]);
  136. tm->year = strtoul(flds[5], nil, 10);
  137. if(strlen(flds[5]) > 2)
  138. tm->year -= 1900;
  139. }else{
  140. tm->mday = strtoul(flds[1], nil, 10);
  141. tm->mon = dateindex(flds[2], monname, 12);
  142. tm->year = strtoul(flds[3], nil, 10);
  143. if(strlen(flds[3]) > 2)
  144. tm->year -= 1900;
  145. time2tm(tm, flds[4]);
  146. zone2tm(tm, flds[5]);
  147. }
  148. if(n == 5){
  149. gmt = *tm;
  150. strncpy(gmt.zone, "", 4);
  151. gmt.tzoff = 0;
  152. atm = gmtime(tm2sec(&gmt));
  153. tm->wday = atm->wday;
  154. }else{
  155. /*
  156. * Wday[,]
  157. */
  158. s = strchr(flds[0], ',');
  159. if(s != nil)
  160. *s = '\0';
  161. tm->wday = dateindex(flds[0], wdayname, 7);
  162. if(tm->wday < 0)
  163. return nil;
  164. }
  165. return tm;
  166. }
  167. /*
  168. * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names
  169. * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong.
  170. * | "UT" universal time
  171. * | [+-][0-9][0-9][0-9][0-9]
  172. * zones is the rfc-822 list of time zone names
  173. */
  174. static NamedInt zones[] =
  175. {
  176. {"A", -1 * 3600},
  177. {"B", -2 * 3600},
  178. {"C", -3 * 3600},
  179. {"CDT", -5 * 3600},
  180. {"CST", -6 * 3600},
  181. {"D", -4 * 3600},
  182. {"E", -5 * 3600},
  183. {"EDT", -4 * 3600},
  184. {"EST", -5 * 3600},
  185. {"F", -6 * 3600},
  186. {"G", -7 * 3600},
  187. {"GMT", 0},
  188. {"H", -8 * 3600},
  189. {"I", -9 * 3600},
  190. {"K", -10 * 3600},
  191. {"L", -11 * 3600},
  192. {"M", -12 * 3600},
  193. {"MDT", -6 * 3600},
  194. {"MST", -7 * 3600},
  195. {"N", +1 * 3600},
  196. {"O", +2 * 3600},
  197. {"P", +3 * 3600},
  198. {"PDT", -7 * 3600},
  199. {"PST", -8 * 3600},
  200. {"Q", +4 * 3600},
  201. {"R", +5 * 3600},
  202. {"S", +6 * 3600},
  203. {"T", +7 * 3600},
  204. {"U", +8 * 3600},
  205. {"UT", 0},
  206. {"V", +9 * 3600},
  207. {"W", +10 * 3600},
  208. {"X", +11 * 3600},
  209. {"Y", +12 * 3600},
  210. {"Z", 0},
  211. {nil, 0}
  212. };
  213. static void
  214. zone2tm(Tm *tm, char *s)
  215. {
  216. Tm aux, *atm;
  217. int i;
  218. if(*s == '+' || *s == '-'){
  219. i = strtol(s, &s, 10);
  220. tm->tzoff = (i / 100) * 3600 + i % 100;
  221. strncpy(tm->zone, "", 4);
  222. return;
  223. }
  224. /*
  225. * look it up in the standard rfc822 table
  226. */
  227. strncpy(tm->zone, s, 3);
  228. tm->zone[3] = '\0';
  229. tm->tzoff = 0;
  230. for(i = 0; zones[i].name != nil; i++){
  231. if(cistrcmp(zones[i].name, s) == 0){
  232. tm->tzoff = zones[i].v;
  233. return;
  234. }
  235. }
  236. /*
  237. * one last try: look it up in the current local timezone
  238. * probe a couple of times to get daylight/standard time change.
  239. */
  240. aux = *tm;
  241. memset(aux.zone, 0, 4);
  242. aux.hour--;
  243. for(i = 0; i < 2; i++){
  244. atm = localtime(tm2sec(&aux));
  245. if(cistrcmp(tm->zone, atm->zone) == 0){
  246. tm->tzoff = atm->tzoff;
  247. return;
  248. }
  249. aux.hour++;
  250. }
  251. strncpy(tm->zone, "GMT", 4);
  252. tm->tzoff = 0;
  253. }
  254. /*
  255. * hh[:mm[:ss]]
  256. */
  257. static void
  258. time2tm(Tm *tm, char *s)
  259. {
  260. tm->hour = strtoul(s, &s, 10);
  261. if(*s++ != ':')
  262. return;
  263. tm->min = strtoul(s, &s, 10);
  264. if(*s++ != ':')
  265. return;
  266. tm->sec = strtoul(s, &s, 10);
  267. }
  268. static int
  269. dateindex(char *d, char **tab, int n)
  270. {
  271. int i;
  272. for(i = 0; i < n; i++)
  273. if(cistrcmp(d, tab[i]) == 0)
  274. return i;
  275. return -1;
  276. }