/*------------------------------------------------------------------------ * parse a date string * * String format is: * YYYY * YYYY-MM * YYYY-MM-DD * YYYY-DDD */ bool ogc_datetime :: parse_date( const char ** str) { const int month_days_norm[] = /* jan feb mar apr may jun jul aug sep oct nov dec */ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const int month_days_leap[] = /* jan feb mar apr may jun jul aug sep oct nov dec */ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const int * month_days; const char * s = *str; int num; int num_digs; /* get year */ num = collect(&s, &num_digs); if ( num_digs != 4 ) return false; _year = num; month_days = (is_leapyear() ? month_days_leap : month_days_norm); if ( *s == 0 ) { *str = s; return true; } if ( *s++ != '-' ) return false; /* get month or ordinal-day */ num = collect(&s, &num_digs); if ( num_digs < 2 || num_digs > 3) return false; if ( num_digs == 3 ) { /* ordinal day */ int i; if ( num == 0 || num > (365 + (is_leapyear() ? 1 : 0)) ) return false; for (i = 0; i < 11; i++) { if ( num <= month_days[i] ) break; num -= month_days[i]; } _month = i+1; _day = num; *str = s; return true; } else { if ( num == 0 || num > 12 ) return false; _month = num; if ( *s == 0 ) { *str = s; return true; } if ( *s++ != '-' ) return false; /* get day */ num = collect(&s, &num_digs); if ( num_digs != 2 ) return false; if ( num == 0 || num > month_days[_month-1] ) return false; _day = num; *str = s; return true; } }
int rtc_read_datetime(struct tm *tm) { char tmp[4]; int i, year, mday, hour, min; unsigned int seconds; /* RTC_AS3514's slave address is 0x46*/ for (i = 0; i < 4; i++){ tmp[i] = ascodec_read(AS3514_RTC_0 + i); } seconds = tmp[0] + (tmp[1]<<8) + (tmp[2]<<16) + (tmp[3]<<24); seconds -= SECS_ADJUST; /* Convert seconds since Jan-1-1980 to format compatible with * get_time() from firmware/common/timefuncs.c */ /* weekday */ tm->tm_wday = ((seconds % WEEK_SECONDS) / DAY_SECONDS + 2) % 7; /* Year */ year = 1980; while (seconds >= LEAP_YEAR_SECONDS) { if (is_leapyear(year)){ seconds -= LEAP_YEAR_SECONDS; } else { seconds -= YEAR_SECONDS; } year++; } if (is_leapyear(year)) { days_in_month[1] = 29; } else { days_in_month[1] = 28; if(seconds>YEAR_SECONDS){ year++; seconds -= YEAR_SECONDS; } } tm->tm_year = year%100 + 100; /* Month */ for (i = 0; i < 12; i++) { if (seconds < days_in_month[i]*DAY_SECONDS){ tm->tm_mon = i; break; } seconds -= days_in_month[i]*DAY_SECONDS; } /* Month Day */ mday = seconds/DAY_SECONDS; seconds -= mday*DAY_SECONDS; tm->tm_mday = mday + 1; /* 1 ... 31 */ /* Hour */ hour = seconds/HOUR_SECONDS; seconds -= hour*HOUR_SECONDS; tm->tm_hour = hour; /* Minute */ min = seconds/MINUTE_SECONDS; seconds -= min*MINUTE_SECONDS; tm->tm_min = min; /* Second */ tm->tm_sec = seconds; return 7; }
/* * Parses (almost) standard ISO 8601 date strings. The differences are: * * + Only seconds may have a decimal point, with up to 18 digits after it * (maximum attoseconds precision). * + Either a 'T' as in ISO 8601 or a ' ' may be used to separate * the date and the time. Both are treated equivalently. * + Doesn't (yet) handle the "YYYY-DDD" or "YYYY-Www" formats. * + Doesn't handle leap seconds (seconds value has 60 in these cases). * + Doesn't handle 24:00:00 as synonym for midnight (00:00:00) tomorrow * + Accepts special values "NaT" (not a time), "Today", (current * day according to local time) and "Now" (current time in UTC). * + ':' separator between hours, minutes, and seconds is optional. When * omitted, each component must be 2 digits if it appears. (GH-10041) * * 'str' must be a NULL-terminated string, and 'len' must be its length. * * 'out' gets filled with the parsed date-time. * 'out_local' gets set to 1 if the parsed time contains timezone, * to 0 otherwise. * 'out_tzoffset' gets set to timezone offset by minutes * if the parsed time was in local time, * to 0 otherwise. The values 'now' and 'today' don't get counted * as local, and neither do UTC +/-#### timezone offsets, because * they aren't using the computer's local timezone offset. * * Returns 0 on success, -1 on failure. */ int parse_iso_8601_datetime(char *str, int len, pandas_datetimestruct *out, int *out_local, int *out_tzoffset) { int year_leap = 0; int i, numdigits; char *substr, sublen; /* If year-month-day are separated by a valid separator, * months/days without leading zeroes will be parsed * (though not iso8601). If the components aren't separated, * 4 (YYYY) or 8 (YYYYMMDD) digits are expected. 6 digits are * forbidden here (but parsed as YYMMDD elsewhere). */ int has_ymd_sep = 0; char ymd_sep = '\0'; char valid_ymd_sep[] = {'-', '.', '/', '\\', ' '}; int valid_ymd_sep_len = sizeof(valid_ymd_sep); /* hour-minute-second may or may not separated by ':'. If not, then * each component must be 2 digits. */ int has_hms_sep = 0; int hour_was_2_digits = 0; /* Initialize the output to all zeros */ memset(out, 0, sizeof(pandas_datetimestruct)); out->month = 1; out->day = 1; substr = str; sublen = len; /* Skip leading whitespace */ while (sublen > 0 && isspace(*substr)) { ++substr; --sublen; } /* Leading '-' sign for negative year */ if (*substr == '-') { ++substr; --sublen; } if (sublen == 0) { goto parse_error; } /* PARSE THE YEAR (4 digits) */ out->year = 0; if (sublen >= 4 && isdigit(substr[0]) && isdigit(substr[1]) && isdigit(substr[2]) && isdigit(substr[3])) { out->year = 1000 * (substr[0] - '0') + 100 * (substr[1] - '0') + 10 * (substr[2] - '0') + (substr[3] - '0'); substr += 4; sublen -= 4; } /* Negate the year if necessary */ if (str[0] == '-') { out->year = -out->year; } /* Check whether it's a leap-year */ year_leap = is_leapyear(out->year); /* Next character must be a separator, start of month, or end of string */ if (sublen == 0) { if (out_local != NULL) { *out_local = 0; } goto finish; } if (!isdigit(*substr)) { for (i = 0; i < valid_ymd_sep_len; ++i) { if (*substr == valid_ymd_sep[i]) { break; } } if (i == valid_ymd_sep_len) { goto parse_error; } has_ymd_sep = 1; ymd_sep = valid_ymd_sep[i]; ++substr; --sublen; /* Cannot have trailing separator */ if (sublen == 0 || !isdigit(*substr)) { goto parse_error; } } /* PARSE THE MONTH */ /* First digit required */ out->month = (*substr - '0'); ++substr; --sublen; /* Second digit optional if there was a separator */ if (isdigit(*substr)) { out->month = 10 * out->month + (*substr - '0'); ++substr; --sublen; } else if (!has_ymd_sep) { goto parse_error; } if (out->month < 1 || out->month > 12) { PyErr_Format(PyExc_ValueError, "Month out of range in datetime string \"%s\"", str); goto error; } /* Next character must be the separator, start of day, or end of string */ if (sublen == 0) { /* Forbid YYYYMM. Parsed instead as YYMMDD by someone else. */ if (!has_ymd_sep) { goto parse_error; } if (out_local != NULL) { *out_local = 0; } goto finish; } if (has_ymd_sep) { /* Must have separator, but cannot be trailing */ if (*substr != ymd_sep || sublen == 1) { goto parse_error; } ++substr; --sublen; } /* PARSE THE DAY */ /* First digit required */ if (!isdigit(*substr)) { goto parse_error; } out->day = (*substr - '0'); ++substr; --sublen; /* Second digit optional if there was a separator */ if (isdigit(*substr)) { out->day = 10 * out->day + (*substr - '0'); ++substr; --sublen; } else if (!has_ymd_sep) { goto parse_error; } if (out->day < 1 || out->day > days_per_month_table[year_leap][out->month - 1]) { PyErr_Format(PyExc_ValueError, "Day out of range in datetime string \"%s\"", str); goto error; } /* Next character must be a 'T', ' ', or end of string */ if (sublen == 0) { if (out_local != NULL) { *out_local = 0; } goto finish; } if ((*substr != 'T' && *substr != ' ') || sublen == 1) { goto parse_error; } ++substr; --sublen; /* PARSE THE HOURS */ /* First digit required */ if (!isdigit(*substr)) { goto parse_error; } out->hour = (*substr - '0'); ++substr; --sublen; /* Second digit optional */ if (isdigit(*substr)) { hour_was_2_digits = 1; out->hour = 10 * out->hour + (*substr - '0'); ++substr; --sublen; if (out->hour >= 24) { PyErr_Format(PyExc_ValueError, "Hours out of range in datetime string \"%s\"", str); goto error; } } /* Next character must be a ':' or the end of the string */ if (sublen == 0) { if (!hour_was_2_digits) { goto parse_error; } goto finish; } if (*substr == ':') { has_hms_sep = 1; ++substr; --sublen; /* Cannot have a trailing separator */ if (sublen == 0 || !isdigit(*substr)) { goto parse_error; } } else if (!isdigit(*substr)) { if (!hour_was_2_digits) { goto parse_error; } goto parse_timezone; } /* PARSE THE MINUTES */ /* First digit required */ out->min = (*substr - '0'); ++substr; --sublen; /* Second digit optional if there was a separator */ if (isdigit(*substr)) { out->min = 10 * out->min + (*substr - '0'); ++substr; --sublen; if (out->min >= 60) { PyErr_Format(PyExc_ValueError, "Minutes out of range in datetime string \"%s\"", str); goto error; } } else if (!has_hms_sep) { goto parse_error; } if (sublen == 0) { goto finish; } /* If we make it through this condition block, then the next * character is a digit. */ if (has_hms_sep && *substr == ':') { ++substr; --sublen; /* Cannot have a trailing ':' */ if (sublen == 0 || !isdigit(*substr)) { goto parse_error; } } else if (!has_hms_sep && isdigit(*substr)) { } else { goto parse_timezone; } /* PARSE THE SECONDS */ /* First digit required */ out->sec = (*substr - '0'); ++substr; --sublen; /* Second digit optional if there was a separator */ if (isdigit(*substr)) { out->sec = 10 * out->sec + (*substr - '0'); ++substr; --sublen; if (out->sec >= 60) { PyErr_Format(PyExc_ValueError, "Seconds out of range in datetime string \"%s\"", str); goto error; } } else if (!has_hms_sep) { goto parse_error; } /* Next character may be a '.' indicating fractional seconds */ if (sublen > 0 && *substr == '.') { ++substr; --sublen; } else { goto parse_timezone; } /* PARSE THE MICROSECONDS (0 to 6 digits) */ numdigits = 0; for (i = 0; i < 6; ++i) { out->us *= 10; if (sublen > 0 && isdigit(*substr)) { out->us += (*substr - '0'); ++substr; --sublen; ++numdigits; } } if (sublen == 0 || !isdigit(*substr)) { goto parse_timezone; } /* PARSE THE PICOSECONDS (0 to 6 digits) */ numdigits = 0; for (i = 0; i < 6; ++i) { out->ps *= 10; if (sublen > 0 && isdigit(*substr)) { out->ps += (*substr - '0'); ++substr; --sublen; ++numdigits; } } if (sublen == 0 || !isdigit(*substr)) { goto parse_timezone; } /* PARSE THE ATTOSECONDS (0 to 6 digits) */ numdigits = 0; for (i = 0; i < 6; ++i) { out->as *= 10; if (sublen > 0 && isdigit(*substr)) { out->as += (*substr - '0'); ++substr; --sublen; ++numdigits; } } parse_timezone: /* trim any whitepsace between time/timeezone */ while (sublen > 0 && isspace(*substr)) { ++substr; --sublen; } if (sublen == 0) { // Unlike NumPy, treating no time zone as naive goto finish; } /* UTC specifier */ if (*substr == 'Z') { /* "Z" should be equivalent to tz offset "+00:00" */ if (out_local != NULL) { *out_local = 1; } if (out_tzoffset != NULL) { *out_tzoffset = 0; } if (sublen == 1) { goto finish; } else { ++substr; --sublen; } } else if (*substr == '-' || *substr == '+') { /* Time zone offset */ int offset_neg = 0, offset_hour = 0, offset_minute = 0; /* * Since "local" means local with respect to the current * machine, we say this is non-local. */ if (*substr == '-') { offset_neg = 1; } ++substr; --sublen; /* The hours offset */ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) { offset_hour = 10 * (substr[0] - '0') + (substr[1] - '0'); substr += 2; sublen -= 2; if (offset_hour >= 24) { PyErr_Format(PyExc_ValueError, "Timezone hours offset out of range " "in datetime string \"%s\"", str); goto error; } } else if (sublen >= 1 && isdigit(substr[0])) { offset_hour = substr[0] - '0'; ++substr; --sublen; } else { goto parse_error; } /* The minutes offset is optional */ if (sublen > 0) { /* Optional ':' */ if (*substr == ':') { ++substr; --sublen; } /* The minutes offset (at the end of the string) */ if (sublen >= 2 && isdigit(substr[0]) && isdigit(substr[1])) { offset_minute = 10 * (substr[0] - '0') + (substr[1] - '0'); substr += 2; sublen -= 2; if (offset_minute >= 60) { PyErr_Format(PyExc_ValueError, "Timezone minutes offset out of range " "in datetime string \"%s\"", str); goto error; } } else if (sublen >= 1 && isdigit(substr[0])) { offset_minute = substr[0] - '0'; ++substr; --sublen; } else { goto parse_error; } } /* Apply the time zone offset */ if (offset_neg) { offset_hour = -offset_hour; offset_minute = -offset_minute; } if (out_local != NULL) { *out_local = 1; // Unlike NumPy, do not change internal value to local time *out_tzoffset = 60 * offset_hour + offset_minute; } } /* Skip trailing whitespace */ while (sublen > 0 && isspace(*substr)) { ++substr; --sublen; } if (sublen != 0) { goto parse_error; } finish: return 0; parse_error: PyErr_Format(PyExc_ValueError, "Error parsing datetime string \"%s\" at position %d", str, (int)(substr - str)); return -1; error: return -1; }