psdate.c revision 20253
1/*- 2 * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer as 10 * the first lines of this file unmodified. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by David L. Nugent. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id$ 33 */ 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <ctype.h> 39 40#include "psdate.h" 41 42 43static int 44a2i(char const ** str) 45{ 46 int i = 0; 47 char const *s = *str; 48 49 if (isdigit(*s)) { 50 i = atoi(s); 51 while (isdigit(*s)) 52 ++s; 53 *str = s; 54 } 55 return i; 56} 57 58static int 59numerics(char const * str) 60{ 61 int rc = isdigit(*str); 62 63 if (rc) 64 while (isdigit(*str) || *str == 'x') 65 ++str; 66 return rc && !*str; 67} 68 69static int 70aindex(char const * arr[], char const ** str, int len) 71{ 72 int l, i; 73 char mystr[32]; 74 75 mystr[len] = '\0'; 76 l = strlen(strncpy(mystr, *str, len)); 77 for (i = 0; i < l; i++) 78 mystr[i] = (char) tolower(mystr[i]); 79 for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++); 80 if (arr[i] == NULL) 81 i = -1; 82 else { /* Skip past it */ 83 while (**str && isalpha(**str)) 84 ++(*str); 85 /* And any following whitespace */ 86 while (**str && (**str == ',' || isspace(**str))) 87 ++(*str); 88 } /* Return index */ 89 return i; 90} 91 92static int 93weekday(char const ** str) 94{ 95 static char const *days[] = 96 {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL}; 97 98 return aindex(days, str, 3); 99} 100 101static int 102month(char const ** str) 103{ 104 static char const *months[] = 105 {"jan", "feb", "mar", "apr", "may", "jun", "jul", 106 "aug", "sep", "oct", "nov", "dec", NULL}; 107 108 return aindex(months, str, 3); 109} 110 111static void 112parse_time(char const * str, int *hour, int *min, int *sec) 113{ 114 *hour = a2i(&str); 115 if ((str = strchr(str, ':')) == NULL) 116 *min = *sec = 0; 117 else { 118 ++str; 119 *min = a2i(&str); 120 *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str); 121 } 122} 123 124 125static void 126parse_datesub(char const * str, int *day, int *mon, int *year) 127{ 128 int i; 129 130 static char const nchrs[] = "0123456789 \t,/-."; 131 132 if ((i = month(&str)) != -1) { 133 *mon = i; 134 if ((i = a2i(&str)) != 0) 135 *day = i; 136 } else if ((i = a2i(&str)) != 0) { 137 *day = i; 138 while (*str && strchr(nchrs + 10, *str) != NULL) 139 ++str; 140 if ((i = month(&str)) != -1) 141 *mon = i; 142 else if ((i = a2i(&str)) != 0) 143 *mon = i - 1; 144 } else 145 return; 146 147 while (*str && strchr(nchrs + 10, *str) != NULL) 148 ++str; 149 if (isdigit(*str)) { 150 *year = atoi(str); 151 if (*year > 1900) 152 *year -= 1900; 153 else if (*year < 32) 154 *year += 100; 155 } 156} 157 158 159/*- 160 * Parse time must be flexible, it handles the following formats: 161 * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now 162 * 0xnnnnnnnn UNIX timestamp in hexadecimal 163 * 0nnnnnnnnn UNIX timestamp in octal 164 * 0 Given time 165 * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years 166 * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years 167 * dd[ ./-]mmm[ ./-]yy Date } 168 * hh:mm:ss Time } May be combined 169 */ 170 171time_t 172parse_date(time_t dt, char const * str) 173{ 174 char *p; 175 int i; 176 long val; 177 struct tm *T; 178 179 if (dt == 0) 180 dt = time(NULL); 181 182 while (*str && isspace(*str)) 183 ++str; 184 185 if (numerics(str)) { 186 val = strtol(str, &p, 0); 187 dt = val ? val : dt; 188 } else if (*str == '+' || *str == '-') { 189 val = strtol(str, &p, 0); 190 switch (*p) { 191 case 'h': 192 case 'H': /* hours */ 193 dt += (val * 3600L); 194 break; 195 case '\0': 196 case 'm': 197 case 'M': /* minutes */ 198 dt += (val * 60L); 199 break; 200 case 's': 201 case 'S': /* seconds */ 202 dt += val; 203 break; 204 case 'd': 205 case 'D': /* days */ 206 dt += (val * 86400L); 207 break; 208 case 'w': 209 case 'W': /* weeks */ 210 dt += (val * 604800L); 211 break; 212 case 'o': 213 case 'O': /* months */ 214 T = localtime(&dt); 215 T->tm_mon += (int) val; 216 i = T->tm_mday; 217 goto fixday; 218 case 'y': 219 case 'Y': /* years */ 220 T = localtime(&dt); 221 T->tm_year += (int) val; 222 i = T->tm_mday; 223 fixday: 224 dt = mktime(T); 225 T = localtime(&dt); 226 if (T->tm_mday != i) { 227 T->tm_mday = 1; 228 dt = mktime(T); 229 dt -= (time_t) 86400L; 230 } 231 default: /* unknown */ 232 break; /* leave untouched */ 233 } 234 } else { 235 char *q, tmp[64]; 236 237 /* 238 * Skip past any weekday prefix 239 */ 240 weekday(&str); 241 str = strncpy(tmp, str, sizeof tmp - 1); 242 tmp[sizeof tmp - 1] = '\0'; 243 T = localtime(&dt); 244 245 /* 246 * See if we can break off any timezone 247 */ 248 while ((q = strrchr(tmp, ' ')) != NULL) { 249 if (strchr("(+-", q[1]) != NULL) 250 *q = '\0'; 251 else { 252 int j = 1; 253 254 while (q[j] && isupper(q[j])) 255 ++j; 256 if (q[j] == '\0') 257 *q = '\0'; 258 else 259 break; 260 } 261 } 262 263 /* 264 * See if there is a time hh:mm[:ss] 265 */ 266 if ((p = strchr(tmp, ':')) == NULL) { 267 268 /* 269 * No time string involved 270 */ 271 T->tm_hour = T->tm_min = T->tm_sec = 0; 272 parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year); 273 } else { 274 char datestr[64], timestr[64]; 275 276 /* 277 * Let's chip off the time string 278 */ 279 if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */ 280 int l = q - str; 281 282 strncpy(timestr, str, l); 283 timestr[l] = '\0'; 284 strncpy(datestr, q + 1, sizeof datestr); 285 datestr[sizeof datestr - 1] = '\0'; 286 parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec); 287 parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year); 288 } else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */ 289 int l = q - tmp; 290 291 strncpy(timestr, q + 1, sizeof timestr); 292 timestr[sizeof timestr - 1] = '\0'; 293 strncpy(datestr, tmp, l); 294 datestr[l] = '\0'; 295 } else /* Bail out */ 296 return dt; 297 parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec); 298 parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year); 299 } 300 dt = mktime(T); 301 } 302 return dt; 303} 304