diff options
Diffstat (limited to 'lib/libc/stdtime')
-rw-r--r-- | lib/libc/stdtime/Makefile.inc | 35 | ||||
-rw-r--r-- | lib/libc/stdtime/Symbol.map | 43 | ||||
-rw-r--r-- | lib/libc/stdtime/ctime.3 | 402 | ||||
-rw-r--r-- | lib/libc/stdtime/strftime.3 | 287 | ||||
-rw-r--r-- | lib/libc/stdtime/strftime.c | 629 | ||||
-rw-r--r-- | lib/libc/stdtime/strptime.3 | 185 | ||||
-rw-r--r-- | lib/libc/stdtime/strptime.c | 709 | ||||
-rw-r--r-- | lib/libc/stdtime/time32.c | 97 | ||||
-rw-r--r-- | lib/libc/stdtime/timelocal.c | 153 | ||||
-rw-r--r-- | lib/libc/stdtime/timelocal.h | 61 | ||||
-rw-r--r-- | lib/libc/stdtime/tzset.3 | 344 |
11 files changed, 2945 insertions, 0 deletions
diff --git a/lib/libc/stdtime/Makefile.inc b/lib/libc/stdtime/Makefile.inc new file mode 100644 index 000000000000..647cbe6f40ba --- /dev/null +++ b/lib/libc/stdtime/Makefile.inc @@ -0,0 +1,35 @@ +# Makefile.inc,v 1.2 1994/09/13 21:26:01 wollman Exp + +.PATH: ${LIBC_SRCTOP}/stdtime ${SRCTOP}/contrib/tzcode + +TZCODE_SRCS= asctime.c difftime.c localtime.c +STDTIME_SRCS= strftime.c strptime.c timelocal.c +SRCS+= ${TZCODE_SRCS} ${STDTIME_SRCS} time32.c + +SYM_MAPS+= ${LIBC_SRCTOP}/stdtime/Symbol.map + +.for src in ${TZCODE_SRCS} ${STDTIME_SRCS} +CFLAGS.${src}+= -I${SRCTOP}/contrib/tzcode -include tzconfig.h +.endfor +.for src in ${STDTIME_SRCS} +CFLAGS.${src}+= -I${LIBC_SRCTOP}/stdtime +.endfor + +CFLAGS.localtime.c+= -DALL_STATE -DTHREAD_SAFE +.if ${MK_DETECT_TZ_CHANGES} != "no" +CFLAGS.localtime.c+= -DDETECT_TZ_CHANGES +CFLAGS.Version.map+= -DDETECT_TZ_CHANGES +.endif + +MAN+= ctime.3 strftime.3 strptime.3 time2posix.3 tzset.3 +MAN+= tzfile.5 + +MLINKS+=ctime.3 asctime.3 ctime.3 difftime.3 ctime.3 gmtime.3 \ + ctime.3 localtime.3 ctime.3 mktime.3 ctime.3 timegm.3 \ + ctime.3 ctime_r.3 ctime.3 localtime_r.3 ctime.3 gmtime_r.3 \ + ctime.3 asctime_r.3 +MLINKS+=strftime.3 strftime_l.3 +MLINKS+=strptime.3 strptime_l.3 +MLINKS+=time2posix.3 posix2time.3 +MLINKS+=tzset.3 daylight.3 \ + tzset.3 timezone.3 diff --git a/lib/libc/stdtime/Symbol.map b/lib/libc/stdtime/Symbol.map new file mode 100644 index 000000000000..6a34cd3ea590 --- /dev/null +++ b/lib/libc/stdtime/Symbol.map @@ -0,0 +1,43 @@ +FBSD_1.0 { + _time32_to_time; + _time_to_time32; + _time64_to_time; + _time_to_time64; + _time_to_long; + _long_to_time; + _time_to_int; + _int_to_time; + strptime; + strftime; + tzname; + tzsetwall; + tzset; + localtime; + localtime_r; + gmtime; + gmtime_r; + offtime; + offtime_r; + ctime; + ctime_r; + mktime; + timelocal; + timegm; + timeoff; + time2posix; + posix2time; + difftime; + asctime_r; + asctime; +}; + +FBSD_1.8 { + daylight; + timezone; +}; + +FBSDprivate_1.0 { +#ifdef DETECT_TZ_CHANGES + __tz_change_interval; +#endif +}; diff --git a/lib/libc/stdtime/ctime.3 b/lib/libc/stdtime/ctime.3 new file mode 100644 index 000000000000..96b7f775535a --- /dev/null +++ b/lib/libc/stdtime/ctime.3 @@ -0,0 +1,402 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Arthur Olson. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd March 26, 2024 +.Dt CTIME 3 +.Os +.Sh NAME +.Nm asctime , +.Nm asctime_r , +.Nm ctime , +.Nm ctime_r , +.Nm difftime , +.Nm gmtime , +.Nm gmtime_r , +.Nm localtime , +.Nm localtime_r , +.Nm mktime , +.Nm timegm +.Nd transform binary date and time values +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In time.h +.Vt extern char *tzname[2] ; +.Ft char * +.Fn asctime "const struct tm *tm" +.Ft char * +.Fn asctime_r "const struct tm *tm" "char *buf" +.Ft char * +.Fn ctime "const time_t *clock" +.Ft char * +.Fn ctime_r "const time_t *clock" "char *buf" +.Ft double +.Fn difftime "time_t time1" "time_t time0" +.Ft struct tm * +.Fn gmtime "const time_t *clock" +.Ft struct tm * +.Fn gmtime_r "const time_t *clock" "struct tm *result" +.Ft struct tm * +.Fn localtime "const time_t *clock" +.Ft struct tm * +.Fn localtime_r "const time_t *clock" "struct tm *result" +.Ft time_t +.Fn mktime "struct tm *tm" +.Ft time_t +.Fn timegm "struct tm *tm" +.Sh DESCRIPTION +The +.Fn ctime , +.Fn gmtime , +and +.Fn localtime +functions all take as argument a pointer to a time value representing +the time in seconds since the Epoch (00:00:00 UTC on January 1, 1970; +see +.Xr time 3 ) . +.Pp +The +.Fn localtime +function converts the time value pointed to by +.Fa clock , +and returns a pointer to a +.Vt struct tm +(described below) which contains +the broken-out time information for the value after adjusting for the current +time zone (see +.Xr tzset 3 ) . +When the specified time translates to a year that will not fit in an +.Vt int , +.Fn localtime +returns +.Dv NULL . +The +.Fn localtime +function uses +.Xr tzset 3 +to initialize time conversion information if +.Xr tzset 3 +has not already been called by the process. +.Pp +After filling in the +.Vt struct tm , +.Fn localtime +sets the +.Va tm_isdst Ns 'th +element of +.Va tzname +to a pointer to an ASCII string that is the time zone abbreviation to be +used with +.Fn localtime Ns 's +return value. +.Pp +The +.Fn gmtime +function similarly converts the time value, but without any time zone +adjustment, and returns a pointer to a +.Vt struct tm . +.Pp +The +.Fn ctime +function +adjusts the time value for the current time zone in the same manner as +.Fn localtime , +and returns a pointer to a 26-character string of the form: +.Bd -literal -offset indent +Thu Nov 24 18:22:48 1986\en\e0 +.Ed +.Pp +All the fields have constant width. +.Pp +The +.Fn asctime +function converts the broken down time in the +.Vt struct tm +pointed to by +.Fa tm +to the form shown in the example above. +.Pp +The +.Fn ctime_r +and +.Fn asctime_r +functions +provide the same functionality as +.Fn ctime +and +.Fn asctime +except the caller must provide the output buffer +.Fa buf , +which must be at least 26 characters long, to store the result in. +The +.Fn localtime_r +and +.Fn gmtime_r +functions provide the same functionality as +.Fn localtime +and +.Fn gmtime +respectively, except the caller must provide the output buffer +.Fa result . +.Pp +The functions +.Fn mktime +and +.Fn timegm +convert the broken-down time in the +.Vt struct tm +pointed to by +.Fa tm +into a time value with the same encoding as that of the values +returned by the +.Xr time 3 +function (that is, seconds from the Epoch, UTC). +The +.Fn mktime +function interprets the input structure according to the current +timezone setting (see +.Xr tzset 3 ) +while the +.Fn timegm +function interprets the input structure as representing Universal +Coordinated Time +.Pq UTC . +.Pp +The original values of the +.Fa tm_wday +and +.Fa tm_yday +components of the structure are ignored, and the original values of the +other components are not restricted to their normal ranges, and will be +normalized if needed. +For example, +October 40 is changed into November 9, +a +.Fa tm_hour +of \-1 means 1 hour before midnight, +.Fa tm_mday +of 0 means the day preceding the current month, and +.Fa tm_mon +of \-2 means 2 months before January of +.Fa tm_year . +(A positive or zero value for +.Fa tm_isdst +causes +.Fn mktime +to presume initially that summer time (for example, Daylight Saving Time) +is or is not in effect for the specified time, respectively. +A negative value for +.Fa tm_isdst +causes the +.Fn mktime +function to attempt to guess whether summer time is in effect for the +specified time. +The +.Fa tm_isdst +and +.Fa tm_gmtoff +members are forced to zero by +.Fn timegm . ) +.Pp +On successful completion, the values of the +.Fa tm_wday +and +.Fa tm_yday +components of the structure are set appropriately, and the other components +are set to represent the specified calendar time, but with their values +forced to their normal ranges; the final value of +.Fa tm_mday +is not set until +.Fa tm_mon +and +.Fa tm_year +are determined. +The +.Fn mktime +function +returns the specified calendar time; if the calendar time cannot be +represented, it returns \-1 and sets +.Xr errno 3 +to an appropriate value. +.Pp +Note that \-1 is a valid result (representing one second before +midnight UTC on the evening of 31 December 1969), so this cannot be +relied upon to indicate success or failure; instead, +.Fa tm_wday +and / or +.Fa tm_yday +should be set to an out-of-bounds value (e.g. \-1) prior to calling +.Fn mktime +or +.Fn timegm +and checked after the call. +.Pp +The +.Fn difftime +function returns the difference in seconds between two time values, +.Fa time1 +\- +.Fa time0 . +.Pp +External declarations as well as the definition of +.Vt struct tm +are in the +.In time.h +header. +The +.Vt tm +structure includes at least the following fields: +.Bd -literal -offset indent +int tm_sec; /* seconds (0 - 60) */ +int tm_min; /* minutes (0 - 59) */ +int tm_hour; /* hours (0 - 23) */ +int tm_mday; /* day of month (1 - 31) */ +int tm_mon; /* month of year (0 - 11) */ +int tm_year; /* year \- 1900 */ +int tm_wday; /* day of week (Sunday = 0) */ +int tm_yday; /* day of year (0 - 365) */ +int tm_isdst; /* is summer time in effect? */ +char *tm_zone; /* abbreviation of timezone name */ +long tm_gmtoff; /* offset from UTC in seconds */ +.Ed +.Pp +The +.Fa tm_isdst +field is non-zero if summer time is in effect. +.Pp +The +.Fa tm_gmtoff +field is the offset in seconds of the time represented from UTC, +with positive values indicating a time zone ahead of UTC (east of the +Prime Meridian). +.Sh SEE ALSO +.Xr date 1 , +.Xr clock_gettime 2 , +.Xr gettimeofday 2 , +.Xr getenv 3 , +.Xr time 3 , +.Xr tzset 3 , +.Xr tzfile 5 +.Sh STANDARDS +The +.Fn asctime , +.Fn ctime , +.Fn difftime , +.Fn gmtime , +.Fn localtime , +and +.Fn mktime +functions conform to +.St -isoC , +and conform to +.St -p1003.1-96 +provided the selected local timezone does not contain a leap-second table +(see +.Xr zic 8 ) . +.Pp +The +.Fn asctime_r , +.Fn ctime_r , +.Fn gmtime_r , +and +.Fn localtime_r +functions are expected to conform to +.St -p1003.1-96 +(again provided the selected local timezone does not contain a leap-second +table). +.Pp +The +.Fn timegm +function is not specified by any standard; its function cannot be +completely emulated using the standard functions described above. +.Sh HISTORY +This manual page is derived from +the time package contributed to Berkeley by +.An Arthur Olson +and which appeared in +.Bx 4.3 . +.Pp +The functions +.Fn asctime , +.Fn gmtime , +and +.Fn localtime +first appeared in +.At v5 , +.Fn difftime +and +.Fn mktime +in +.Bx 4.3 Reno , +and +.Fn timegm +and +.Fn timelocal +in SunOS 4.0. +.Pp +The +.Fn asctime_r , +.Fn ctime_r , +.Fn gmtime_r +and +.Fn localtime_r +functions have been available since +.Fx 8.0 . +.Sh BUGS +Except for +.Fn difftime , +.Fn mktime , +and the +.Fn \&_r +variants of the other functions, +these functions leave their result in an internal static object and return +a pointer to that object. +Subsequent calls to these +function will modify the same object. +.Pp +The C Standard provides no mechanism for a program to modify its current +local timezone setting, and the POSIX-standard +method is not reentrant. +(However, thread-safe implementations are provided +in the POSIX threaded environment.) +.Pp +The +.Va tm_zone +field of a returned +.Vt tm +structure points to a static array of characters, +which will also be overwritten by any subsequent calls (as well as by +subsequent calls to +.Xr tzset 3 ) . +.Pp +Use of the external variable +.Fa tzname +is discouraged; the +.Fa tm_zone +entry in the tm structure is preferred. diff --git a/lib/libc/stdtime/strftime.3 b/lib/libc/stdtime/strftime.3 new file mode 100644 index 000000000000..f46eee525900 --- /dev/null +++ b/lib/libc/stdtime/strftime.3 @@ -0,0 +1,287 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the American National Standards Committee X3, on Information +.\" Processing Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd June 25, 2012 +.Dt STRFTIME 3 +.Os +.Sh NAME +.Nm strftime +.Nd format date and time +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In time.h +.Ft size_t +.Fo strftime +.Fa "char * restrict buf" +.Fa "size_t maxsize" +.Fa "const char * restrict format" +.Fa "const struct tm * restrict timeptr" +.Fc +.Ft size_t +.Fn strftime_l "char *restrict buf" "size_t maxsize" "const char * restrict format" "const struct tm *restrict timeptr" "locale_t loc" +.Sh DESCRIPTION +The +.Fn strftime +function formats the information from +.Fa timeptr +into the buffer +.Fa buf +according to the string pointed to by +.Fa format . +The function +.Fn strftime_l +does the same as +.Fn strftime +but takes an explicit locale rather than using the current locale. +.Pp +The +.Fa format +string consists of zero or more conversion specifications and +ordinary characters. +All ordinary characters are copied directly into the buffer. +A conversion specification consists of a percent sign +.Dq Ql % +and one other character. +.Pp +No more than +.Fa maxsize +characters will be placed into the array. +If the total number of resulting characters, including the terminating +NUL character, is not more than +.Fa maxsize , +.Fn strftime +returns the number of characters in the array, not counting the +terminating NUL. +Otherwise, zero is returned and the buffer contents are indeterminate. +.Pp +The conversion specifications are copied to the buffer after expansion +as follows: +.Bl -tag -width "xxxx" +.It Cm \&%A +is replaced by national representation of the full weekday name. +.It Cm %a +is replaced by national representation of +the abbreviated weekday name. +.It Cm \&%B +is replaced by national representation of the full month name. +.It Cm %b +is replaced by national representation of +the abbreviated month name. +.It Cm \&%C +is replaced by (year / 100) as decimal number; single +digits are preceded by a zero. +.It Cm %c +is replaced by national representation of time and date. +.It Cm \&%D +is equivalent to +.Dq Li %m/%d/%y . +.It Cm %d +is replaced by the day of the month as a decimal number (01-31). +.It Cm %E* %O* +POSIX locale extensions. +The sequences +%Ec %EC %Ex %EX %Ey %EY +%Od %Oe %OH %OI %Om %OM +%OS %Ou %OU %OV %Ow %OW %Oy +are supposed to provide alternate +representations. +.Pp +Additionally %OB implemented +to represent alternative months names +(used standalone, without day mentioned). +.It Cm %e +is replaced by the day of the month as a decimal number (1-31); single +digits are preceded by a blank. +.It Cm \&%F +is equivalent to +.Dq Li %Y-%m-%d . +.It Cm \&%G +is replaced by a year as a decimal number with century. +This year is the one that contains the greater part of +the week (Monday as the first day of the week). +.It Cm %g +is replaced by the same year as in +.Dq Li %G , +but as a decimal number without century (00-99). +.It Cm \&%H +is replaced by the hour (24-hour clock) as a decimal number (00-23). +.It Cm %h +the same as +.Cm %b . +.It Cm \&%I +is replaced by the hour (12-hour clock) as a decimal number (01-12). +.It Cm %j +is replaced by the day of the year as a decimal number (001-366). +.It Cm %k +is replaced by the hour (24-hour clock) as a decimal number (0-23); +single digits are preceded by a blank. +.It Cm %l +is replaced by the hour (12-hour clock) as a decimal number (1-12); +single digits are preceded by a blank. +.It Cm \&%M +is replaced by the minute as a decimal number (00-59). +.It Cm %m +is replaced by the month as a decimal number (01-12). +.It Cm %n +is replaced by a newline. +.It Cm %O* +the same as +.Cm %E* . +.It Cm %p +is replaced by national representation of either +"ante meridiem" (a.m.) +or +"post meridiem" (p.m.) +as appropriate. +.It Cm \&%R +is equivalent to +.Dq Li %H:%M . +.It Cm %r +is equivalent to +.Dq Li %I:%M:%S %p . +.It Cm \&%S +is replaced by the second as a decimal number (00-60). +.It Cm %s +is replaced by the number of seconds since the Epoch, UTC (see +.Xr mktime 3 ) . +.It Cm \&%T +is equivalent to +.Dq Li %H:%M:%S . +.It Cm %t +is replaced by a tab. +.It Cm \&%U +is replaced by the week number of the year (Sunday as the first day of +the week) as a decimal number (00-53). +.It Cm %u +is replaced by the weekday (Monday as the first day of the week) +as a decimal number (1-7). +.It Cm \&%V +is replaced by the week number of the year (Monday as the first day of +the week) as a decimal number (01-53). +If the week containing January +1 has four or more days in the new year, then it is week 1; otherwise +it is the last week of the previous year, and the next week is week 1. +.It Cm %v +is equivalent to +.Dq Li %e-%b-%Y . +.It Cm \&%W +is replaced by the week number of the year (Monday as the first day of +the week) as a decimal number (00-53). +.It Cm %w +is replaced by the weekday (Sunday as the first day of the week) +as a decimal number (0-6). +.It Cm \&%X +is replaced by national representation of the time. +.It Cm %x +is replaced by national representation of the date. +.It Cm \&%Y +is replaced by the year with century as a decimal number. +.It Cm %y +is replaced by the year without century as a decimal number (00-99). +.It Cm \&%Z +is replaced by the time zone name. +.It Cm %z +is replaced by the time zone offset from UTC; a leading plus sign stands for +east of UTC, a minus sign for west of UTC, hours and minutes follow +with two digits each and no delimiter between them (common form for +RFC 822 date headers). +.It Cm %+ +is replaced by national representation of the date and time +(the format is similar to that produced by +.Xr date 1 ) . +.It Cm %-* +GNU libc extension. +Do not do any padding when performing numerical outputs. +.It Cm %_* +GNU libc extension. +Explicitly specify space for padding. +.It Cm %0* +GNU libc extension. +Explicitly specify zero for padding. +.It Cm %% +is replaced by +.Ql % . +.El +.Sh SEE ALSO +.Xr date 1 , +.Xr printf 1 , +.Xr ctime 3 , +.Xr printf 3 , +.Xr strptime 3 , +.Xr wcsftime 3 +.Sh STANDARDS +The +.Fn strftime +function +conforms to +.St -isoC +with a lot of extensions including +.Ql \&%C , +.Ql \&%D , +.Ql %E* , +.Ql %e , +.Ql %G , +.Ql %g , +.Ql %h , +.Ql %k , +.Ql %l , +.Ql %n , +.Ql %O* , +.Ql \&%R , +.Ql %r , +.Ql %s , +.Ql \&%T , +.Ql %t , +.Ql %u , +.Ql \&%V , +.Ql %z , +.Ql %+ . +.Pp +The peculiar week number and year in the replacements of +.Ql %G , +.Ql %g +and +.Ql \&%V +are defined in +.St -iso8601 . +The +.Fn strftime_l +function conforms to +.St -p1003.1-2008 . +.Sh BUGS +There is no conversion specification for the phase of the moon. +.Pp +The +.Fn strftime +function does not correctly handle multibyte characters in the +.Fa format +argument. diff --git a/lib/libc/stdtime/strftime.c b/lib/libc/stdtime/strftime.c new file mode 100644 index 000000000000..89c9e4cf88ff --- /dev/null +++ b/lib/libc/stdtime/strftime.c @@ -0,0 +1,629 @@ +/* + * SPDX-License-Identifier: BSD-4.3TAHOE + * + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Copyright (c) 2011 The FreeBSD Foundation + * + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "namespace.h" +#include "private.h" + +#include "tzfile.h" +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> +#include "un-namespace.h" +#include "timelocal.h" + +static char * _add(const char *, char *, const char *); +static char * _conv(int, const char *, char *, const char *, locale_t); +static char * _fmt(const char *, const struct tm *, char *, const char *, + int *, locale_t); +static char * _yconv(int, int, int, int, char *, const char *, locale_t); + +extern char * tzname[]; + +#ifndef YEAR_2000_NAME +#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" +#endif /* !defined YEAR_2000_NAME */ + +#define IN_NONE 0 +#define IN_SOME 1 +#define IN_THIS 2 +#define IN_ALL 3 + +#define PAD_DEFAULT 0 +#define PAD_LESS 1 +#define PAD_SPACE 2 +#define PAD_ZERO 3 + +static const char fmt_padding[][4][5] = { + /* DEFAULT, LESS, SPACE, ZERO */ +#define PAD_FMT_MONTHDAY 0 +#define PAD_FMT_HMS 0 +#define PAD_FMT_CENTURY 0 +#define PAD_FMT_SHORTYEAR 0 +#define PAD_FMT_MONTH 0 +#define PAD_FMT_WEEKOFYEAR 0 +#define PAD_FMT_DAYOFMONTH 0 + { "%02d", "%d", "%2d", "%02d" }, +#define PAD_FMT_SDAYOFMONTH 1 +#define PAD_FMT_SHMS 1 + { "%2d", "%d", "%2d", "%02d" }, +#define PAD_FMT_DAYOFYEAR 2 + { "%03d", "%d", "%3d", "%03d" }, +#define PAD_FMT_YEAR 3 + { "%04d", "%d", "%4d", "%04d" } +}; + +size_t +strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format, + const struct tm * __restrict t, locale_t loc) +{ + char * p; + int warn; + FIX_LOCALE(loc); + + tzset(); + warn = IN_NONE; + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, loc); +#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU + if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { + (void) fprintf_l(stderr, loc, "\n"); + if (format == NULL) + (void) fputs("NULL strftime format ", stderr); + else (void) fprintf_l(stderr, loc, "strftime format \"%s\" ", + format); + (void) fputs("yields only two digits of years in ", stderr); + if (warn == IN_SOME) + (void) fputs("some locales", stderr); + else if (warn == IN_THIS) + (void) fputs("the current locale", stderr); + else (void) fputs("all locales", stderr); + (void) fputs("\n", stderr); + } +#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ + if (p == s + maxsize) + return (0); + *p = '\0'; + return p - s; +} + +size_t +strftime(char * __restrict s, size_t maxsize, const char * __restrict format, + const struct tm * __restrict t) +{ + return strftime_l(s, maxsize, format, t, __get_locale()); +} + +static char * +_fmt(const char *format, const struct tm * const t, char *pt, + const char * const ptlim, int *warnp, locale_t loc) +{ + int Ealternative, Oalternative, PadIndex; + struct lc_time_T *tptr = __get_current_time_locale(loc); + + for ( ; *format; ++format) { + if (*format == '%') { + Ealternative = 0; + Oalternative = 0; + PadIndex = PAD_DEFAULT; +label: + switch (*++format) { + case '\0': + --format; + break; + case 'A': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : tptr->weekday[t->tm_wday], + pt, ptlim); + continue; + case 'a': + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? + "?" : tptr->wday[t->tm_wday], + pt, ptlim); + continue; + case 'B': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : (Oalternative ? tptr->alt_month : + tptr->month)[t->tm_mon], + pt, ptlim); + continue; + case 'b': + case 'h': + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? + "?" : tptr->mon[t->tm_mon], + pt, ptlim); + continue; + case 'C': + /* + * %C used to do a... + * _fmt("%a %b %e %X %Y", t); + * ...whereas now POSIX 1003.2 calls for + * something completely different. + * (ado, 1993-05-24) + */ + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, + pt, ptlim, loc); + continue; + case 'c': + { + int warn2 = IN_SOME; + + pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2, loc); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'D': + pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, loc); + continue; + case 'd': + pt = _conv(t->tm_mday, + fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex], + pt, ptlim, loc); + continue; + case 'E': + if (Ealternative || Oalternative) + break; + Ealternative++; + goto label; + case 'O': + /* + * C99 locale modifiers. + * The sequences + * %Ec %EC %Ex %EX %Ey %EY + * %Od %oe %OH %OI %Om %OM + * %OS %Ou %OU %OV %Ow %OW %Oy + * are supposed to provide alternate + * representations. + * + * FreeBSD extension + * %OB + */ + if (Ealternative || Oalternative) + break; + Oalternative++; + goto label; + case 'e': + pt = _conv(t->tm_mday, + fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], + pt, ptlim, loc); + continue; + case 'F': + pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, loc); + continue; + case 'H': + pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex], + pt, ptlim, loc); + continue; + case 'I': + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + fmt_padding[PAD_FMT_HMS][PadIndex], + pt, ptlim, loc); + continue; + case 'j': + pt = _conv(t->tm_yday + 1, + fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], + pt, ptlim, loc); + continue; + case 'k': + /* + * This used to be... + * _conv(t->tm_hour % 12 ? + * t->tm_hour % 12 : 12, 2, ' '); + * ...and has been changed to the below to + * match SunOS 4.1.1 and Arnold Robbins' + * strftime version 3.0. That is, "%k" and + * "%l" have been swapped. + * (ado, 1993-05-24) + */ + pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex], + pt, ptlim, loc); + continue; +#ifdef KITCHEN_SINK + case 'K': + /* + ** After all this time, still unclaimed! + */ + pt = _add("kitchen sink", pt, ptlim); + continue; +#endif /* defined KITCHEN_SINK */ + case 'l': + /* + * This used to be... + * _conv(t->tm_hour, 2, ' '); + * ...and has been changed to the below to + * match SunOS 4.1.1 and Arnold Robbin's + * strftime version 3.0. That is, "%k" and + * "%l" have been swapped. + * (ado, 1993-05-24) + */ + pt = _conv((t->tm_hour % 12) ? + (t->tm_hour % 12) : 12, + fmt_padding[PAD_FMT_SHMS][PadIndex], + pt, ptlim, loc); + continue; + case 'M': + pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex], + pt, ptlim, loc); + continue; + case 'm': + pt = _conv(t->tm_mon + 1, + fmt_padding[PAD_FMT_MONTH][PadIndex], + pt, ptlim, loc); + continue; + case 'n': + pt = _add("\n", pt, ptlim); + continue; + case 'p': + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? + tptr->pm : tptr->am, + pt, ptlim); + continue; + case 'R': + pt = _fmt("%H:%M", t, pt, ptlim, warnp, loc); + continue; + case 'r': + pt = _fmt(tptr->ampm_fmt, t, pt, ptlim, + warnp, loc); + continue; + case 'S': + pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex], + pt, ptlim, loc); + continue; + case 's': + { + struct tm tm; + char buf[INT_STRLEN_MAXIMUM( + time_t) + 1]; + time_t mkt; + + tm = *t; + mkt = timeoff(&tm, t->tm_gmtoff); + if (TYPE_SIGNED(time_t)) + (void) sprintf_l(buf, loc, "%ld", + (long) mkt); + else (void) sprintf_l(buf, loc, "%lu", + (unsigned long) mkt); + pt = _add(buf, pt, ptlim); + } + continue; + case 'T': + pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, loc); + continue; + case 't': + pt = _add("\t", pt, ptlim); + continue; + case 'U': + pt = _conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, + fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], + pt, ptlim, loc); + continue; + case 'u': + /* + * From Arnold Robbins' strftime version 3.0: + * "ISO 8601: Weekday as a decimal number + * [1 (Monday) - 7]" + * (ado, 1993-05-24) + */ + pt = _conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, + "%d", pt, ptlim, loc); + continue; + case 'V': /* ISO 8601 week number */ + case 'G': /* ISO 8601 year (four digits) */ + case 'g': /* ISO 8601 year (two digits) */ +/* + * From Arnold Robbins' strftime version 3.0: "the week number of the + * year (the first Monday as the first day of week 1) as a decimal number + * (01-53)." + * (ado, 1993-05-24) + * + * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: + * "Week 01 of a year is per definition the first week which has the + * Thursday in this year, which is equivalent to the week which contains + * the fourth day of January. In other words, the first week of a new year + * is the week which has the majority of its days in the new year. Week 01 + * might also contain days from the previous year and the week before week + * 01 of a year is the last week (52 or 53) of the previous year even if + * it contains days from the new year. A week starts with Monday (day 1) + * and ends with Sunday (day 7). For example, the first week of the year + * 1997 lasts from 1996-12-30 to 1997-01-05..." + * (ado, 1996-01-02) + */ + { + int year; + int base; + int yday; + int wday; + int w; + + year = t->tm_year; + base = TM_YEAR_BASE; + yday = t->tm_yday; + wday = t->tm_wday; + for ( ; ; ) { + int len; + int bot; + int top; + + len = isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + /* + * What yday (-3 ... 3) does + * the ISO year begin on? + */ + bot = ((yday + 11 - wday) % + DAYSPERWEEK) - 3; + /* + * What yday does the NEXT + * ISO year begin on? + */ + top = bot - + (len % DAYSPERWEEK); + if (top < -3) + top += DAYSPERWEEK; + top += len; + if (yday >= top) { + ++base; + w = 1; + break; + } + if (yday >= bot) { + w = 1 + ((yday - bot) / + DAYSPERWEEK); + break; + } + --base; + yday += isleap_sum(year, base) ? + DAYSPERLYEAR : + DAYSPERNYEAR; + } +#ifdef XPG4_1994_04_09 + if ((w == 52 && + t->tm_mon == TM_JANUARY) || + (w == 1 && + t->tm_mon == TM_DECEMBER)) + w = 53; +#endif /* defined XPG4_1994_04_09 */ + if (*format == 'V') + pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], + pt, ptlim, loc); + else if (*format == 'g') { + *warnp = IN_ALL; + pt = _yconv(year, base, 0, 1, + pt, ptlim, loc); + } else pt = _yconv(year, base, 1, 1, + pt, ptlim, loc); + } + continue; + case 'v': + /* + * From Arnold Robbins' strftime version 3.0: + * "date as dd-bbb-YYYY" + * (ado, 1993-05-24) + */ + pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, loc); + continue; + case 'W': + pt = _conv((t->tm_yday + DAYSPERWEEK - + (t->tm_wday ? + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, + fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], + pt, ptlim, loc); + continue; + case 'w': + pt = _conv(t->tm_wday, "%d", pt, ptlim, loc); + continue; + case 'X': + pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp, loc); + continue; + case 'x': + { + int warn2 = IN_SOME; + + pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2, loc); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } + continue; + case 'y': + *warnp = IN_ALL; + pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, + pt, ptlim, loc); + continue; + case 'Y': + pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, + pt, ptlim, loc); + continue; + case 'Z': +#ifdef TM_ZONE + if (t->TM_ZONE != NULL) + pt = _add(t->TM_ZONE, pt, ptlim); + else +#endif /* defined TM_ZONE */ + if (t->tm_isdst >= 0) + pt = _add(tzname[t->tm_isdst != 0], + pt, ptlim); + /* + * C99 says that %Z must be replaced by the + * empty string if the time zone is not + * determinable. + */ + continue; + case 'z': + { + int diff; + char const * sign; + + if (t->tm_isdst < 0) + continue; +#ifdef TM_GMTOFF + diff = t->TM_GMTOFF; +#else /* !defined TM_GMTOFF */ + /* + * C99 says that the UTC offset must + * be computed by looking only at + * tm_isdst. This requirement is + * incorrect, since it means the code + * must rely on magic (in this case + * altzone and timezone), and the + * magic might not have the correct + * offset. Doing things correctly is + * tricky and requires disobeying C99; + * see GNU C strftime for details. + * For now, punt and conform to the + * standard, even though it's incorrect. + * + * C99 says that %z must be replaced by the + * empty string if the time zone is not + * determinable, so output nothing if the + * appropriate variables are not available. + */ + if (t->tm_isdst == 0) +#ifdef USG_COMPAT + diff = -timezone; +#else /* !defined USG_COMPAT */ + continue; +#endif /* !defined USG_COMPAT */ + else +#ifdef ALTZONE + diff = -altzone; +#else /* !defined ALTZONE */ + continue; +#endif /* !defined ALTZONE */ +#endif /* !defined TM_GMTOFF */ + if (diff < 0) { + sign = "-"; + diff = -diff; + } else + sign = "+"; + pt = _add(sign, pt, ptlim); + diff /= SECSPERMIN; + diff = (diff / MINSPERHOUR) * 100 + + (diff % MINSPERHOUR); + pt = _conv(diff, + fmt_padding[PAD_FMT_YEAR][PadIndex], + pt, ptlim, loc); + } + continue; + case '+': + pt = _fmt(tptr->date_fmt, t, pt, ptlim, + warnp, loc); + continue; + case '-': + if (PadIndex != PAD_DEFAULT) + break; + PadIndex = PAD_LESS; + goto label; + case '_': + if (PadIndex != PAD_DEFAULT) + break; + PadIndex = PAD_SPACE; + goto label; + case '0': + if (PadIndex != PAD_DEFAULT) + break; + PadIndex = PAD_ZERO; + goto label; + case '%': + /* + * X311J/88-090 (4.12.3.5): if conversion char is + * undefined, behavior is undefined. Print out the + * character itself as printf(3) also does. + */ + default: + break; + } + } + if (pt == ptlim) + break; + *pt++ = *format; + } + return (pt); +} + +static char * +_conv(const int n, const char * const format, char * const pt, + const char * const ptlim, locale_t loc) +{ + char buf[INT_STRLEN_MAXIMUM(int) + 1]; + + (void) sprintf_l(buf, loc, format, n); + return _add(buf, pt, ptlim); +} + +static char * +_add(const char *str, char *pt, const char * const ptlim) +{ + while (pt < ptlim && (*pt = *str++) != '\0') + ++pt; + return (pt); +} + +/* + * POSIX and the C Standard are unclear or inconsistent about + * what %C and %y do if the year is negative or exceeds 9999. + * Use the convention that %C concatenated with %y yields the + * same output as %Y, and that %Y contains at least 4 bytes, + * with more only if necessary. + */ + +static char * +_yconv(const int a, const int b, const int convert_top, const int convert_yy, + char *pt, const char * const ptlim, locale_t loc) +{ + register int lead; + register int trail; + +#define DIVISOR 100 + trail = a % DIVISOR + b % DIVISOR; + lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; + trail %= DIVISOR; + if (trail < 0 && lead > 0) { + trail += DIVISOR; + --lead; + } else if (lead < 0 && trail > 0) { + trail -= DIVISOR; + ++lead; + } + if (convert_top) { + if (lead == 0 && trail < 0) + pt = _add("-0", pt, ptlim); + else pt = _conv(lead, "%02d", pt, ptlim, loc); + } + if (convert_yy) + pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, + ptlim, loc); + return (pt); +} diff --git a/lib/libc/stdtime/strptime.3 b/lib/libc/stdtime/strptime.3 new file mode 100644 index 000000000000..7df73d2d080a --- /dev/null +++ b/lib/libc/stdtime/strptime.3 @@ -0,0 +1,185 @@ +.\" +.\" Copyright (c) 1997 Joerg Wunsch +.\" +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" " +.Dd December 9, 2024 +.Dt STRPTIME 3 +.Os +.Sh NAME +.Nm strptime +.Nd parse date and time string +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In time.h +.Ft char * +.Fo strptime +.Fa "const char * restrict buf" +.Fa "const char * restrict format" +.Fa "struct tm * restrict timeptr" +.Fc +.In time.h +.In xlocale.h +.Ft char * +.Fn strptime_l "const char * restrict buf" "const char * restrict format" "struct tm * restrict timeptr" "locale_t loc" +.Sh DESCRIPTION +The +.Fn strptime +function parses the string in the buffer +.Fa buf +according to the string pointed to by +.Fa format , +and fills in the elements of the structure pointed to by +.Fa timeptr . +The resulting values will be relative to the local time zone. +Thus, it can be considered the reverse operation of +.Xr strftime 3 . +The +.Fn strptime_l +function does the same as +.Fn strptime , +but takes an explicit locale rather than using the current locale. +.Pp +The +.Fa format +string consists of zero or more conversion specifications and +ordinary characters. +All ordinary characters are matched exactly with the buffer, where +white space in the format string will match any amount of white space +in the buffer. +All conversion specifications are identical to those described in +.Xr strftime 3 . +.Pp +Two-digit year values, including formats +.Fa %y +and +.Fa \&%D , +are now interpreted as beginning at 1969 per POSIX requirements. +Years 69-00 are interpreted in the 20th century (1969-2000), years +01-68 in the 21st century (2001-2068). +The +.Fa \&%U +and +.Fa %W +format specifiers accept any value within the range 00 to 53. +.Pp +If the +.Fa format +string does not contain enough conversion specifications to completely +specify the resulting +.Vt struct tm , +the unspecified members of +.Va timeptr +are left untouched. +For example, if +.Fa format +is +.Dq Li "%H:%M:%S" , +only +.Va tm_hour , +.Va tm_sec +and +.Va tm_min +will be modified. +If time relative to today is desired, initialize the +.Fa timeptr +structure with today's date before passing it to +.Fn strptime . +.Sh RETURN VALUES +Upon successful completion, +.Fn strptime +returns the pointer to the first character in +.Fa buf +that has not been required to satisfy the specified conversions in +.Fa format . +It returns +.Dv NULL +if one of the conversions failed. +.Fn strptime_l +returns the same values as +.Fn strptime . +.Sh SEE ALSO +.Xr date 1 , +.Xr scanf 3 , +.Xr strftime 3 +.Sh HISTORY +The +.Fn strptime +function appeared in +.Fx 3.0 . +.Sh AUTHORS +The +.Fn strptime +function has been contributed by Powerdog Industries. +.Pp +This man page was written by +.An J\(:org Wunsch . +.Sh CAVEATS +The +.Fn strptime +function assumes the Gregorian calendar and will produce incorrect +results for dates prior to its introduction. +.Sh BUGS +Both the +.Fa %e +and +.Fa %l +format specifiers may incorrectly scan one too many digits +if the intended values comprise only a single digit +and that digit is followed immediately by another digit. +Both specifiers accept zero-padded values, +even though they are both defined as taking unpadded values. +.Pp +The +.Fa %p +format specifier has no effect unless it is parsed +.Em after +hour-related specifiers. +Specifying +.Fa %l +without +.Fa %p +will produce undefined results. +Note that 12AM +(ante meridiem) +is taken as midnight +and 12PM +(post meridiem) +is taken as noon. +.Pp +The +.Fa %Z +format specifier only accepts time zone abbreviations of the local time zone, +or the value "GMT". +This limitation is because of ambiguity due to of the over loading of time +zone abbreviations. +One such example is +.Fa EST +which is both Eastern Standard Time and Eastern Australia Summer Time. +.Pp +The +.Fn strptime +function does not correctly handle multibyte characters in the +.Fa format +argument. diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c new file mode 100644 index 000000000000..5f1293c7a267 --- /dev/null +++ b/lib/libc/stdtime/strptime.c @@ -0,0 +1,709 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2014 Gary Mills + * Copyright 2011, Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 1994 Powerdog Industries. All rights reserved. + * + * Copyright (c) 2011 The FreeBSD Foundation + * + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of Powerdog Industries. + */ + +#include "namespace.h" +#include <time.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include "private.h" +#include "un-namespace.h" +#include "libc_private.h" +#include "timelocal.h" +#include "tzfile.h" + +static char * _strptime(const char *, const char *, struct tm *, int *, locale_t); + +#define asizeof(a) (sizeof(a) / sizeof((a)[0])) + +#define FLAG_NONE (1 << 0) +#define FLAG_YEAR (1 << 1) +#define FLAG_MONTH (1 << 2) +#define FLAG_YDAY (1 << 3) +#define FLAG_MDAY (1 << 4) +#define FLAG_WDAY (1 << 5) + +/* + * Gauss's algorithm for the day of the week of the first day of any year + * in the Gregorian calendar. + */ +static int +first_wday_of(int year) +{ + return ((1 + + 5 * ((year - 1) % 4) + + 4 * ((year - 1) % 100) + + 6 * ((year - 1) % 400)) % 7); +} + +static char * +_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp, + locale_t locale) +{ + char c; + const char *ptr; + int day_offset = -1, wday_offset; + int week_offset; + int i, len; + int flags; + int Ealternative, Oalternative; + int century, year; + const struct lc_time_T *tptr = __get_current_time_locale(locale); + static int start_of_month[2][13] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} + }; + + flags = FLAG_NONE; + century = -1; + year = -1; + + ptr = fmt; + while (*ptr != 0) { + c = *ptr++; + + if (c != '%') { + if (isspace_l((unsigned char)c, locale)) + while (*buf != 0 && + isspace_l((unsigned char)*buf, locale)) + buf++; + else if (c != *buf++) + return (NULL); + continue; + } + + Ealternative = 0; + Oalternative = 0; +label: + c = *ptr++; + switch (c) { + case '%': + if (*buf++ != '%') + return (NULL); + break; + + case '+': + buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale); + if (buf == NULL) + return (NULL); + flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; + break; + + case 'C': + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + /* XXX This will break for 3-digit centuries. */ + len = 2; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++) { + i *= 10; + i += *buf - '0'; + len--; + } + + century = i; + flags |= FLAG_YEAR; + + break; + + case 'c': + buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale); + if (buf == NULL) + return (NULL); + flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; + break; + + case 'D': + buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale); + if (buf == NULL) + return (NULL); + flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; + break; + + case 'E': + if (Ealternative || Oalternative) + break; + Ealternative++; + goto label; + + case 'O': + if (Ealternative || Oalternative) + break; + Oalternative++; + goto label; + + case 'F': + buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale); + if (buf == NULL) + return (NULL); + flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; + break; + + case 'R': + buf = _strptime(buf, "%H:%M", tm, GMTp, locale); + if (buf == NULL) + return (NULL); + break; + + case 'r': + buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale); + if (buf == NULL) + return (NULL); + break; + + case 'T': + buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale); + if (buf == NULL) + return (NULL); + break; + + case 'X': + buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale); + if (buf == NULL) + return (NULL); + break; + + case 'x': + buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale); + if (buf == NULL) + return (NULL); + flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; + break; + + case 'j': + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + len = 3; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++){ + i *= 10; + i += *buf - '0'; + len--; + } + if (i < 1 || i > 366) + return (NULL); + + tm->tm_yday = i - 1; + flags |= FLAG_YDAY; + + break; + + case 'M': + case 'S': + if (*buf == 0 || + isspace_l((unsigned char)*buf, locale)) + break; + + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + len = 2; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++){ + i *= 10; + i += *buf - '0'; + len--; + } + + if (c == 'M') { + if (i > 59) + return (NULL); + tm->tm_min = i; + } else { + if (i > 60) + return (NULL); + tm->tm_sec = i; + } + + break; + + case 'H': + case 'I': + case 'k': + case 'l': + /* + * %k and %l specifiers are documented as being + * blank-padded. However, there is no harm in + * allowing zero-padding. + * + * XXX %k and %l specifiers may gobble one too many + * digits if used incorrectly. + */ + + len = 2; + if ((c == 'k' || c == 'l') && + isblank_l((unsigned char)*buf, locale)) { + buf++; + len = 1; + } + + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++) { + i *= 10; + i += *buf - '0'; + len--; + } + if (c == 'H' || c == 'k') { + if (i > 23) + return (NULL); + } else if (i == 0 || i > 12) + return (NULL); + + tm->tm_hour = i; + + break; + + case 'p': + /* + * XXX This is bogus if parsed before hour-related + * specifiers. + */ + if (tm->tm_hour > 12) + return (NULL); + + len = strlen(tptr->am); + if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { + if (tm->tm_hour == 12) + tm->tm_hour = 0; + buf += len; + break; + } + + len = strlen(tptr->pm); + if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { + if (tm->tm_hour != 12) + tm->tm_hour += 12; + buf += len; + break; + } + + return (NULL); + + case 'A': + case 'a': + for (i = 0; i < asizeof(tptr->weekday); i++) { + len = strlen(tptr->weekday[i]); + if (strncasecmp_l(buf, tptr->weekday[i], + len, locale) == 0) + break; + len = strlen(tptr->wday[i]); + if (strncasecmp_l(buf, tptr->wday[i], + len, locale) == 0) + break; + } + if (i == asizeof(tptr->weekday)) + return (NULL); + + buf += len; + tm->tm_wday = i; + flags |= FLAG_WDAY; + break; + + case 'U': + case 'W': + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + len = 2; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++) { + i *= 10; + i += *buf - '0'; + len--; + } + if (i > 53) + return (NULL); + + if (c == 'U') + day_offset = TM_SUNDAY; + else + day_offset = TM_MONDAY; + + + week_offset = i; + + break; + + case 'u': + case 'w': + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + i = *buf++ - '0'; + if (i < 0 || i > 7 || (c == 'u' && i < 1) || + (c == 'w' && i > 6)) + return (NULL); + + tm->tm_wday = i % 7; + flags |= FLAG_WDAY; + + break; + + case 'e': + /* + * With %e format, our strftime(3) adds a blank space + * before single digits. + */ + if (*buf != 0 && + isspace_l((unsigned char)*buf, locale)) + buf++; + /* FALLTHROUGH */ + case 'd': + /* + * The %e specifier was once explicitly documented as + * not being zero-padded but was later changed to + * equivalent to %d. There is no harm in allowing + * such padding. + * + * XXX The %e specifier may gobble one too many + * digits if used incorrectly. + */ + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + len = 2; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++) { + i *= 10; + i += *buf - '0'; + len--; + } + if (i == 0 || i > 31) + return (NULL); + + tm->tm_mday = i; + flags |= FLAG_MDAY; + + break; + + case 'B': + case 'b': + case 'h': + for (i = 0; i < asizeof(tptr->month); i++) { + if (Oalternative) { + if (c == 'B') { + len = strlen(tptr->alt_month[i]); + if (strncasecmp_l(buf, + tptr->alt_month[i], + len, locale) == 0) + break; + } + } else { + len = strlen(tptr->month[i]); + if (strncasecmp_l(buf, tptr->month[i], + len, locale) == 0) + break; + } + } + /* + * Try the abbreviated month name if the full name + * wasn't found and Oalternative was not requested. + */ + if (i == asizeof(tptr->month) && !Oalternative) { + for (i = 0; i < asizeof(tptr->month); i++) { + len = strlen(tptr->mon[i]); + if (strncasecmp_l(buf, tptr->mon[i], + len, locale) == 0) + break; + } + } + if (i == asizeof(tptr->month)) + return (NULL); + + tm->tm_mon = i; + buf += len; + flags |= FLAG_MONTH; + + break; + + case 'm': + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + len = 2; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++) { + i *= 10; + i += *buf - '0'; + len--; + } + if (i < 1 || i > 12) + return (NULL); + + tm->tm_mon = i - 1; + flags |= FLAG_MONTH; + + break; + + case 's': + { + char *cp; + int sverrno; + long n; + time_t t; + + sverrno = errno; + errno = 0; + n = strtol_l(buf, &cp, 10, locale); + if (errno == ERANGE || (long)(t = n) != n) { + errno = sverrno; + return (NULL); + } + errno = sverrno; + buf = cp; + if (gmtime_r(&t, tm) == NULL) + return (NULL); + *GMTp = 1; + flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | + FLAG_MDAY | FLAG_YEAR; + } + break; + + case 'Y': + case 'y': + if (*buf == 0 || + isspace_l((unsigned char)*buf, locale)) + break; + + if (!isdigit_l((unsigned char)*buf, locale)) + return (NULL); + + len = (c == 'Y') ? 4 : 2; + for (i = 0; len && *buf != 0 && + isdigit_l((unsigned char)*buf, locale); buf++) { + i *= 10; + i += *buf - '0'; + len--; + } + if (c == 'Y') + century = i / 100; + year = i % 100; + + flags |= FLAG_YEAR; + + break; + + case 'Z': + { + const char *cp; + char *zonestr; + + for (cp = buf; *cp && + isupper_l((unsigned char)*cp, locale); ++cp) { + /*empty*/} + if (cp - buf) { + zonestr = alloca(cp - buf + 1); + strncpy(zonestr, buf, cp - buf); + zonestr[cp - buf] = '\0'; + tzset(); + if (0 == strcmp(zonestr, "GMT") || + 0 == strcmp(zonestr, "UTC")) { + *GMTp = 1; + } else if (0 == strcmp(zonestr, tzname[0])) { + tm->tm_isdst = 0; + } else if (0 == strcmp(zonestr, tzname[1])) { + tm->tm_isdst = 1; + } else { + return (NULL); + } + buf += cp - buf; + } + } + break; + + case 'z': + { + int sign = 1; + + if (*buf != '+') { + if (*buf == '-') + sign = -1; + else + return (NULL); + } + + buf++; + i = 0; + for (len = 4; len > 0; len--) { + if (isdigit_l((unsigned char)*buf, locale)) { + i *= 10; + i += *buf - '0'; + buf++; + } else if (len == 2) { + i *= 100; + break; + } else + return (NULL); + } + + if (i > 1400 || (sign == -1 && i > 1200) || + (i % 100) >= 60) + return (NULL); + tm->tm_hour -= sign * (i / 100); + tm->tm_min -= sign * (i % 100); + *GMTp = 1; + } + break; + + case 'n': + case 't': + while (isspace_l((unsigned char)*buf, locale)) + buf++; + break; + + default: + return (NULL); + } + } + + if (century != -1 || year != -1) { + if (year == -1) + year = 0; + if (century == -1) { + if (year < 69) + year += 100; + } else + year += century * 100 - TM_YEAR_BASE; + tm->tm_year = year; + } + + if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { + if ((flags & (FLAG_MONTH | FLAG_MDAY)) == + (FLAG_MONTH | FLAG_MDAY)) { + tm->tm_yday = start_of_month[isleap(tm->tm_year + + TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); + flags |= FLAG_YDAY; + } else if (day_offset != -1) { + int tmpwday, tmpyday, fwo; + + fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE); + /* No incomplete week (week 0). */ + if (week_offset == 0 && fwo == day_offset) + return (NULL); + + /* Set the date to the first Sunday (or Monday) + * of the specified week of the year. + */ + tmpwday = (flags & FLAG_WDAY) ? tm->tm_wday : + day_offset; + tmpyday = (7 - fwo + day_offset) % 7 + + (week_offset - 1) * 7 + + (tmpwday - day_offset + 7) % 7; + /* Impossible yday for incomplete week (week 0). */ + if (tmpyday < 0) { + if (flags & FLAG_WDAY) + return (NULL); + tmpyday = 0; + } + tm->tm_yday = tmpyday; + flags |= FLAG_YDAY; + } + } + + if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { + if (!(flags & FLAG_MONTH)) { + i = 0; + while (tm->tm_yday >= + start_of_month[isleap(tm->tm_year + + TM_YEAR_BASE)][i]) + i++; + if (i > 12) { + i = 1; + tm->tm_yday -= + start_of_month[isleap(tm->tm_year + + TM_YEAR_BASE)][12]; + tm->tm_year++; + } + tm->tm_mon = i - 1; + flags |= FLAG_MONTH; + } + if (!(flags & FLAG_MDAY)) { + tm->tm_mday = tm->tm_yday - + start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] + [tm->tm_mon] + 1; + flags |= FLAG_MDAY; + } + if (!(flags & FLAG_WDAY)) { + wday_offset = first_wday_of(tm->tm_year + TM_YEAR_BASE); + tm->tm_wday = (wday_offset + tm->tm_yday) % 7; + flags |= FLAG_WDAY; + } + } + + return ((char *)buf); +} + +char * +strptime_l(const char * __restrict buf, const char * __restrict fmt, + struct tm * __restrict tm, locale_t loc) +{ + char *ret; + int gmt; + FIX_LOCALE(loc); + + gmt = 0; + ret = _strptime(buf, fmt, tm, &gmt, loc); + if (ret && gmt) { + time_t t = timegm(tm); + + localtime_r(&t, tm); + } + + return (ret); +} + +char * +strptime(const char * __restrict buf, const char * __restrict fmt, + struct tm * __restrict tm) +{ + return strptime_l(buf, fmt, tm, __get_locale()); +} diff --git a/lib/libc/stdtime/time32.c b/lib/libc/stdtime/time32.c new file mode 100644 index 000000000000..5fb7c95c22de --- /dev/null +++ b/lib/libc/stdtime/time32.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2001 FreeBSD Inc. + * All rights reserved. + * + * These routines are for converting time_t to fixed-bit representations + * for use in protocols or storage. When converting time to a larger + * representation of time_t these routines are expected to assume temporal + * locality and use the 50-year rule to properly set the msb bits. XXX + * + * Redistribution and use under the terms of the COPYRIGHT file at the + * base of the source tree. + */ + +#include <sys/types.h> +#include <timeconv.h> + +/* + * Convert a 32 bit representation of time_t into time_t. XXX needs to + * implement the 50-year rule to handle post-2038 conversions. + */ +time_t +_time32_to_time(__int32_t t32) +{ + return((time_t)t32); +} + +/* + * Convert time_t to a 32 bit representation. If time_t is 64 bits we can + * simply chop it down. The resulting 32 bit representation can be + * converted back to a temporally local 64 bit time_t using time32_to_time. + */ +__int32_t +_time_to_time32(time_t t) +{ + return((__int32_t)t); +} + +/* + * Convert a 64 bit representation of time_t into time_t. If time_t is + * represented as 32 bits we can simply chop it and not support times + * past 2038. + */ +time_t +_time64_to_time(__int64_t t64) +{ + return((time_t)t64); +} + +/* + * Convert time_t to a 64 bit representation. If time_t is represented + * as 32 bits we simply sign-extend and do not support times past 2038. + */ +__int64_t +_time_to_time64(time_t t) +{ + return((__int64_t)t); +} + +/* + * Convert to/from 'long'. Depending on the sizeof(long) this may or + * may not require using the 50-year rule. + */ +long +_time_to_long(time_t t) +{ + if (sizeof(long) == sizeof(__int64_t)) + return(_time_to_time64(t)); + return((long)t); +} + +time_t +_long_to_time(long tlong) +{ + if (sizeof(long) == sizeof(__int32_t)) + return(_time32_to_time(tlong)); + return((time_t)tlong); +} + +/* + * Convert to/from 'int'. Depending on the sizeof(int) this may or + * may not require using the 50-year rule. + */ +int +_time_to_int(time_t t) +{ + if (sizeof(int) == sizeof(__int64_t)) + return(_time_to_time64(t)); + return((int)t); +} + +time_t +_int_to_time(int tint) +{ + if (sizeof(int) == sizeof(__int32_t)) + return(_time32_to_time(tint)); + return((time_t)tint); +} diff --git a/lib/libc/stdtime/timelocal.c b/lib/libc/stdtime/timelocal.c new file mode 100644 index 000000000000..680270e0f9b1 --- /dev/null +++ b/lib/libc/stdtime/timelocal.c @@ -0,0 +1,153 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org> + * Copyright (c) 1997 FreeBSD Inc. + * All rights reserved. + * + * Copyright (c) 2011 The FreeBSD Foundation + * + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stddef.h> + +#include "ldpart.h" +#include "timelocal.h" + +struct xlocale_time { + struct xlocale_component header; + char *buffer; + struct lc_time_T locale; +}; + +struct xlocale_time __xlocale_global_time; + +#define LCTIME_SIZE (sizeof(struct lc_time_T) / sizeof(char *)) + +static const struct lc_time_T _C_time_locale = { + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }, { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }, { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" + }, { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }, + + /* X_fmt */ + "%H:%M:%S", + + /* + * x_fmt + * Since the C language standard calls for + * "date, using locale's date format," anything goes. + * Using just numbers (as here) makes Quakers happier; + * it's also compatible with SVR4. + */ + "%m/%d/%y", + + /* + * c_fmt + */ + "%a %b %e %H:%M:%S %Y", + + /* am */ + "AM", + + /* pm */ + "PM", + + /* date_fmt */ + "%a %b %e %H:%M:%S %Z %Y", + + /* alt_month + * Standalone months forms for %OB + */ + { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }, + + /* md_order + * Month / day order in dates + */ + "md", + + /* ampm_fmt + * To determine 12-hour clock format time (empty, if N/A) + */ + "%I:%M:%S %p" +}; + +static void destruct_time(void *v) +{ + struct xlocale_time *l = v; + if (l->buffer) + free(l->buffer); + free(l); +} + +#include <stdio.h> +struct lc_time_T * +__get_current_time_locale(locale_t loc) +{ + return (loc->using_time_locale + ? &((struct xlocale_time *)loc->components[XLC_TIME])->locale + : (struct lc_time_T *)&_C_time_locale); +} + +static int +time_load_locale(struct xlocale_time *l, int *using_locale, const char *name) +{ + struct lc_time_T *time_locale = &l->locale; + return (__part_load_locale(name, using_locale, + &l->buffer, "LC_TIME", + LCTIME_SIZE, LCTIME_SIZE, + (const char **)time_locale)); +} +int +__time_load_locale(const char *name) +{ + return time_load_locale(&__xlocale_global_time, + &__xlocale_global_locale.using_time_locale, name); +} +void* __time_load(const char* name, locale_t loc) +{ + struct xlocale_time *new = calloc(sizeof(struct xlocale_time), 1); + new->header.header.destructor = destruct_time; + if (time_load_locale(new, &loc->using_time_locale, name) == _LDP_ERROR) + { + xlocale_release(new); + return NULL; + } + return new; +} + diff --git a/lib/libc/stdtime/timelocal.h b/lib/libc/stdtime/timelocal.h new file mode 100644 index 000000000000..7fe62b41ebfd --- /dev/null +++ b/lib/libc/stdtime/timelocal.h @@ -0,0 +1,61 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 1997-2002 FreeBSD Project. + * All rights reserved. + * + * Copyright (c) 2011 The FreeBSD Foundation + * + * Portions of this software were developed by David Chisnall + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _TIMELOCAL_H_ +#define _TIMELOCAL_H_ +#include "xlocale_private.h" + +/* + * Private header file for the strftime and strptime localization + * stuff. + */ +struct lc_time_T { + const char *mon[12]; + const char *month[12]; + const char *wday[7]; + const char *weekday[7]; + const char *X_fmt; + const char *x_fmt; + const char *c_fmt; + const char *am; + const char *pm; + const char *date_fmt; + const char *alt_month[12]; + const char *md_order; + const char *ampm_fmt; +}; + +struct lc_time_T *__get_current_time_locale(locale_t); +int __time_load_locale(const char *); + +#endif /* !_TIMELOCAL_H_ */ diff --git a/lib/libc/stdtime/tzset.3 b/lib/libc/stdtime/tzset.3 new file mode 100644 index 000000000000..94ccbec9aba7 --- /dev/null +++ b/lib/libc/stdtime/tzset.3 @@ -0,0 +1,344 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Arthur Olson. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 25, 2023 +.Dt TZSET 3 +.Os +.Sh NAME +.Nm daylight , +.Nm timezone , +.Nm tzname , +.Nm tzset +.Nd initialize time conversion information +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In time.h +.Vt extern int daylight ; +.Vt extern long timezone ; +.Vt extern char *tzname[2] ; +.Ft void +.Fn tzset void +.Sh DESCRIPTION +The +.Fn tzset +function +initializes time conversion information used by the library routine +.Xr localtime 3 . +The environment variable +.Ev TZ +specifies how this is done. +.Pp +If +.Ev TZ +does not appear in the environment, the best available approximation to +local wall clock time, as specified by the +.Xr tzfile 5 Ns -format +file +.Pa /etc/localtime +is used. +.Pp +If +.Ev TZ +appears in the environment but its value is a null string, Coordinated +Universal Time +.Pq Tn UTC +is used (without leap second correction). +.Pp +If +.Ev TZ +appears in the environment and its value begins with a colon +.Pq Ql \&: , +the rest of its value is used as a pathname of a +.Xr tzfile 5 Ns -format +file from which to read the time conversion information. +If the first character of the pathname is a slash +.Pq Ql / +it is used as +an absolute pathname; otherwise, it is used as a pathname relative to +the system time conversion information directory. +.Pp +If its value does not begin with a colon, it is first used as the pathname +of a file (as described above) from which to read the time conversion +information. +If that file cannot be read, the value is then interpreted as a direct +specification (the format is described below) of the time conversion +information. +.Pp +If the +.Ev TZ +environment variable does not specify a +.Xr tzfile 5 Ns -format +file and cannot be interpreted as a direct specification, +.Tn UTC +is used. +.Pp +After the first call to +.Nm tzset , +the +.Vt timezone +variable is set to the difference, in seconds, between Coordinated Universal +Time (UTC) and the local time. +The +.Vt daylight +variable is set to 0 if the local timezone is observing standard time. +It is set to 1 if the local timezone ever observes an alternate time, such as summer time. +The first element of the +.Vt tzname +array is the timezone name of standard time, while the second element is the +name of the altnerative time zone. +.Sh SPECIFICATION FORMAT +When +.Ev TZ +is used directly as a specification of the time conversion information, +it must have the following syntax (spaces inserted for clarity): +.Bd -ragged -offset indent +.Em std offset +.Bo +.Em dst +.Bq Em offset +.Bq , Em rule +.Bc +.Ed +.Pp +Where: +.Bl -tag -width std_and_dst -offset indent +.It Em std No and Em dst +Three or more bytes that are the designation for the standard +.Pq Em std +or summer +.Pq Em dst +time zone. +Only +.Em std +is required; if +.Em dst +is missing, then summer time does not apply in this locale. +Upper and lowercase letters are explicitly allowed. +Any characters +except a leading colon +.Pq Ql \&: , +digits, comma +.Pq Ql \&, , +minus +.Pq Ql \- , +plus +.Pq Ql + , +and +.Tn ASCII +.Dv NUL +are allowed. +.It Em offset +Indicates the value one must add to the local time to arrive at +Coordinated Universal Time. +The +.Em offset +has the form: +.Bd -ragged -offset indent +.Sm off +.Em hh Bo +.Em : mm +.Bq Em : ss +.Bc +.Sm on +.Ed +.Pp +The minutes +.Pq Em mm +and seconds +.Pq Em ss +are optional. +The hour +.Pq Em hh +is required and may be a single digit. +The +.Em offset +following +.Em std +is required. +If no +.Em offset +follows +.Em dst , +summer time is assumed to be one hour ahead of standard time. +One or +more digits may be used; the value is always interpreted as a decimal +number. +The hour must be between zero and 24, and the minutes (and +seconds) \(em if present \(em between zero and 59. +If preceded by a +.Pq Ql \- +the time zone shall be east of the Prime Meridian; otherwise it shall be +west (which may be indicated by an optional preceding +.Pq Ql + ) . +.It Em rule +Indicates when to change to and back from summer time. +The +.Em rule +has the form: +.Bd -ragged -offset indent +.Em date/time,date/time +.Ed +.Pp +where the first +.Em date +describes when the change from standard to summer time occurs and the +second +.Em date +describes when the change back happens. +Each +.Em time +field describes when, in current local time, the change to the other +time is made. +.Pp +The format of +.Em date +is one of the following: +.Bl -tag -width "M.m.n.d" +.It Sy J Em n +The Julian day +.Em n +(1 \*(Le +.Em n +\*(Le 365). +Leap days are not counted; that is, in all years \(em including leap +years \(em February 28 is day 59 and March 1 is day 60. +It is +impossible to explicitly refer to the occasional February 29. +.It Em n +The zero-based Julian day +(0 \*(Le +.Em n +\*(Le 365 ) . +Leap days are counted, and it is possible to refer to February 29. +.It Sy M Em m.n.d +The +.Em d Ns 'th +day (0 \*(Le +.Em d +\*(Le 6) +of week +.Em n +of month +.Em m +of the year +(1 \*(Le +.Em n +\*(Le 5), +(1 \*(Le +.Em m +\*(Le 12), +where week 5 means +.Do +the last +.Em d +day in month +.Em m +.Dc +which may occur in either the fourth or the fifth week). +Week 1 is the +first week in which the +.Em d Ns 'th +day occurs. +Day zero is Sunday. +.Pp +The +.Em time +has the same format as +.Em offset +except that no leading sign +.Pq Ql \- +or +.Pq Ql + +is allowed. +The default, if +.Em time +is not given, is +.Sy 02:00:00 . +.El +.Pp +If no +.Em rule +is present in the +.Ev TZ +specification, the rules specified +by the +.Xr tzfile 5 Ns -format +file +.Em posixrules +in the system time conversion information directory are used, with the +standard and summer time offsets from +.Tn UTC +replaced by those specified by +the +.Em offset +values in +.Ev TZ . +.El +.Pp +For compatibility with System V Release 3.1, a semicolon +.Pq Ql \&; +may be used to separate the +.Em rule +from the rest of the specification. +.Sh FILES +.Bl -tag -width /usr/share/zoneinfo/posixrules -compact +.It Pa /etc/localtime +local time zone file +.It Pa /usr/share/zoneinfo +time zone directory +.It Pa /usr/share/zoneinfo/posixrules +rules for +.Tn POSIX Ns -style +.Tn TZ Ns 's +.It Pa /usr/share/zoneinfo/Etc/GMT +for +.Tn UTC +leap seconds +.El +.Pp +If the file +.Pa /usr/share/zoneinfo/UTC +does not exist, +.Tn UTC +leap seconds are loaded from +.Pa /usr/share/zoneinfo/posixrules . +.Sh SEE ALSO +.Xr date 1 , +.Xr gettimeofday 2 , +.Xr ctime 3 , +.Xr getenv 3 , +.Xr time 3 , +.Xr tzfile 5 +.Sh HISTORY +The +.Fn tzset +function first appeared in +.Bx 4.4 . |