⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jdate.c

📁 这是一个银行业务上用来计算日期时间的工具包
💻 C
字号:
/* * $Id: //pentools/main/datemath/jdate.c#2 $ * * written by:  Stephen J. Friedl *              Software Consultant *              Tustin, California USA *              steve@unixwiz.net / www.unixwiz.net * *	This module is responsible for calculating "jdates" (Julian *	Dates), and the algorithm mirrors that used by the Informix *	3.3 product (predating Inofrmix SQL). */#include	<stdio.h>#include	<string.h>#include	<memory.h>#include	<assert.h>#include	<ctype.h>#include	<time.h>#include	"defs.h"/*------------------------------------------------------------------------ * This macro represents the "null" date and is treated specially * throughout the code.  This corresponds to the most negative long * int and would be outside the range of these date routines anyway. */#define		DATENULL	0x80000000LU/*------------------------------------------------------------------------ * these are indexes into the    short mdy[3]    array for each of * the month/day/year components. */#define		MM		0#define		DD		1#define		YY		2/*------------------------------------------------------------------------ * These macros help out with leap year calculations.  A leap year happens * every four years, except century years, but including years evenly * divisible by 400.  So, 1900 was not a leap year but 2000 will be. */#define		MOD(a,b)	(((a)%(b))==0)#define		is_leap(yy)	(MOD((yy),4) && (!MOD((yy),100)||MOD((yy),400)))#define		daysinyear(yy)	(is_leap(yy) ? 366 : 365)/*------------------------------------------------------------------------ * This macro can be added to a Julian date go shift it from a 12/31/1899- * based date to one starting at 01/01/0001.  This makes the calculations * a *lot* easier throughout. */#define	DATE_OFFSET ( \	  (1899 * 365) +	/* # of full years		*/\	  (1899 /   4) -	/* add in leap years		*/\	  (1899 / 100) +	/* take out div-by-100 leap yrs	*/\	  (1899 / 400) )	/* add back div-by-100 leap yrs *//*  * rdayofweek() * *	Given a Julian date, return its day of week: * *		0	Sunday *		1	Monday *		2	Tuesday *		3	Wednesday *		4	Thursday *		5	Friday *		6	Saturday * *	A date of DATENULL is considered to be Sunday, and there *	are no other error routines. */intrdayofweek(jdate_t jdate){	if (jdate == DATENULL)		/* a null date?			*/		return(0);		/* .. yup, it's a Sunday	*/	jdate += DATE_OFFSET;		/* convert to 1/1/1 dates	*/	return(jdate % 7);		/* 1/1/1 was a Sunday too	*/}/* * jdaysinyymm() * *	Given a full year and a month, return the number of days *	in the month.  This routine assumes that the input values *	are valid and does not do any checking on them.  Sorry. */static int jdaysinyymm(int yy, int mm){static const short days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int		   dd;	dd = days[mm];	if (mm == 2 && is_leap(yy))	/* leap year?	*/		dd++;	return(dd);}/* * rtoday() * *	Get today's date, convert it to Julian format, and stuff it *	in the long integer pointed to by pjdate; */int rtoday(jdate_t *pjdate){time_t		now;struct tm	*tm,		*localtime();short		mdy[3];	assert(pjdate != NULL);	/*----------------------------------------------------------------	 * get the current UNIX time and convert it to the time struct.	 */	(void)time(&now);	tm = localtime(&now);	/*----------------------------------------------------------------	 * build the mm/dd/yy array	 */	mdy[MM] = tm->tm_mon + 1;	mdy[DD] = tm->tm_mday;	mdy[YY] = 1900 + tm->tm_year;	/*----------------------------------------------------------------	 * finally, convert to Julian	 */	return(rmdyjul(mdy, pjdate));}/* * rjulmdy() * *	This function takes a Julian date and converts it into a *	month/day/year array.  The date is the number of days since *	12/31/1899, and any year from 1 to 9999 is allowed.  Return *	values are: * *		0	all is OK *		-1210	error in date: year>9999 or date is null * *	In event of error, the mdy[] array is zeroed out.  A date *	of null (DATENULL) zeroes out the array as well. */int rjulmdy(jdate_t jdate, short *mdy){short	mm = 1,	dd = 1,	yy = 1,	ndays;	assert(mdy != NULL);	/*----------------------------------------------------------------	 * first se if we have been passed a NULL date.  If so, set all the	 * mm/dd/yy values to indicate this and return the appropriate	 * error code for this.	 */	if (jdate == DATENULL)	{		mdy[MM] = mdy[DD] = mdy[YY] = 0;		return(-1210);	}	/*----------------------------------------------------------------	 * The Julian dates are based at 12/31/1899 but we can do our math	 * a lot easier if we start them at 1/1/1 -- that's why we add the	 * offset and calculate from there.	 */	jdate += DATE_OFFSET;	/*----------------------------------------------------------------	 * we have to first figure out the proper year.  We start with	 * year one and loop until the number of remaining days is less	 * than one year.	 */	while ((yy < 9999)  &&  (int)jdate > (ndays = daysinyear(yy)))	{		jdate -= ndays;		yy++;	}	/*----------------------------------------------------------------	 * If we have too many years, bomb out with an error.  This code	 * only works up to 9999 (oh well).	 */	if (yy > 9999)	{		mdy[MM] = mdy[DD] = mdy[YY] = 0;		return(-1210);	}	/*----------------------------------------------------------------	 * Now we have the proper year, so count down by months.	 */	while ( (int)jdate > (ndays = jdaysinyymm(yy, mm)))	{		jdate -= ndays;		mm++;	}	/*----------------------------------------------------------------	 * the only thing left is the number of days in the current month.	 */	dd = jdate;	/*----------------------------------------------------------------	 * all is OK, so assign the proper mm/dd/yy values and return OK.	 */	mdy[MM] = mm;	mdy[DD] = dd;	mdy[YY] = yy;	return(0);}/* * rmdyjul() * *	Given an array containing month/day/year values and a pointer *	to a long, convert the mdy[] date to the number of days since *	12/31/1899.  The year must be in full format (i.e. 19YY), and *	negative jdate values mean days before 12/31/1899. * *	Return values are: * *		    0	all is OK *		-1204	invalid year component (must be 1-9999) *		-1205	invalid month (must be 1-12) *		-1206	invalid day of month * *	The long date value is not touched if the input date is not *	valid, and in no case are the mdy[] elements touched.  If the *	mm/dd/yy are all zero, a null date is created. * *	Rather than have different algorithms before/after 12/31/1899, *	we calculate the number of days since 1/1/1 and then add in the *	"offset" to give it a 12/31/1899-based value. */int rmdyjul(const short *mdy, jdate_t *pjdate){static const short dy[2][13] = {	{ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },	{ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }};long	jdate = 0;		/* date being constructed	*/short	mm = mdy[MM],		/* current month  1-12		*/	dd = mdy[DD],		/* current day 1-31 (or so)	*/	yy = mdy[YY];		/* current year 1-9999		*/	assert(mdy    != NULL);	assert(pjdate != NULL);	/*----------------------------------------------------------------	 * see if the user has requested a NULL date.	 */	if (mm == 0 && dd == 0 && yy == 0)	{		*pjdate = DATENULL;		return(0);	}	/*----------------------------------------------------------------	 * first make sure that the input parameters are not out of bounds.	 * If so, return the proper error codes for them (all negative).	 */	if (yy < 1 || yy > 9999)		/* valid year?		*/		return(-1204);	if (mm < 1 || mm > 12)			/* valid month?		*/		return(-1205);	if (dd < 1 || dd > jdaysinyymm(yy, mm))	/* valid day?		*/		return(-1206);	/*----------------------------------------------------------------	 * calculate the number of days since 1/1/1 (the first day of	 * the A.D.).	 */	jdate += (yy-1) * 365;		/* add in full years		*/	jdate += (yy-1) / 4;		/* add in leap year days	*/	jdate -= (yy-1) / 100;		/* sub out div-by-100		*/	jdate += (yy-1) / 400;		/* add back div-by-400		*/	jdate += dy[is_leap(yy)][mm];	/* add in the months		*/	jdate += dd;			/* add in the days		*/	/*----------------------------------------------------------------	 * store the date after subtracting the offset.  This calculation	 * is simply the number of days before 1/1/1900.	 */	*pjdate = jdate - DATE_OFFSET;	return(0);}/* * rdatestr() * *	Given a Julian date, convert it to a string in MM/DD/YYYY *	format.  The output buffer must be at least 11 bytes long. *	A date of DATENULL is converted to a buffer of all spaces, *	and in all cases the output buffer is NUL terminated. * *	This routine does not use sprintf(). */int rdatestr(jdate_t jdate, char *str){int	rv;short	mdy[3];	assert(str != NULL);	/*----------------------------------------------------------------	 * if the date is NULL, convert everything to spaces and return	 * success.	 */	if (jdate == DATENULL)	{		memset(str, ' ', 10);		str[10] = '\0';		return(0);	}	/*----------------------------------------------------------------	 * first convert from the Julian to the individual pieces.  If	 * there is an error, return that error code.	 */	if ( (rv = rjulmdy(jdate, mdy)) < 0)		return(rv);	/*----------------------------------------------------------------	 * now go from the mdy[] array and build the string.  We avoid	 * sprintf() because it is so big and this is probably lots	 * faster anyway.	 */	*str++ = (mdy[MM] / 10) + '0';	*str++ = (mdy[MM] % 10) + '0';	*str++ = '/';	*str++ = (mdy[DD] / 10) + '0';	*str++ = (mdy[DD] % 10) + '0';	*str++ = '/';	*str++ = ((mdy[YY] / 1000)  % 10) + '0';	*str++ = ((mdy[YY] /  100)  % 10) + '0';	*str++ = ((mdy[YY] /   10)  % 10) + '0';	*str++ = ((mdy[YY]       )  % 10) + '0';	*str = '\0';	return(0);}/* * rfmtdate() * *	This function takes a Julian date, a format string, and *	and output buffer and formats the date into the buffer. *	The format string can contain: * *		mm		month number (01-12) *		mmm		month abbrev (Jan-Dec) *		dd		day number (01-31) *		ddd		day of week abbrev (Sun-Sat) *		yy		last two digits of year *		yyyy		full year notation * *	Any other characters appear literally in the output string. * *	For instance, * *		rfmtdate(jdate, "dd mmm, yyyy", outbuf); * *	might yield * *			"07-Aug, 1989" * *	Return is zero if all is well and a negative value on error. *	It seems that the only cause of an error is an invalid jdate, *	and there is nothing else short of a NULL format string that *	does this. * *	===NOTE=== This is not defined by the interface, but it appears *	that if longer sequences of m/d/y are used, the entire sequence *	has the replacment text shoved into the rightmost portion.  For *	instance, a format of * *			"mmmmm ddddd yyyyy" *	yields		"mmDec ddTue y1989" * *	This suggests the algorithm used internally and we use it too. */int rfmtdate(jdate_t jdate, const char *format, char *outbuf){static const char daynames[]   = "SunMonTueWedThuFriSat";static const char monthnames[] = "___JanFebMarAprMayJunJulAugSepOctNovDec";short		mdy[3];char		*p;int		rv;	if ( (rv = rjulmdy(jdate, mdy)) < 0)		return(rv);	for (p = strcpy(outbuf, format); *p; p++)	{	char const	*constp;	char		*q;	int		len, c;		if (strchr("MmDdYy", *p) == NULL)			continue;			/*--------------------------------------------------------		 * We found a special sequence, so run to the end of it.		 */		q = p;		while (*++q == *p)			;		/*--------------------------------------------------------		 * At this point, `q' is pointing one character beyond		 * the end of the sequence of m or d or y.  If the length		 * is just one, then this is an ordinary character and		 * not part of any special sequence so skip processing.		 * If the length if greater than four, we treat it as		 * just four during this function.  Note that we only		 * use the length as an indicator and not as an index		 * into the string so this is OK.		 */		if (len = q - p, len <= 1)	/* too short?	*/			continue;		else if (len > 4)			len = 4;		/*--------------------------------------------------------		 * We multiply the character by the length to give us a		 * "key" into the switch.  Note that we use upper case		 * for all the indicators.		 */		c = isupper(*p) ? *p : toupper(*p);		switch (c * len)		{		/*--------------------------------------------------------		 * process a two-digit month number (01-12)		 */		  case 'M'*2:			/* 2-digit month	*/			*p++ = (mdy[MM]/10) + '0';			*p   = (mdy[MM]%10) + '0';			break;		/*--------------------------------------------------------		 * generate the three-charaacter month abbreviation.		 */		  case 'M'*3:			/* 3-char month abbrev	*/		  case 'M'*4:			p = q-3;			constp = &monthnames[mdy[MM]*3];			*p++ = *constp++;			*p++ = *constp++;			*p   = *constp;			break;		/*--------------------------------------------------------		 * process a two-digit day.		 */		  case 'D'*2:			/* 2-digit day		*/			*p++ = (mdy[DD]/10) + '0';			*p   = (mdy[DD]%10) + '0';			break;		/*--------------------------------------------------------		 * take care of the three-charaacter day-of-week abbrev.		 */		  case 'D'*3:		  case 'D'*4:			p = q-3;			constp = &daynames[rdayofweek(jdate)*3];			*p++ = *constp++;			*p++ = *constp++;			*p   = *constp;			break;		/*--------------------------------------------------------		 * The year is a bit different.  In all cases we have to		 * process the final two digits, and only in the case of		 * a length of four do we have to do the first two.		 */		  case 'Y'*4:			/* four-digit year	*/			p = q-4;			*p++ = (mdy[YY]/1000)    + '0';		/* thou */			*p++ = (mdy[YY]/ 100)%10 + '0';		/* hund */			/*FALLTHROUGH*/		  case 'Y'*2:			/* two-digit year	*/		  case 'Y'*3:			p = q-2;			*p++ = (mdy[YY]/10)%10 + '0';		/* tens	*/			*p   =  mdy[YY]    %10 + '0';		/* ones */			break;		}	}	return(0);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -