Use libiso8601 to parse the time.
This commit is contained in:
		
							parent
							
								
									7d6be54038
								
							
						
					
					
						commit
						6195451899
					
				
							
								
								
									
										4
									
								
								README
								
								
								
								
							
							
						
						
									
										4
									
								
								README
								
								
								
								
							| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
mini-at
 | 
			
		||||
mini-at                      http://www.lwithers.me.uk/projects/mini-at/
 | 
			
		||||
========================================================================
 | 
			
		||||
(c)2006, Laurence Withers, <l@lwithers.me.uk>.
 | 
			
		||||
Released under the GNU GPLv2. See file COPYING or 
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +7,8 @@ http://www.gnu.org/copyleft/gpl.html for details.
 | 
			
		|||
Really Quick Instructions
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
Install libiso8601 from http://www.lwithers.me.uk/projects/libiso8601/ .
 | 
			
		||||
 | 
			
		||||
To build: ./make.sh
 | 
			
		||||
To install: ./make.sh install
 | 
			
		||||
    (you might want to set PREFIX, by default it's /usr/local)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,7 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/un.h>
 | 
			
		||||
#include <iso8601.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,8 @@
 | 
			
		|||
if [ -z ${mini_at_BUILT} ]
 | 
			
		||||
then
 | 
			
		||||
    mini_at="obj/mini-at"
 | 
			
		||||
    EXTRAS="" # @TODO@ cflags, libs
 | 
			
		||||
    [ -z "${LIBISO8601_FLAGS}" ] && LIBISO8601_FLAGS="$(pkg-config libiso8601 --libs --cflags)"
 | 
			
		||||
    EXTRAS="${LIBISO8601_FLAGS}"
 | 
			
		||||
 | 
			
		||||
    echo "Building application ${mini_at}..."
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,426 +7,18 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int parse_time_digs(const char* str, int numdigs)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    char ch;
 | 
			
		||||
 | 
			
		||||
    while(numdigs--) {
 | 
			
		||||
        switch( (ch = *str++) ) {
 | 
			
		||||
        case '0' ... '9':
 | 
			
		||||
            i *= 10;
 | 
			
		||||
            i += ch - '0';
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int parse_time_date_daysinyear(int year)
 | 
			
		||||
{
 | 
			
		||||
    if(!(year % 400)) return 366;
 | 
			
		||||
    if(!(year % 100)) return 365;
 | 
			
		||||
    if(!(year % 4)) return 366;
 | 
			
		||||
    return 365;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct parse_time_date_dayselapsed_t {
 | 
			
		||||
    int ndays, elapsed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct parse_time_date_dayselapsed_t parse_time_date_dayselapsed_cal[] = {
 | 
			
		||||
    { 31, 0 },
 | 
			
		||||
    { 28, 31 },
 | 
			
		||||
    { 31, 59 },
 | 
			
		||||
    { 30, 90 },
 | 
			
		||||
    { 31, 120 },
 | 
			
		||||
    { 30, 151 },
 | 
			
		||||
    { 31, 181 },
 | 
			
		||||
    { 31, 212 },
 | 
			
		||||
    { 30, 243 },
 | 
			
		||||
    { 31, 273 },
 | 
			
		||||
    { 30, 304 },
 | 
			
		||||
    { 31, 334 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct parse_time_date_dayselapsed_t parse_time_date_dayselapsed_leapcal[] = {
 | 
			
		||||
    { 31, 0 },
 | 
			
		||||
    { 29, 31 },
 | 
			
		||||
    { 31, 60 },
 | 
			
		||||
    { 30, 91 },
 | 
			
		||||
    { 31, 121 },
 | 
			
		||||
    { 30, 152 },
 | 
			
		||||
    { 31, 182 },
 | 
			
		||||
    { 31, 213 },
 | 
			
		||||
    { 30, 244 },
 | 
			
		||||
    { 31, 274 },
 | 
			
		||||
    { 30, 305 },
 | 
			
		||||
    { 31, 335 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int parse_time_date_dayselapsed(int year, int mon, int day)
 | 
			
		||||
{
 | 
			
		||||
    struct parse_time_date_dayselapsed_t* t;
 | 
			
		||||
 | 
			
		||||
    --mon;
 | 
			
		||||
    if(mon < 0 || mon > 11) return -1;
 | 
			
		||||
 | 
			
		||||
    if(parse_time_date_daysinyear(year) == 366) t = parse_time_date_dayselapsed_leapcal;
 | 
			
		||||
    else t = parse_time_date_dayselapsed_cal;
 | 
			
		||||
 | 
			
		||||
    t += mon;
 | 
			
		||||
    --day;
 | 
			
		||||
    if(day < 0 || day > t->ndays) return -1;
 | 
			
		||||
 | 
			
		||||
    return day + t->elapsed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// number of seconds elapsed between 1970 and start of `year'
 | 
			
		||||
time_t parse_time_date_mkyear(int year)
 | 
			
		||||
{
 | 
			
		||||
    long nd;
 | 
			
		||||
 | 
			
		||||
    // we have an inverse y2k problem!
 | 
			
		||||
    //  basically, the algorithm needs to operate on a 400-year boundary for leap days
 | 
			
		||||
    //  since this is `at', we don't deal with dates in the past
 | 
			
		||||
    //  time_t can't deal with dates earlier than 1970
 | 
			
		||||
    //  so 2000 seems a logical place to start
 | 
			
		||||
    year -= 2000;
 | 
			
		||||
    if(year < 0) return -1;
 | 
			
		||||
 | 
			
		||||
    /* Every 4th year is leap
 | 
			
		||||
     * unless it's a century (not leap)
 | 
			
		||||
     * unless it's a 4*century (leap)
 | 
			
		||||
     *
 | 
			
		||||
     * So there are 97 leap days in 400 years, giving 365 * 400 + 397 = 146097 days
 | 
			
		||||
     * For each 100 years, there are 24 leaps, giving 365 * 100 + 24 = 36524 days
 | 
			
		||||
     * For each 4 years, there is 1 leap, giving 365 * 4 + 1 = 1461 days
 | 
			
		||||
     */
 | 
			
		||||
    nd = 146097l * (year / 400);
 | 
			
		||||
    year %= 400;
 | 
			
		||||
    if(year) {
 | 
			
		||||
        nd += 36524 * (year / 100);
 | 
			
		||||
        year %= 100;
 | 
			
		||||
        nd += 1461 * (year / 4);
 | 
			
		||||
        if(year) {
 | 
			
		||||
            year %= 4;
 | 
			
		||||
            nd += (year ? 1 : 0) + 365 * year;
 | 
			
		||||
        } else {
 | 
			
		||||
            nd++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now add number of days between 1970 and 2000 (7 leap years)
 | 
			
		||||
     * 30 * 365 + 7 = 10957 */
 | 
			
		||||
    nd += 10957;
 | 
			
		||||
 | 
			
		||||
    // ignoring leap seconds, as time_t does, there are 86400 seconds in a day
 | 
			
		||||
    nd *= 86400;
 | 
			
		||||
 | 
			
		||||
    // done!
 | 
			
		||||
    return (time_t)nd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
time_t parse_time_date_mkwk(int year, int wk, int wday)
 | 
			
		||||
{
 | 
			
		||||
//    time_t t;
 | 
			
		||||
 | 
			
		||||
    fprintf(stderr, "ISO8601 week dates not supported yet\n");
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    t = parse_time_date_mkyear(year);
 | 
			
		||||
    if(t == (time_t)-1) return -1;
 | 
			
		||||
*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
time_t parse_time_date_mkord(int year, int oday)
 | 
			
		||||
{
 | 
			
		||||
    time_t t;
 | 
			
		||||
 | 
			
		||||
    if(oday < 1 || oday > parse_time_date_daysinyear(year)) return -1;
 | 
			
		||||
    t = parse_time_date_mkyear(year);
 | 
			
		||||
    if(t == (time_t)-1) return -1;
 | 
			
		||||
 | 
			
		||||
    t += (oday - 1) * 86400;
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
time_t parse_time_date_mkcal(int year, int mon, int day)
 | 
			
		||||
{
 | 
			
		||||
    time_t t;
 | 
			
		||||
 | 
			
		||||
    t = parse_time_date_mkyear(year);
 | 
			
		||||
    if(t == (time_t)-1) return -1;
 | 
			
		||||
 | 
			
		||||
    t += parse_time_date_dayselapsed(year, mon, day) * 86400;
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
time_t parse_time_date(const char* str, const char* end)
 | 
			
		||||
{
 | 
			
		||||
    int year, mon, day;
 | 
			
		||||
    const char* pos;
 | 
			
		||||
 | 
			
		||||
    // we don't deal with negative years -- this is at!
 | 
			
		||||
    if(*str < '0' || *str > '9') return -1;
 | 
			
		||||
 | 
			
		||||
    if( (pos = strchr(str, 'W')) ) {
 | 
			
		||||
        if(strchr(pos, '-')) {
 | 
			
		||||
            /* Either of:
 | 
			
		||||
             *  yyyy-Www
 | 
			
		||||
             *  yyyy-Www-d
 | 
			
		||||
             */
 | 
			
		||||
            switch(end - pos) {
 | 
			
		||||
            case 3:
 | 
			
		||||
                year = parse_time_digs(str, pos - str - 1);
 | 
			
		||||
                mon = parse_time_digs(pos + 1, 2);
 | 
			
		||||
                return parse_time_date_mkwk(year, mon, 1);
 | 
			
		||||
 | 
			
		||||
            case 5:
 | 
			
		||||
                year = parse_time_digs(str, pos - str - 1);
 | 
			
		||||
                mon = parse_time_digs(pos + 1, 2);
 | 
			
		||||
                day = parse_time_digs(pos + 4, 1);
 | 
			
		||||
                return parse_time_date_mkwk(year, mon, day);
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Either of:
 | 
			
		||||
             *  yyyyWww
 | 
			
		||||
             *  yyyyWwwd
 | 
			
		||||
             */
 | 
			
		||||
            switch(end - pos) {
 | 
			
		||||
            case 3:
 | 
			
		||||
                year = parse_time_digs(str, pos - str);
 | 
			
		||||
                mon = parse_time_digs(pos + 1, 2);
 | 
			
		||||
                return parse_time_date_mkwk(year, mon, 1);
 | 
			
		||||
 | 
			
		||||
            case 4:
 | 
			
		||||
                year = parse_time_digs(str, pos - str);
 | 
			
		||||
                mon = parse_time_digs(pos + 1, 2);
 | 
			
		||||
                day = parse_time_digs(pos + 3, 2);
 | 
			
		||||
                return parse_time_date_mkwk(year, mon, day);
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    } else if( (pos = strchr(str, '-')) ) {
 | 
			
		||||
        /* Valid extended formats are:
 | 
			
		||||
         *  yyyy-mm         (year + len 3)
 | 
			
		||||
         *  yyyy-ooo        (year + len 4)
 | 
			
		||||
         *  yyyy-mm-dd      (year + len 6)
 | 
			
		||||
         */
 | 
			
		||||
        switch(end - pos) {
 | 
			
		||||
        case 3:
 | 
			
		||||
            year = parse_time_digs(str, pos - str);
 | 
			
		||||
            mon = parse_time_digs(pos + 1, 2);
 | 
			
		||||
            return parse_time_date_mkcal(year, mon, 1);
 | 
			
		||||
 | 
			
		||||
        case 4:
 | 
			
		||||
            year = parse_time_digs(str, pos - str);
 | 
			
		||||
            day = parse_time_digs(pos + 1, 3);
 | 
			
		||||
            return parse_time_date_mkord(year, day);
 | 
			
		||||
 | 
			
		||||
        case 6:
 | 
			
		||||
            year = parse_time_digs(str, pos - str);
 | 
			
		||||
            mon = parse_time_digs(pos + 1, 2);
 | 
			
		||||
            day = parse_time_digs(pos + 4, 2);
 | 
			
		||||
            return parse_time_date_mkcal(year, mon, day);
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Valid basic formats, excluding week formats, are:
 | 
			
		||||
     *  yyyy            (len 4)
 | 
			
		||||
     *  yyyymm          (len 6)
 | 
			
		||||
     *  yyyyooo         (len 7)
 | 
			
		||||
     *  yyyymmdd        (len 8)
 | 
			
		||||
     */
 | 
			
		||||
    
 | 
			
		||||
    switch(end - str) {
 | 
			
		||||
    case 4:
 | 
			
		||||
        year = parse_time_digs(str, 4);
 | 
			
		||||
        return parse_time_date_mkord(year, 1);
 | 
			
		||||
 | 
			
		||||
    case 6:
 | 
			
		||||
        year = parse_time_digs(str, 4);
 | 
			
		||||
        mon = parse_time_digs(str + 4, 2);
 | 
			
		||||
        return parse_time_date_mkcal(year, mon, 1);
 | 
			
		||||
 | 
			
		||||
    case 7:
 | 
			
		||||
        year = parse_time_digs(str, 4);
 | 
			
		||||
        day = parse_time_digs(str + 4, 3);
 | 
			
		||||
        return parse_time_date_mkord(year, day);
 | 
			
		||||
 | 
			
		||||
    case 8:
 | 
			
		||||
        year = parse_time_digs(str, 4);
 | 
			
		||||
        mon = parse_time_digs(str + 4, 2);
 | 
			
		||||
        day = parse_time_digs(str + 6, 2);
 | 
			
		||||
        return parse_time_date_mkcal(year, mon, day);
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int parse_time_time(const char* str, const char* end)
 | 
			
		||||
{
 | 
			
		||||
    const char* pos;
 | 
			
		||||
    double d = 0;
 | 
			
		||||
    char* endp;
 | 
			
		||||
    int h = 0, m = 0, s = 0, ret;
 | 
			
		||||
 | 
			
		||||
    pos = strchr(str, '.');
 | 
			
		||||
    if(pos) {
 | 
			
		||||
        errno = 0;
 | 
			
		||||
        endp = 0;
 | 
			
		||||
        d = strtod(pos, &endp);
 | 
			
		||||
        if(errno || !endp) return -1;
 | 
			
		||||
        switch(*endp) {
 | 
			
		||||
        case 'Z':
 | 
			
		||||
        case '+':
 | 
			
		||||
        case '-':
 | 
			
		||||
        case 0:
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        pos = end;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Valid formats, excluding fraction, are:
 | 
			
		||||
     *
 | 
			
		||||
     *  hh              (len 2)
 | 
			
		||||
     *  hhmm            (len 4)
 | 
			
		||||
     *  hh:mm           (len 5)
 | 
			
		||||
     *  hhmmss          (len 6)
 | 
			
		||||
     *  hh:mm:ss        (len 8)
 | 
			
		||||
     */
 | 
			
		||||
    switch(pos - str) {
 | 
			
		||||
    case 2:
 | 
			
		||||
        h = parse_time_digs(str, 2);
 | 
			
		||||
        ret = ((h + d) * 3600 + 0.5);
 | 
			
		||||
        break;
 | 
			
		||||
        
 | 
			
		||||
    case 4:
 | 
			
		||||
        h = parse_time_digs(str, 2);
 | 
			
		||||
        m = parse_time_digs(str + 2, 2);
 | 
			
		||||
        ret = h * 3600 + ((m + d) * 60 + 0.5);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 5:
 | 
			
		||||
        h = parse_time_digs(str, 2);
 | 
			
		||||
        m = parse_time_digs(str + 3, 2);
 | 
			
		||||
        ret = h * 3600 + ((m + d) * 60 + 0.5);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 6:
 | 
			
		||||
        h = parse_time_digs(str, 2);
 | 
			
		||||
        m = parse_time_digs(str + 2, 2);
 | 
			
		||||
        s = parse_time_digs(str + 4, 2);
 | 
			
		||||
        ret = h * 3600 + m * 60 + s + d + 0.5;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 8:
 | 
			
		||||
        h = parse_time_digs(str, 2);
 | 
			
		||||
        m = parse_time_digs(str + 3, 2);
 | 
			
		||||
        s = parse_time_digs(str + 6, 2);
 | 
			
		||||
        ret = h * 3600 + m * 60 + s + d + 0.5;
 | 
			
		||||
        break;
 | 
			
		||||
        
 | 
			
		||||
    default:
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // special cases
 | 
			
		||||
    if(h == 24 && m == 0 && s == 0) return ret; // midnight at end of day
 | 
			
		||||
    if(h == 23 && m == 59 && s == 60) return ret; // leap second
 | 
			
		||||
 | 
			
		||||
    if(h < 0 || h > 23) return -1;
 | 
			
		||||
    if(m < 0 || m > 59) return -1;
 | 
			
		||||
    if(s < 0 || s > 59) return -1;
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* parse_time()
 | 
			
		||||
 *
 | 
			
		||||
 *  Parses an ISO-8601 date/time. We support any of the ISO-8601 formats.
 | 
			
		||||
 *  Parses an ISO-8601 date/time. We support any of the ISO-8601 formats. Uses libiso8601. On error,
 | 
			
		||||
 *  returns (time_t)-1.
 | 
			
		||||
 */
 | 
			
		||||
time_t parse_time(const char* str)
 | 
			
		||||
{
 | 
			
		||||
    const char* p, * tz;
 | 
			
		||||
    struct iso8601_date iso;
 | 
			
		||||
    time_t t;
 | 
			
		||||
    int s;
 | 
			
		||||
 | 
			
		||||
    // parse the date portion
 | 
			
		||||
    p = strchr(str, 'T');
 | 
			
		||||
    t = p ? parse_time_date(str, p) : parse_time_date(str, str + strlen(str));
 | 
			
		||||
    if(t == (time_t)-1) return -1;
 | 
			
		||||
 | 
			
		||||
    // parse the time portion
 | 
			
		||||
    if(p) {
 | 
			
		||||
        ++p;
 | 
			
		||||
        tz = strchr(p, 'Z');
 | 
			
		||||
        if(tz) {
 | 
			
		||||
            if(tz[1]) return -1;
 | 
			
		||||
        } else {
 | 
			
		||||
            tz = strchr(p, '+');
 | 
			
		||||
            if(!tz) tz = strchr(p, '-');
 | 
			
		||||
            if(!tz) tz = p + strlen(p);
 | 
			
		||||
        }
 | 
			
		||||
        s = parse_time_time(p, tz);
 | 
			
		||||
        if(s == -1) return -1;
 | 
			
		||||
        t += s;
 | 
			
		||||
 | 
			
		||||
        p = tz + 1;
 | 
			
		||||
        switch(*tz) {
 | 
			
		||||
        case '+':
 | 
			
		||||
            s = parse_time_time(p, p + strlen(p));
 | 
			
		||||
            if(s == -1) return -1;
 | 
			
		||||
            t -= s;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case '-':
 | 
			
		||||
            s = parse_time_time(p, p + strlen(p));
 | 
			
		||||
            if(s == -1) return -1;
 | 
			
		||||
            t += s;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(iso8601_parse(str, &iso, 0, 0)) return (time_t)-1;
 | 
			
		||||
    iso8601_to_time_t(&t, &iso);
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue