📄 time.c
字号:
/* Copyright (C) 2002 Manuel Novoa III * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! * * Besides uClibc, I'm using this code in my libc for elks, which is * a 16-bit environment with a fairly limited compiler. It would make * things much easier for me if this file isn't modified unnecessarily. * In particular, please put any new or replacement functions somewhere * else, and modify the makefile to use your version instead. * Thanks. Manuel * * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! *//* June 15, 2002 Initial Notes: * * Note: It is assumed throught that time_t is either long or unsigned long. * Similarly, clock_t is assumed to be long int. * * Warning: Assumptions are made about the layout of struct tm! It is * assumed that the initial fields of struct tm are (in order): * tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday * * Reached the inital goal of supporting the ANSI/ISO C99 time functions * as well as SUSv3's strptime. All timezone info is obtained from the * TZ env variable. * * Differences from glibc worth noting: * * Leap seconds are not considered here. * * glibc stores additional timezone info the struct tm, whereas we don't. * * Alternate digits and era handling are not currently implemented. * The modifiers are accepted, and tested for validity with the following * specifier, but are ignored otherwise. * * strftime does not implement glibc extension modifiers or widths for * conversion specifiers. However it does implement the glibc * extension specifiers %l, %k, and %s. It also recognizes %P, but * treats it as a synonym for %p; i.e. doesn't convert to lower case. * * strptime implements the glibc extension specifiers. However, it follows * SUSv3 in requiring at least one non-alphanumeric char between * conversion specifiers. Also, strptime only sets struct tm fields * for which format specifiers appear and does not try to infer other * fields (such as wday) as glibc's version does. * * TODO - Since glibc's %l and %k can space-pad their output in strftime, * it might be reasonable to eat whitespace first for those specifiers. * This could be done by pushing " %I" and " %H" respectively so that * leading whitespace is consumed. This is really only an issue if %l * or %k occurs at the start of the format string. * * TODO - Implement getdate? tzfile? struct tm extensions? * * TODO - Rework _time_mktime to remove the dependency on long long. *//* Oct 28, 2002 * * Fixed allowed char check for std and dst TZ fields. * * Added several options concerned with timezone support. The names will * probably change once Erik gets the new config system in place. * * Defining __TIME_TZ_FILE causes tzset() to attempt to read the TZ value * from the file /etc/TZ if the TZ env variable isn't set. The file contents * must be the intended value of TZ, followed by a newline. No other chars, * spacing, etc is allowed. As an example, an easy way for me to init * /etc/TZ appropriately would be: echo CST6CDT > /etc/TZ * * Defining __TIME_TZ_FILE_ONCE will cause all further accesses of /etc/TZ * to be skipped once a legal value has been read. * * Defining __TIME_TZ_OPT_SPEED will cause a tzset() to keep a copy of the * last TZ setting string and do a "fast out" if the current string is the * same. * * Nov 21, 2002 Fix an error return case in _time_mktime. * * Nov 26, 2002 Fix bug in setting daylight and timezone when no (valid) TZ. * Bug reported by Arne Bernin <arne@alamut.de> in regards to freeswan. */#define _GNU_SOURCE#define _STDIO_UTILITY#include <stdio.h>#include <stdlib.h>#include <stddef.h>#include <string.h>#include <time.h>#include <limits.h>#include <assert.h>#include <errno.h>#include <ctype.h>#include <langinfo.h>#include <locale.h>#ifndef __isleap#define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )#endif#ifndef TZNAME_MAX#define TZNAME_MAX _POSIX_TZNAME_MAX#endif/**********************************************************************//* The era code is currently unfinished. *//* #define ENABLE_ERA_CODE */#define __TIME_TZ_FILE/* #define __TIME_TZ_FILE_ONCE */#define __TIME_TZ_OPT_SPEED#define TZ_BUFLEN (2*TZNAME_MAX + 56)#ifdef __TIME_TZ_FILE#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include "paths.h"/* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul *//* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */#else /* __TIME_TZ_FILE */#undef __TIME_TZ_FILE_ONCE#endif /* __TIME_TZ_FILE *//**********************************************************************/extern struct tm __time_tm;typedef struct { long gmt_offset; long dst_offset; short day; /* for J or normal */ short week; short month; short rule_type; /* J, M, \0 */ char tzname[TZNAME_MAX+1];} rule_struct;#ifdef __UCLIBC_HAS_THREADS__#include <pthread.h>extern pthread_mutex_t _time_tzlock;#define TZLOCK pthread_mutex_lock(&_time_tzlock)#define TZUNLOCK pthread_mutex_unlock(&_time_tzlock)#else#define TZLOCK ((void) 0)#define TZUNLOCK ((void) 0)#endifextern rule_struct _time_tzinfo[2];extern struct tm *_time_t2tm(const time_t *__restrict timer, int offset, struct tm *__restrict result);extern time_t _time_mktime(struct tm *timeptr, int store_on_success);/**********************************************************************/#ifdef L_asctimestatic char __time_str[26];char *asctime(const struct tm *__restrict ptm){ return asctime_r(ptm, __time_str);}#endif/**********************************************************************/#ifdef L_asctime_r/* Strictly speaking, this implementation isn't correct. ANSI/ISO specifies * that the implementation of asctime() be equivalent to * * char *asctime(const struct tm *timeptr) * { * static char wday_name[7][3] = { * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" * }; * static char mon_name[12][3] = { * "Jan", "Feb", "Mar", "Apr", "May", "Jun", * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" * }; * static char result[26]; * * sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", * wday_name[timeptr->tm_wday], * mon_name[timeptr->tm_mon], * timeptr->tm_mday, timeptr->tm_hour, * timeptr->tm_min, timeptr->tm_sec, * 1900 + timeptr->tm_year); * return result; * } * * but the above is either inherently unsafe, or carries with it the implicit * assumption that all fields of timeptr fall within their usual ranges, and * that the tm_year value falls in the range [-2899,8099] to avoid overflowing * the static buffer. * * If we take the implicit assumption as given, then the implementation below * is still incorrect for tm_year values < -900, as there will be either * 0-padding and/or a missing negative sign for the year conversion . But given * the ususal use of asctime(), I think it isn't unreasonable to restrict correct * operation to the domain of years between 1000 and 9999. *//* This is generally a good thing, but if you're _sure_ any data passed will be * in range, you can #undef this. */#define SAFE_ASCTIME_R 1static const unsigned char at_data[] = { 'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd', 'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't', 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r', 'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g', 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c', #ifdef SAFE_ASCTIME_R '?', '?', '?', #endif ' ', '?', '?', '?', ' ', '0', offsetof(struct tm, tm_mday), ' ', '0', offsetof(struct tm, tm_hour), ':', '0', offsetof(struct tm, tm_min), ':', '0', offsetof(struct tm, tm_sec), ' ', '?', '?', '?', '?', '\n', 0};char *asctime_r(register const struct tm *__restrict ptm, register char *__restrict buffer){ int tmp; assert(ptm); assert(buffer);#ifdef SAFE_ASCTIME_R memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12)); if (((unsigned int)(ptm->tm_wday)) <= 6) { memcpy(buffer, at_data + 3 * ptm->tm_wday, 3); } if (((unsigned int)(ptm->tm_mon)) <= 11) { memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3); }#else assert(((unsigned int)(ptm->tm_wday)) <= 6); assert(((unsigned int)(ptm->tm_mon)) <= 11); memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12)); memcpy(buffer, at_data + 3 * ptm->tm_wday, 3); memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);#endif#ifdef SAFE_ASCTIME_R buffer += 19; tmp = ptm->tm_year + 1900; if (((unsigned int) tmp) < 10000) { buffer += 4; do { *buffer = '0' + (tmp % 10); tmp /= 10; } while (*--buffer == '?'); }#else /* SAFE_ASCTIME_R */ buffer += 23; tmp = ptm->tm_year + 1900; assert( ((unsigned int) tmp) < 10000 ); do { *buffer = '0' + (tmp % 10); tmp /= 10; } while (*--buffer == '?');#endif /* SAFE_ASCTIME_R */ do { --buffer; tmp = *((int *)(((const char *) ptm) + (int) *buffer));#ifdef SAFE_ASCTIME_R if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */ buffer[-1] = *buffer = '?'; } else#else /* SAFE_ASCTIME_R */ assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */#endif /* SAFE_ASCTIME_R */ { *buffer = '0' + (tmp % 10);#ifdef __BCC__ buffer[-1] = '0' + (tmp/10);#else /* __BCC__ */ buffer[-1] += (tmp/10);#endif /* __BCC__ */ } } while ((buffer -= 2)[-2] == '0'); if (*++buffer == '0') { /* Space-pad day of month. */ *buffer = ' '; } return buffer - 8;}#endif/**********************************************************************/#ifdef L_clock#include <sys/times.h>/* Note: According to glibc... * CAE XSH, Issue 4, Version 2: <time.h> * The value of CLOCKS_PER_SEC is required to be 1 million on all * XSI-conformant systems. */#ifndef __BCC__#if CLOCKS_PER_SEC != 1000000L#error unexpected value for CLOCKS_PER_SEC!#endif#endifclock_t clock(void){ struct tms xtms; unsigned long t; times(&xtms); t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;#ifndef __UCLIBC_CLK_TCK_CONST#error __UCLIBC_CLK_TCK_CONST not defined!#endif#undef CLK_TCK#define CLK_TCK __UCLIBC_CLK_TCK_CONST#if CLK_TCK > CLOCKS_PER_SEC#error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!#elif CLK_TCK < 1#error __UCLIBC_CLK_TCK_CONST < 1!#endif#if (CLK_TCK == CLOCKS_PER_SEC) return (t <= LONG_MAX) ? t : -1;#elif (CLOCKS_PER_SEC % CLK_TCK) == 0 return (t <= (LONG_MAX / (CLOCKS_PER_SEC/CLK_TCK))) ? t * (CLOCKS_PER_SEC/CLK_TCK) : -1;#else return (t <= ((LONG_MAX / CLOCKS_PER_SEC) * CLK_TCK + ((LONG_MAX % CLOCKS_PER_SEC) * CLK_TCK) / CLOCKS_PER_SEC)) ? (((t / CLK_TCK) * CLOCKS_PER_SEC) + (((t % CLK_TCK) * CLOCKS_PER_SEC) / CLK_TCK)) : -1;#endif}#endif/**********************************************************************/#ifdef L_ctimechar *ctime(const time_t *clock){ /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following. */ return asctime(localtime(clock));}#endif/**********************************************************************/#ifdef L_ctime_rchar *ctime_r(const time_t *clock, char *buf){ struct tm xtms; return asctime_r(localtime_r(clock, &xtms), buf);}#endif/**********************************************************************/#ifdef L_difftime#include <float.h>#if FLT_RADIX != 2#error difftime implementation assumptions violated for you arch!#endifdouble difftime(time_t time1, time_t time0){#if (LONG_MAX >> DBL_MANT_DIG) == 0 /* time_t fits in the mantissa of a double. */ return ((double) time1) - time0;#elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0 /* time_t can overflow the mantissa of a double. */ time_t t1, t0, d; d = ((time_t) 1) << DBL_MANT_DIG; t1 = time1 / d; time1 -= (t1 * d); t0 = time0 / d; time0 -= (t0*d); /* Since FLT_RADIX==2 and d is a power of 2, the only possible * rounding error in the expression below would occur from the * addition. */ return (((double) t1) - t0) * d + (((double) time1) - time0);#else#error difftime needs special implementation on your arch.#endif}#endif/**********************************************************************/#ifdef L_gmtimestruct tm *gmtime(const time_t *timer){ register struct tm *ptm = &__time_tm; _time_t2tm(timer, 0, ptm); /* Can return NULL... */ return ptm;}#endif/**********************************************************************/#ifdef L_gmtime_rstruct tm *gmtime_r(const time_t *__restrict timer, struct tm *__restrict result){ return _time_t2tm(timer, 0, result);}#endif/**********************************************************************/#ifdef L_localtimestruct tm *localtime(const time_t *timer){ register struct tm *ptm = &__time_tm; /* In this implementation, tzset() is called by localtime_r(). */ localtime_r(timer, ptm); /* Can return NULL... */ return ptm;}#endif/**********************************************************************/#ifdef L_localtime_rstatic const unsigned char day_cor[] = { /* non-leap */ 31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38/* 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7 *//* 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */};/* Note: timezone locking is done by localtime_r. */static int tm_isdst(register const struct tm *__restrict ptm){ register rule_struct *r = _time_tzinfo; long sec; int i, isdst, isleap, day, day0, monlen, mday; int oday=0; /* Note: oday can be uninitialized. */ isdst = 0; if (r[1].tzname[0] != 0) { /* First, get the current seconds offset from the start of the year. * Fields of ptm are assumed to be in their normal ranges. */ sec = ptm->tm_sec + 60 * (ptm->tm_min + 60 * (long)(ptm->tm_hour + 24 * ptm->tm_yday));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -