/* * Copyright (C) 2007 by egnite Software GmbH. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /*! * \file pro/rfctime.c * \brief RFC date and time conversion. * * \verbatim * $Id$ * \endverbatim */ #include #include #include #include static char rfc1123_buf[32]; static char *wkdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /*! * \brief Skip spaces and tabs. * * \param str Pointer to the string containing the leading spaces. * * \return Pointer into the string after the last space character. */ static char *skip_spaces(const char *str) { while (*str == ' ' || *str == '\t') str++; return (char *)str; } /*! * \brief Parse digits. * * Number base is 10. * * \param str Pointer to the string containing the digits. * \param year Points to the variable that will receive the binary value. * * \return Pointer into the string after the parsed characters. */ static char *parse_digits(const char *str, int *val) { *val = 0; while (isdigit((unsigned char)*str)) { *val *= 10; *val += *str++ - '0'; } return (char *)str; } /*! * \brief Parse string containing the year. * * Works with years including and excluding the century. If the * resulting value is lower than 70, the 21st century is assumed. * Values * * \param str Pointer to the time string. * \param year Points to the variable that will receive the years since 1900. * * \return Pointer into the string after the parsed characters. */ char *TimeParseYear(const char *str, int *year) { str = parse_digits(str, year); if (*year < 70) { *year += 100; } else if (*year > 1900) { *year -= 1900; } return (char *)str; } /*! * \brief Parse a string for the name of a month. * * Does basic comparision only, no range checking etc. For example, if * the string starts with capital letter 'D', December is assumed without * further checking the rest. It will work for abbreviated as well as * full names. * * \param str Pointer to the time string. Must start with a capital letter * followed by lower case characters. * \param month Points to the variable that will receive the month (0..11). * * \return Pointer into the string after the parsed characters. */ char *TimeParseMonth(const char *str, int *month) { if (*str == 'A') { if (*++str == 'p' || *str == 'P') { /* April */ *month = 3; } else { /* August */ *month = 7; } } else if (*str == 'D') { /* December */ *month = 11; } else if (*str == 'F') { /* February */ *month = 1; } else if (*str == 'J') { if (*++str == 'a' || *str == 'A') { /* January */ *month = 0; } else if (*str && (*++str == 'l' || *str == 'L')) { /* July */ *month = 6; } else { /* June */ *month = 5; } } else if (*str == 'M') { if (*++str == 'a' && (*++str == 'r' || *str == 'R')) { /* March */ *month = 2; } else { /* May */ *month = 4; } } else if (*str == 'N') { /* November */ *month = 10; } else if (*str == 'O') { /* October */ *month = 9; } else { /* September */ *month = 8; } while (isalpha((unsigned char)*str)) { str++; } return (char *)str; } /*! * \brief Parse date string. * * Parses a string of the format day 'x' month-name 'x' year, where * 'x' is any non-alphanumeric character. * * \param str Pointer to the date string. * \param mday Points to the variable that will receive the day (1..31). * \param mon Points to the variable that will receive the month (0..11). * \param year Points to the variable that will receive the years since 1900. * * \return Pointer into the string after the parsed characters. */ char *TimeParseDmy(const char *str, int *mday, int *mon, int *year) { str = parse_digits(str, mday); while (*str && !isalpha((unsigned char)*str)) { str++; } str = TimeParseMonth(str, mon); while (*str && !isdigit((unsigned char)*str)) { str++; } str = TimeParseYear(str, year); return (char *)str; } /*! * \brief Parse time of day string. * * Parses a string of the format hour ':' minute ':' second. * * \param str Pointer to the time string. * \param hour Points to the variable that will receive the hour (0..23). * \param min Points to the variable that will receive the minute (0..59). * \param sec Points to the variable that will receive the second (0..59). * * \return Pointer into the string after the parsed characters. */ char *TimeParseHms(const char *str, int *hour, int *min, int *sec) { str = parse_digits(str, hour); if (*str == ':') { str = parse_digits(str + 1, min); if (*str == ':') { str = parse_digits(str + 1, sec); } else { *sec = 0; } } else { *min = 0; } return (char *)str; } /*! * \brief Parse RFC date and time string. * * This routine accepts RFC 850, RFC 1123 and asctime time formats. * * \param str Pointer to the date and time string. * * \return Number of seconds since epoch or -1 in case of any error. */ time_t RfcTimeParse(const char *str) { struct tm dts = { 0, 0, 0, 1, 0, 0, 0, 0, 0 }; /* Skip leading whitespace. */ str = skip_spaces(str); /* Skip weekday, optional in RFC 822. */ if (isalpha((unsigned char)*str)) { while (*str && *str != ' ' && *str != '\t') str++; str = skip_spaces(str); } if (isalpha((unsigned char)*str)) { /* asctime format 'Fri Feb 2 2007 07:30:05'. */ str = TimeParseMonth(str, &dts.tm_mon); str = skip_spaces(str); str = parse_digits(str, &dts.tm_mday); str = skip_spaces(str); str = TimeParseYear(str, &dts.tm_year); str = skip_spaces(str); str = TimeParseHms(str, &dts.tm_hour, &dts.tm_min, &dts.tm_sec); } else if (*str) { /* RFC 850 'Friday, 02-Feb-2007 07:30:05 GMT'. */ /* RFC 1123 'Fri, 02 Feb 2007 07:30:05 GMT'. */ str = TimeParseDmy(str, &dts.tm_mday, &dts.tm_mon, &dts.tm_year); str = skip_spaces(str); str = TimeParseHms(str, &dts.tm_hour, &dts.tm_min, &dts.tm_sec); } str = skip_spaces(str); if (strcmp(str, "GMT") == 0) { return _mkgmtime(&dts); } return mktime(&dts); } /*! * \brief Create RFC 1123 date and time string. * * \param tm Pointer to the date and time structure. * * \return Pointer to a static buffer containing the string. */ char *Rfc1123TimeString(struct tm *tm) { sprintf(rfc1123_buf, "%s, %02d %s %04d %02d:%02d:%02d", wkdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec); return rfc1123_buf; }