amath/build/flexcat/strptime.c

315 lines
7.4 KiB
C

/*
* $Id$
*
* Copyright (C) 1993-1999 by Jochen Wiedmann and Marcin Orlowski
* Copyright (C) 2002-2015 FlexCat Open Source Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <string.h>
#include <time.h>
#include <stdio.h>
/// strptime
// parse a date string produced by strftime() and put the success in a struct tm
enum ScanDateState
{
SDS_DEFAULT = 0,
SDS_SPECIFIER,
SDS_DONE,
SDS_SECOND,
SDS_MINUTE,
SDS_HOUR,
SDS_DAY_OF_MONTH,
SDS_MONTH,
SDS_YEAR,
SDS_DAY_OF_WEEK,
SDS_DAY_YEAR,
SDS_IS_DST,
};
#define FLG_SEC (1<<0)
#define FLG_MIN (1<<1)
#define FLG_HOUR (1<<2)
#define FLG_MDAY (1<<3)
#define FLG_MON (1<<4)
#define FLG_YEAR (1<<5)
#define FLG_WDAY (1<<6)
#define FLG_YDAY (1<<7)
#define FLG_ISDST (1<<8)
#define FLG_4DIGIT_YEAR (1<<9)
char *strptime(const char *string, const char *fmt, struct tm *res)
{
int success = 1;
char fc;
char sc;
enum ScanDateState state = SDS_DEFAULT;
int flags = 0;
// start with the first character in both strings
fc = *fmt++;
sc = *string++;
while(state != SDS_DONE)
{
if(fc == '\0' && sc == '\0')
state = SDS_DONE;
switch(state)
{
case SDS_DEFAULT:
{
if(fc == '%')
{
state = SDS_SPECIFIER;
fc = *fmt++;
}
else
{
// the format string seems to be malformed, bail out
state = SDS_DONE;
}
}
break;
case SDS_SPECIFIER:
{
switch(fc)
{
case 'd': // %d - day number with leading zeros (01-31)
case 'e': // %e - day number with leading spaces ( 1-31)
{
flags |= FLG_MDAY;
state = SDS_DAY_OF_MONTH;
fc = *fmt++;
}
break;
case 'm': // %m - month number with leading zeros (01-12)
{
flags |= FLG_MON;
state = SDS_MONTH;
fc = *fmt++;
}
break;
case 'Y': // %Y - year using four digits with leading zeros
{
flags |= FLG_4DIGIT_YEAR;
}
// we fall through here
case 'y': // %y - year using two digits with leading zeros (00-99)
{
flags |= FLG_YEAR;
state = SDS_YEAR;
fc = *fmt++;
}
break;
case '-':
{
// ignore any switches between with/without leading zeros/spaces
fc = *fmt++;
}
break;
default:
{
// unknown specifier, bail out
state = SDS_DONE;
}
break;
}
}
break;
case SDS_DAY_OF_MONTH:
{
if(sc == fc)
{
// next separator in format string found
state = SDS_DEFAULT;
fc = *fmt++;
sc = *string++;
}
else if(sc >= '0' && sc <= '9')
{
// valid number found, add it to the day of month
res->tm_mday = res->tm_mday * 10 + sc - '0';
sc = *string++;
}
else
{
// unexpected character, bail out
state = SDS_DONE;
}
}
break;
case SDS_MONTH:
{
if(sc == fc)
{
// next separator in format string found
state = SDS_DEFAULT;
fc = *fmt++;
sc = *string++;
}
else if(sc >= '0' && sc <= '9')
{
// valid number found, add it to the month
res->tm_mon = res->tm_mon * 10 + sc - '0';
sc = *string++;
}
else
{
// unexpected character, bail out
state = SDS_DONE;
}
}
break;
case SDS_YEAR:
{
if(sc == fc)
{
// next separator in format string found
state = SDS_DEFAULT;
fc = *fmt++;
sc = *string++;
}
else if(sc >= '0' && sc <= '9')
{
// valid number found, add it to the year
res->tm_year = res->tm_year * 10 + sc - '0';
sc = *string++;
}
else
{
// unexpected character, bail out
state = SDS_DONE;
}
}
break;
default:
// nothing to do
break;
}
}
// finally check if the calculated values are correct, but only those which
// were specified in the format string
if((flags & FLG_MDAY) || strstr(fmt, "%d") != NULL || strstr(fmt, "%-d") != NULL || strstr(fmt, "%e") != NULL)
{
if(res->tm_mday >= 1 && res->tm_mday <= 31)
{
// nothing to adjust
}
else
{
success = 0;
}
}
if((flags & FLG_MON) || strstr(fmt, "%m") != NULL || strstr(fmt, "%-m") != NULL)
{
if(res->tm_mon >= 1 && res->tm_mon <= 12)
{
// tm_mon counts from 0 to 11
res->tm_mon--;
}
else
{
success = 0;
}
}
if((flags & FLG_YEAR) || strstr(fmt, "%y") != NULL || strstr(fmt, "%-y") != NULL || strstr(fmt, "%Y") != NULL || strstr(fmt, "%-Y") != NULL)
{
if((flags & FLG_4DIGIT_YEAR) || strstr(fmt, "%Y") != NULL || strstr(fmt, "%-Y") != NULL)
{
if(res->tm_year >= 1900)
{
// tm_year counts the years from 1900
res->tm_year -= 1900;
}
else
{
// year numbers less than 1900 are not supported
success = 0;
}
}
else
{
// 2 digit year number, must be less than 100
if(res->tm_year < 100)
{
if(res->tm_year < 40)
{
// tm_year counts the years from 1900
// if the year number is less than 40 we assume a year between
// 2000 and 2039 instead of between 1900 and 1939 to allow a user
// age of at least ~70 years.
res->tm_year += 100;
}
}
// Although we expect a two digit year number for %y we got one with more digits.
// Better not fail at this even if the entered string is wrong. People tend to
// forget the correct formatting.
else if(res->tm_year >= 1900)
{
// tm_year counts the years from 1900
res->tm_year -= 1900;
}
else
{
// numbers between 100 and 1899 are definitely not allowed
success = 0;
}
}
}
// finally check if the day value is correct
if(success == 1 && (flags & FLG_MDAY))
{
if(res->tm_mon == 1)
{
// February has 29 days at most, but we don't check for leap years here
if(res->tm_mday > 29)
{
success = 0;
}
}
else if(res->tm_mon == 3 ||
res->tm_mon == 5 ||
res->tm_mon == 8 ||
res->tm_mon == 10)
{
// April, June, September and November have 30 days
if(res->tm_mday > 30)
{
success = 0;
}
}
}
return (char *)string;
}
///