📄 strptime.c
字号:
#if !defined(lint) && defined(SCCSIDS)static char sccsid[] = "@(#)strptime.c 1.1 92/07/30 SMI";#endif#include <ctype.h>#include <locale.h>#include <time.h>static char *strmatch(/*char *cp, char *string*/);static char *yearmatch(/*char *cp, char *format, struct tm *tm, int *hadyearp*/);static char *cvtnum(/*char *cp, int *nump*/);static char *skipnws(/*char *format*/);extern char *getlocale_time();#define NULL 0char *strptime(buf, format, tm) char *buf; char *format; struct tm *tm;{ register char *cp, *p; register int c, ch; register int i; register struct dtconv *dtcp; int hadyear; (void) getlocale_time(); dtcp = localdtconv(); /* get locale's strings */ cp = buf; while ((c = *format++) != '\0') { if (c == '%') { switch (*format++) { case '%': /* Percent sign */ if (*cp++ != '%') return (NULL); break; case 'a': /* Abbreviated weekday name */ case 'A': /* Weekday name */ for (i = 0; i < 7; i++) { if ((p = strmatch(cp, dtcp->weekday_names[i], *format)) != NULL || (p = strmatch(cp, dtcp->abbrev_weekday_names[i], *format)) != NULL) goto match_wday; } return (NULL); /* no match */ match_wday: tm->tm_wday = i; cp = p; break; case 'h': case 'b': /* Abbreviated month name */ case 'B': /* Month name */ for (i = 0; i < 12; i++) { if ((p = strmatch(cp, dtcp->month_names[i], *format)) != NULL || (p = strmatch(cp, dtcp->abbrev_month_names[i], *format)) != NULL) goto match_month; } return (NULL); /* no match */ match_month: tm->tm_mon = i; cp = p; break; case 'c': /* date and time representation */ cp = strptime(cp, "%x %X", tm); if (cp == NULL) return (NULL); break; case 'C': /* long date and time representation */ cp = strptime(cp, dtcp->ldate_format, tm); if (cp == NULL) return (NULL); break; case 'd': /* Day of month, with leading zero */ case 'e': /* Day of month without leading zero */ cp = cvtnum(cp, &tm->tm_mday); if (cp == NULL) return (NULL); /* no digits */ if (tm->tm_mday > 31) return (NULL); if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); break; case 'D': /* Shorthand for %m/%d/%y */ cp = strptime(cp, "%m/%d/%y", tm); if (cp == NULL) return (NULL); break; case 'H': /* Hour (24 hour version) */ case 'k': /* Hour (24 hour version) */ cp = cvtnum(cp, &tm->tm_hour); if (cp == NULL) return (NULL); /* no digits */ if (tm->tm_hour > 23) return (NULL); if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); break; case 'I': /* Hour (12 hour version) */ case 'l': /* Hour (12 hour version) */ cp = cvtnum(cp, &tm->tm_hour); if (cp == NULL) return (NULL); /* no digits */ if (tm->tm_hour == 12) tm->tm_hour = 0; else if (tm->tm_hour > 11) return (NULL); if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); break; case 'j': /* Julian date */ cp = cvtnum(cp, &tm->tm_yday); if (cp == NULL) return (NULL); /* no digits */ if (tm->tm_yday > 365) return (NULL); break; case 'm': /* Month number */ cp = cvtnum(cp, &tm->tm_mon); if (cp == NULL) return (NULL); /* no digits */ tm->tm_mon--; if (tm->tm_mon < 0 || tm->tm_mon > 11) return (NULL); if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); break; case 'M': /* Minute */ /* * This is optional; if we're at the end of the * string, or the next character is white * space, don't try to match it. */ if ((c = *cp) != '\0' && !isspace((unsigned char)c)) { cp = cvtnum(cp, &tm->tm_min); if (cp == NULL) return (NULL); /* no digits */ if (tm->tm_min > 59) return (NULL); } if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); break; case 'p': /* AM or PM */ if ((p = strmatch(cp, dtcp->am_string, *format)) != NULL) { /* * AM. */ if (tm->tm_hour == 12) tm->tm_hour = 0; cp = p; } else if ((p = strmatch(cp, dtcp->pm_string, *format)) != NULL) { /* * PM. */ if (tm->tm_hour > 12) return (NULL); /* error */ else if (tm->tm_hour != 12) tm->tm_hour += 12; cp = p; } break; case 'r': /* Shorthand for %I:%M:%S AM or PM */ cp = strptime(cp, "%I:%M:%S %p", tm); if (cp == NULL) return (NULL); break; case 'R': /* Time as %H:%M */ cp = strptime(cp, "%H:%M", tm); if (cp == NULL) return (NULL); break; case 'S': /* Seconds */ /* * This is optional; if we're at the end of the * string, or the next character is white * space, don't try to match it. */ if ((c = *cp) != '\0' && !isspace((unsigned char)c)) { cp = cvtnum(cp, &tm->tm_sec); if (cp == NULL) return (NULL); /* no digits */ if (tm->tm_sec > 59) return (NULL); } if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); break; case 'T': /* Shorthand for %H:%M:%S */ cp = strptime(cp, "%H:%M:%S", tm); if (cp == NULL) return (NULL); break; case 'x': /* Localized date format */ cp = strptime(cp, dtcp->sdate_format, tm); if (cp == NULL) return (NULL); break; case 'X': /* Localized time format */ cp = strptime(cp, dtcp->time_format, tm); if (cp == NULL) return (NULL); break; case 'y': /* Year in the form yy */ cp = yearmatch(cp, format, tm, &hadyear); if (cp == NULL) return (NULL); if (hadyear) { if (tm->tm_year > 99) return (NULL); } return (cp); /* match is complete */ case 'Y': /* Year in the form ccyy */ cp = yearmatch(cp, format, tm, &hadyear); if (cp == NULL) return (NULL); if (hadyear) { tm->tm_year -= 1900; if (tm->tm_year < 0) return (NULL); } return (cp); /* match is complete */ default: return (NULL); /* unknown conversion */ } } else { if (isspace((unsigned char)c)) { while ((ch = *cp++) != '\0' && isspace((unsigned char)ch)) ; cp--; } else { if (*cp++ != c) return (NULL); } } } return (cp);}/* * Try to match the beginning of the string pointed to by "cp" with the string * pointed to by "string". The match is independent of the case of either * string. * * "termc" is the next character in the format string following the one for * which this match is being done. If the match succeeds, make sure the next * character after the match is either '\0', or that it would match "termc". * * If both matches succeed, return a pointer to the next character after the * first match. Otherwise, return NULL. */static char *strmatch(cp, string, termc) register char *cp; register char *string; char termc;{ register unsigned char c, strc; /* * Match the beginning portion of "cp" with "string". */ while ((strc = *string++) != '\0') { c = *cp++; if (isupper(c)) c = tolower(c); if (isupper(strc)) strc = tolower(strc); if (c != strc) return (NULL); } if ((c = *cp) != '\0') { if (isspace((unsigned char)termc)) { if (!isspace(c)) return (NULL); } else { if (c != (unsigned char)termc) return (NULL); } } return (cp);}/* * Try to match a %y or %Y specification. * If it matches, try matching the rest of the format. If it succeeds, just * return. Otherwise, try backing the scan up, ignoring the %y/%Y and any * following non-white-space string. If that succeeds, just return. (This * permits a missing year to be detected if it's at the beginning of a date, as * well as if it's at the end of a date, so that formats such as "%Y/%m/%d" can * match "3/14" and default the year.) * * Set "*hadyearp" to indicate whether a year was specified or not. */static char *yearmatch(cp, format, tm, hadyearp) register char *cp; char *format; struct tm *tm; int *hadyearp;{ register int c; char *savecp; int saveyear; /* * This is optional; if we're at the end of the * string, or the next character is white * space, don't try to match it. */ if ((c = *cp) != '\0' && !isspace((unsigned char)c)) { savecp = cp; saveyear = tm->tm_year; cp = cvtnum(cp, &tm->tm_year); if (cp == NULL) return (NULL); /* no digits */ if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); /* * Year can also be optional if it's at * the *beginning* of a date. We check * this by trying to parse the rest of * the date here. If we succeed, OK; * otherwise, we skip over the %y and * try again. */ cp = strptime(cp, format, tm); if (cp != NULL) *hadyearp = 1; else { *hadyearp = 0; cp = savecp; format = skipnws(format); tm->tm_year = saveyear; cp = strptime(cp, format, tm); } } else { *hadyearp = 0; if ((c = *cp) == '\0' || isspace((unsigned char)c)) format = skipnws(format); cp = strptime(cp, format, tm); } return (cp);}/* * Try to match a (decimal) number in the string pointed to by "cp". * If the match succeeds, store the result in the "int" pointed to by "nump" * and return a pointer to the character following the number in the string. * If it fails, return NULL. */static char *cvtnum(cp, nump) register char *cp; int *nump;{ register int c; register int i; c = (unsigned char)*cp++; if (!isdigit(c)) return (NULL); /* no digits */ i = 0; do { i = i*10 + c - '0'; c = (unsigned char)*cp++; } while (isdigit(c)); *nump = i; return (cp - 1);}/* * If a format item (such as %H, hours) is followed by a non-white-space * character other than "%", and the part of the string that matched the format * item is followed by white space, the string of non-white-space, * non-format-item characters following that format item may be omitted. */static char *skipnws(format) register char *format;{ register char c; /* * Skip over non-white-space, non-digit characters. "%" is special. */ while ((c = *format) != '\0' && !isspace((unsigned char)c)) { if (c == '%') { /* * This is a format item. If it's %%, skip it as * that's a non-white space, non-digit character. */ if (*(format + 1) == '%') format++; /* skip % */ else break; } format++; } return (format);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -