date.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. tm.yday = 0;
  78. t = tm2sec(&tm);
  79. zone2tm(&tm, flds[2]);
  80. t -= tm.tzoff;
  81. return t;
  82. }
  83. /*
  84. * parse dates of formats
  85. * [Wkd[,]] DD Mon YYYY HH:MM:SS zone
  86. * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY
  87. * plus anything similar
  88. * return nil for a failure
  89. */
  90. Tm*
  91. date2tm(Tm *tm, char *date)
  92. {
  93. Tm gmt, *atm;
  94. char *flds[7], *s, dstr[64];
  95. int n;
  96. /*
  97. * default date is Thu Jan 1 00:00:00 GMT 1970
  98. */
  99. tm->wday = 4;
  100. tm->mday = 1;
  101. tm->mon = 1;
  102. tm->hour = 0;
  103. tm->min = 0;
  104. tm->sec = 0;
  105. tm->year = 70;
  106. strcpy(tm->zone, "GMT");
  107. tm->tzoff = 0;
  108. strncpy(dstr, date, sizeof(dstr));
  109. dstr[sizeof(dstr)-1] = '\0';
  110. n = tokenize(dstr, flds, 7);
  111. if(n != 6 && n != 5)
  112. return nil;
  113. if(n == 5){
  114. for(n = 5; n >= 0; n--)
  115. flds[n] = flds[n - 1];
  116. n = 5;
  117. }else{
  118. /*
  119. * Wday[,]
  120. */
  121. s = strchr(flds[0], ',');
  122. if(s != nil)
  123. *s = '\0';
  124. tm->wday = dateindex(flds[0], wdayname, 7);
  125. if(tm->wday < 0)
  126. return nil;
  127. }
  128. /*
  129. * check for the two major formats:
  130. * Month first or day first
  131. */
  132. tm->mon = dateindex(flds[1], monname, 12);
  133. if(tm->mon >= 0){
  134. tm->mday = strtoul(flds[2], nil, 10);
  135. time2tm(tm, flds[3]);
  136. zone2tm(tm, flds[4]);
  137. tm->year = strtoul(flds[5], nil, 10);
  138. if(strlen(flds[5]) > 2)
  139. tm->year -= 1900;
  140. }else{
  141. tm->mday = strtoul(flds[1], nil, 10);
  142. tm->mon = dateindex(flds[2], monname, 12);
  143. tm->year = strtoul(flds[3], nil, 10);
  144. if(strlen(flds[3]) > 2)
  145. tm->year -= 1900;
  146. time2tm(tm, flds[4]);
  147. zone2tm(tm, flds[5]);
  148. }
  149. if(n == 5){
  150. gmt = *tm;
  151. strncpy(gmt.zone, "", 4);
  152. gmt.tzoff = 0;
  153. atm = gmtime(tm2sec(&gmt));
  154. tm->wday = atm->wday;
  155. }else{
  156. /*
  157. * Wday[,]
  158. */
  159. s = strchr(flds[0], ',');
  160. if(s != nil)
  161. *s = '\0';
  162. tm->wday = dateindex(flds[0], wdayname, 7);
  163. if(tm->wday < 0)
  164. return nil;
  165. }
  166. return tm;
  167. }
  168. /*
  169. * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names
  170. * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong.
  171. * | "UT" universal time
  172. * | [+-][0-9][0-9][0-9][0-9]
  173. * zones is the rfc-822 list of time zone names
  174. */
  175. static NamedInt zones[] =
  176. {
  177. {"A", -1 * 3600},
  178. {"B", -2 * 3600},
  179. {"C", -3 * 3600},
  180. {"CDT", -5 * 3600},
  181. {"CST", -6 * 3600},
  182. {"D", -4 * 3600},
  183. {"E", -5 * 3600},
  184. {"EDT", -4 * 3600},
  185. {"EST", -5 * 3600},
  186. {"F", -6 * 3600},
  187. {"G", -7 * 3600},
  188. {"GMT", 0},
  189. {"H", -8 * 3600},
  190. {"I", -9 * 3600},
  191. {"K", -10 * 3600},
  192. {"L", -11 * 3600},
  193. {"M", -12 * 3600},
  194. {"MDT", -6 * 3600},
  195. {"MST", -7 * 3600},
  196. {"N", +1 * 3600},
  197. {"O", +2 * 3600},
  198. {"P", +3 * 3600},
  199. {"PDT", -7 * 3600},
  200. {"PST", -8 * 3600},
  201. {"Q", +4 * 3600},
  202. {"R", +5 * 3600},
  203. {"S", +6 * 3600},
  204. {"T", +7 * 3600},
  205. {"U", +8 * 3600},
  206. {"UT", 0},
  207. {"V", +9 * 3600},
  208. {"W", +10 * 3600},
  209. {"X", +11 * 3600},
  210. {"Y", +12 * 3600},
  211. {"Z", 0},
  212. {nil, 0}
  213. };
  214. static void
  215. zone2tm(Tm *tm, char *s)
  216. {
  217. Tm aux, *atm;
  218. int i;
  219. if(*s == '+' || *s == '-'){
  220. i = strtol(s, &s, 10);
  221. tm->tzoff = (i / 100) * 3600 + i % 100;
  222. strncpy(tm->zone, "", 4);
  223. return;
  224. }
  225. /*
  226. * look it up in the standard rfc822 table
  227. */
  228. strncpy(tm->zone, s, 3);
  229. tm->zone[3] = '\0';
  230. tm->tzoff = 0;
  231. for(i = 0; zones[i].name != nil; i++){
  232. if(cistrcmp(zones[i].name, s) == 0){
  233. tm->tzoff = zones[i].v;
  234. return;
  235. }
  236. }
  237. /*
  238. * one last try: look it up in the current local timezone
  239. * probe a couple of times to get daylight/standard time change.
  240. */
  241. aux = *tm;
  242. memset(aux.zone, 0, 4);
  243. aux.hour--;
  244. for(i = 0; i < 2; i++){
  245. atm = localtime(tm2sec(&aux));
  246. if(cistrcmp(tm->zone, atm->zone) == 0){
  247. tm->tzoff = atm->tzoff;
  248. return;
  249. }
  250. aux.hour++;
  251. }
  252. strncpy(tm->zone, "GMT", 4);
  253. tm->tzoff = 0;
  254. }
  255. /*
  256. * hh[:mm[:ss]]
  257. */
  258. static void
  259. time2tm(Tm *tm, char *s)
  260. {
  261. tm->hour = strtoul(s, &s, 10);
  262. if(*s++ != ':')
  263. return;
  264. tm->min = strtoul(s, &s, 10);
  265. if(*s++ != ':')
  266. return;
  267. tm->sec = strtoul(s, &s, 10);
  268. }
  269. static int
  270. dateindex(char *d, char **tab, int n)
  271. {
  272. int i;
  273. for(i = 0; i < n; i++)
  274. if(cistrcmp(d, tab[i]) == 0)
  275. return i;
  276. return -1;
  277. }