mirror of
https://github.com/curl/curl.git
synced 2026-06-16 04:45:39 +03:00
writeout: add %time{}
Output the current UTC time using strftime format. %f is an extra curl specific flag to output the microsecond fraction of the current second. Verified by test 1981 Closes #18119
This commit is contained in:
parent
5b80b4c012
commit
fadc487567
5 changed files with 340 additions and 3 deletions
|
|
@ -220,6 +220,10 @@ From this point on, the --write-out output is written to standard output.
|
||||||
This is the default, but can be used to switch back after switching to stderr.
|
This is the default, but can be used to switch back after switching to stderr.
|
||||||
(Added in 7.63.0)
|
(Added in 7.63.0)
|
||||||
|
|
||||||
|
## `time{format}`
|
||||||
|
Output the current UTC time using `strftime()` format. See TIME OUTPUT FORMAT
|
||||||
|
below for details. (Added in 8.16.0)
|
||||||
|
|
||||||
## `time_appconnect`
|
## `time_appconnect`
|
||||||
The time, in seconds, it took from the start until the SSL/SSH/etc
|
The time, in seconds, it took from the start until the SSL/SSH/etc
|
||||||
connect/handshake to the remote host was completed. (Added in 7.19.0)
|
connect/handshake to the remote host was completed. (Added in 7.19.0)
|
||||||
|
|
@ -347,3 +351,185 @@ The numerical identifier of the last transfer done. -1 if no transfer has been
|
||||||
started yet for the handle. The transfer id is unique among all transfers
|
started yet for the handle. The transfer id is unique among all transfers
|
||||||
performed using the same connection cache.
|
performed using the same connection cache.
|
||||||
(Added in 8.2.0)
|
(Added in 8.2.0)
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
TIME OUTPUT FORMAT
|
||||||
|
|
||||||
|
When showing time with `%time{}`, the following output qualifiers are
|
||||||
|
available:
|
||||||
|
|
||||||
|
## `%a`
|
||||||
|
|
||||||
|
The abbreviated name of the day of the week according to the current locale.
|
||||||
|
|
||||||
|
## `%A`
|
||||||
|
|
||||||
|
The full name of the day of the week according to the current locale.
|
||||||
|
|
||||||
|
## `%b`
|
||||||
|
|
||||||
|
The abbreviated month name according to the current locale.
|
||||||
|
|
||||||
|
## `%B`
|
||||||
|
|
||||||
|
The full month name according to the current locale.
|
||||||
|
|
||||||
|
## `%c`
|
||||||
|
|
||||||
|
The preferred date and time representation for the current locale. (In the
|
||||||
|
POSIX locale this is equivalent to %a %b %e %H:%M:%S %Y.)
|
||||||
|
|
||||||
|
## `%C`
|
||||||
|
|
||||||
|
The century number (year/100) as a 2-digit integer.
|
||||||
|
|
||||||
|
## `%d`
|
||||||
|
|
||||||
|
The day of the month as a decimal number (range 01 to 31).
|
||||||
|
|
||||||
|
## `%D`
|
||||||
|
|
||||||
|
Equivalent to %m/%d/%y. In international contexts, this format is ambiguous
|
||||||
|
and should be avoided.)
|
||||||
|
|
||||||
|
## `%e`
|
||||||
|
|
||||||
|
Like %d, the day of the month as a decimal number, but a leading zero is
|
||||||
|
replaced by a space.
|
||||||
|
|
||||||
|
## `%f`
|
||||||
|
|
||||||
|
The number of microseconds elapsed of the current second. (This a curl special
|
||||||
|
code and not a standard one.)
|
||||||
|
|
||||||
|
## `%F`
|
||||||
|
|
||||||
|
Equivalent to %Y-%m-%d (the ISO 8601 date format).
|
||||||
|
|
||||||
|
## `%G`
|
||||||
|
|
||||||
|
The ISO 8601 week-based year with century as a decimal number. The 4-digit
|
||||||
|
year corresponding to the ISO week number (see %V). This has the same format
|
||||||
|
and value as %Y, except that if the ISO week number belongs to the previous or
|
||||||
|
next year, that year is used instead.
|
||||||
|
|
||||||
|
## `%g`
|
||||||
|
|
||||||
|
Like `%G`, but without century, that is, with a 2-digit year (00-99).
|
||||||
|
|
||||||
|
## `%h`
|
||||||
|
|
||||||
|
Equivalent to `%b`.
|
||||||
|
|
||||||
|
## `%H`
|
||||||
|
|
||||||
|
The hour as a decimal number using a 24-hour clock (range 00 to 23).
|
||||||
|
|
||||||
|
## `%I`
|
||||||
|
|
||||||
|
The hour as a decimal number using a 12-hour clock (range 01 to 12).
|
||||||
|
|
||||||
|
## `%j`
|
||||||
|
|
||||||
|
The day of the year as a decimal number (range 001 to 366).
|
||||||
|
|
||||||
|
## `%k`
|
||||||
|
|
||||||
|
The hour (24-hour clock) as a decimal number (range 0 to 23); single digits
|
||||||
|
are preceded by a blank.
|
||||||
|
|
||||||
|
## `%l`
|
||||||
|
|
||||||
|
The hour (12-hour clock) as a decimal number (range 1 to 12); single digits
|
||||||
|
are preceded by a blank.
|
||||||
|
|
||||||
|
## `%m`
|
||||||
|
|
||||||
|
The month as a decimal number (range 01 to 12).
|
||||||
|
|
||||||
|
## `%M`
|
||||||
|
|
||||||
|
The minute as a decimal number (range 00 to 59).
|
||||||
|
|
||||||
|
## `%p`
|
||||||
|
|
||||||
|
Either "AM" or "PM" according to the given time value, or the corresponding
|
||||||
|
strings for the current locale. Noon is treated as "PM" and midnight as "AM".
|
||||||
|
|
||||||
|
## `%P`
|
||||||
|
|
||||||
|
Like %p but in lowercase: "am" or "pm" or a corresponding string for the
|
||||||
|
current locale.
|
||||||
|
|
||||||
|
## `%r`
|
||||||
|
|
||||||
|
The time in am or pm notation.
|
||||||
|
|
||||||
|
## `%R`
|
||||||
|
|
||||||
|
The time in 24-hour notation (%H:%M). For a version including the seconds, see
|
||||||
|
`%T` below.
|
||||||
|
|
||||||
|
## `%s`
|
||||||
|
|
||||||
|
The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
|
||||||
|
|
||||||
|
## `%S`
|
||||||
|
|
||||||
|
The second as a decimal number (range 00 to 60). (The range is up to 60 to
|
||||||
|
allow for occasional leap seconds.)
|
||||||
|
|
||||||
|
## `%T`
|
||||||
|
|
||||||
|
The time in 24-hour notation (%H:%M:%S).
|
||||||
|
|
||||||
|
## `%u`
|
||||||
|
|
||||||
|
The day of the week as a decimal, range 1 to 7, Monday being 1.
|
||||||
|
|
||||||
|
## `%U`
|
||||||
|
|
||||||
|
The week number of the current year as a decimal number, range 00 to 53,
|
||||||
|
starting with the first Sunday as the first day of week 01. See also `%V` and
|
||||||
|
`%W`.
|
||||||
|
|
||||||
|
## `%V`
|
||||||
|
|
||||||
|
The ISO 8601 week number (see NOTES) of the current year as a decimal number,
|
||||||
|
range 01 to 53, where week 1 is the first week that has at least 4 days in the
|
||||||
|
new year. See also `%U` and `%W`.
|
||||||
|
|
||||||
|
## `%w`
|
||||||
|
|
||||||
|
The day of the week as a decimal, range 0 to 6, Sunday being 0. See also `%u`.
|
||||||
|
|
||||||
|
## `%W`
|
||||||
|
|
||||||
|
The week number of the current year as a decimal number, range 00 to 53,
|
||||||
|
starting with the first Monday as the first day of week 01.
|
||||||
|
|
||||||
|
## `%x`
|
||||||
|
|
||||||
|
The preferred date representation for the current locale without the time.
|
||||||
|
|
||||||
|
## `%X`
|
||||||
|
|
||||||
|
The preferred time representation for the current locale without the date.
|
||||||
|
|
||||||
|
## `%y`
|
||||||
|
|
||||||
|
The year as a decimal number without a century (range 00 to 99).
|
||||||
|
|
||||||
|
## `%Y`
|
||||||
|
|
||||||
|
The year as a decimal number including the century.
|
||||||
|
|
||||||
|
## `%z`
|
||||||
|
|
||||||
|
The `+hhmm` or `-hhmm` numeric timezone (that is, the hour and minute offset
|
||||||
|
from UTC). As time is always UTC, this outputs `+0000`.
|
||||||
|
|
||||||
|
## `%Z`
|
||||||
|
|
||||||
|
The timezone name. For some reason `GMT`.
|
||||||
|
|
|
||||||
|
|
@ -539,6 +539,81 @@ matchvar(const void *m1, const void *m2)
|
||||||
|
|
||||||
#define MAX_WRITEOUT_NAME_LENGTH 24
|
#define MAX_WRITEOUT_NAME_LENGTH 24
|
||||||
|
|
||||||
|
/* return the position after %time{} */
|
||||||
|
static const char *outtime(const char *ptr, /* %time{ ... */
|
||||||
|
FILE *stream)
|
||||||
|
{
|
||||||
|
const char *end;
|
||||||
|
ptr += 6;
|
||||||
|
end = strchr(ptr, '}');
|
||||||
|
if(end) {
|
||||||
|
struct tm *utc;
|
||||||
|
struct dynbuf format;
|
||||||
|
char output[256]; /* max output time length */
|
||||||
|
#ifdef HAVE_GETTIMEOFDAY
|
||||||
|
struct timeval cnow;
|
||||||
|
#else
|
||||||
|
struct curltime cnow;
|
||||||
|
#endif
|
||||||
|
time_t secs;
|
||||||
|
unsigned int usecs;
|
||||||
|
size_t i;
|
||||||
|
size_t vlen;
|
||||||
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
|
#ifdef HAVE_GETTIMEOFDAY
|
||||||
|
gettimeofday(&cnow, NULL);
|
||||||
|
#else
|
||||||
|
cnow.tv_sec = time(NULL);
|
||||||
|
cnow.tv_usec = 0;
|
||||||
|
#endif
|
||||||
|
secs = cnow.tv_sec;
|
||||||
|
usecs = (unsigned int)cnow.tv_usec;
|
||||||
|
#ifdef DEBUGBUILD
|
||||||
|
{
|
||||||
|
const char *timestr = getenv("CURL_TIME");
|
||||||
|
if(timestr) {
|
||||||
|
curl_off_t val;
|
||||||
|
curlx_str_number(×tr, &val, TIME_T_MAX);
|
||||||
|
secs = (time_t)val;
|
||||||
|
usecs = (unsigned int)(val % 1000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
vlen = end - ptr;
|
||||||
|
curlx_dyn_init(&format, 1024);
|
||||||
|
|
||||||
|
/* insert sub-seconds for %f */
|
||||||
|
/* insert +0000 for %z because it is otherwise not portable */
|
||||||
|
/* insert UTC for %Z because it is otherwise not portable */
|
||||||
|
for(i = 0; !result && i < vlen; i++) {
|
||||||
|
if((i < vlen - 1) && ptr[i] == '%' &&
|
||||||
|
((ptr[i + 1] == 'f') || ((ptr[i + 1] | 0x20) == 'z'))) {
|
||||||
|
if(ptr[i + 1] == 'f')
|
||||||
|
result = curlx_dyn_addf(&format, "%06u", usecs);
|
||||||
|
else if(ptr[i + 1] == 'Z')
|
||||||
|
result = curlx_dyn_addn(&format, "UTC", 3);
|
||||||
|
else
|
||||||
|
result = curlx_dyn_addn(&format, "+0000", 5);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = curlx_dyn_addn(&format, &ptr[i], 1);
|
||||||
|
}
|
||||||
|
if(!result) {
|
||||||
|
/* !checksrc! disable BANNEDFUNC 1 */
|
||||||
|
utc = gmtime(&secs);
|
||||||
|
strftime(output, sizeof(output), curlx_dyn_ptr(&format), utc);
|
||||||
|
fputs(output, stream);
|
||||||
|
curlx_dyn_free(&format);
|
||||||
|
}
|
||||||
|
ptr = end + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs("%time{", stream);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
|
void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
|
||||||
CURLcode per_result)
|
CURLcode per_result)
|
||||||
{
|
{
|
||||||
|
|
@ -640,6 +715,9 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per,
|
||||||
else
|
else
|
||||||
fputs("%header{", stream);
|
fputs("%header{", stream);
|
||||||
}
|
}
|
||||||
|
else if(!strncmp("time{", &ptr[1], 5)) {
|
||||||
|
ptr = outtime(ptr, stream);
|
||||||
|
}
|
||||||
else if(!strncmp("output{", &ptr[1], 7)) {
|
else if(!strncmp("output{", &ptr[1], 7)) {
|
||||||
bool append = FALSE;
|
bool append = FALSE;
|
||||||
ptr += 8;
|
ptr += 8;
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \
|
||||||
test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
|
test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
|
||||||
test1955 test1956 test1957 test1958 test1959 test1960 test1964 \
|
test1955 test1956 test1957 test1958 test1959 test1960 test1964 \
|
||||||
test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \
|
test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \
|
||||||
test1978 test1979 test1980 \
|
test1978 test1979 test1980 test1981 \
|
||||||
\
|
\
|
||||||
test2000 test2001 test2002 test2003 test2004 test2005 \
|
test2000 test2001 test2002 test2003 test2004 test2005 \
|
||||||
\
|
\
|
||||||
|
|
|
||||||
62
tests/data/test1981
Normal file
62
tests/data/test1981
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<testcase>
|
||||||
|
<info>
|
||||||
|
<keywords>
|
||||||
|
HTTP
|
||||||
|
HTTP GET
|
||||||
|
</keywords>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Server-side
|
||||||
|
<reply>
|
||||||
|
<data crlf="yes" nocheck="yes">
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||||
|
Server: test-server/fake
|
||||||
|
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||||
|
ETag: "21025-dc7-39462498"
|
||||||
|
Accept-Ranges: bytes
|
||||||
|
Content-Length: 6
|
||||||
|
Connection: close
|
||||||
|
Content-Type: text/html
|
||||||
|
Funny-head: yesyes
|
||||||
|
|
||||||
|
-foo-
|
||||||
|
</data>
|
||||||
|
</reply>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Client-side
|
||||||
|
<client>
|
||||||
|
<server>
|
||||||
|
http
|
||||||
|
</server>
|
||||||
|
<name>
|
||||||
|
%time output with --write-out
|
||||||
|
</name>
|
||||||
|
<features>
|
||||||
|
Debug
|
||||||
|
</features>
|
||||||
|
<setenv>
|
||||||
|
CURL_TIME=1754037103
|
||||||
|
</setenv>
|
||||||
|
<command>
|
||||||
|
http://%HOSTIP:%HTTPPORT/%TESTNUMBER --write-out='Time: %time{%d/%b/%Y %H:%M:%S.%f %z %Z}\n' -s -o %LOGDIR/dump
|
||||||
|
</command>
|
||||||
|
</client>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Verify data after the test has been "shot"
|
||||||
|
<verify>
|
||||||
|
<protocol crlf="yes">
|
||||||
|
GET /%TESTNUMBER HTTP/1.1
|
||||||
|
Host: %HOSTIP:%HTTPPORT
|
||||||
|
User-Agent: curl/%VERSION
|
||||||
|
Accept: */*
|
||||||
|
|
||||||
|
</protocol>
|
||||||
|
<stdout mode="text">
|
||||||
|
Time: 01/Aug/2025 08:31:43.037103 +0000 UTC
|
||||||
|
</stdout>
|
||||||
|
</verify>
|
||||||
|
</testcase>
|
||||||
|
|
@ -54,11 +54,22 @@ sub getsrcvars {
|
||||||
close($f);
|
close($f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my %special = (
|
||||||
|
'header{name}' => 1,
|
||||||
|
'output{filename}' => 1,
|
||||||
|
'time{format}' => 1,
|
||||||
|
);
|
||||||
|
|
||||||
sub getdocsvars {
|
sub getdocsvars {
|
||||||
open(my $f, "<", "$root/../docs/cmdline-opts/write-out.md");
|
open(my $f, "<", "$root/../docs/cmdline-opts/write-out.md");
|
||||||
while(<$f>) {
|
while(<$f>) {
|
||||||
if($_ =~ /^\#\# \`([^\`]*)\`/) {
|
chomp;
|
||||||
if($1 ne "header{name}" && $1 ne "output{filename}") {
|
$_ =~ s/[\r\n]//g;
|
||||||
|
if($_ =~ /^\#\# *\z/) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
elsif($_ =~ /^\#\# \`([^\`]*)\`/) {
|
||||||
|
if(!$special{$1}) {
|
||||||
$indocs{$1} = 1;
|
$indocs{$1} = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue