3 strftime.c -- provide strftime functionality on the command line
4 Copyright (C) 2016,2017 Kyle J. McKay. All rights reserved.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 "strftime version 1.2.0\n" \
24 "Copyright (C) 2016,2017 Kyle J. McKay <mackyle at gmail dot com>\n" \
25 "License GPLv2+: GNU GPL version 2 or later.\n" \
26 "<http://gnu.org/licenses/gpl2.html>\n" \
27 "This is free software: you are free to change and redistribute it.\n" \
28 "There is NO WARRANTY, to the extent permitted by law.\n"
31 "Usage: strftime [option...] [\"strftime(3) format\" [<epochsecs> [<offset>]]]\n" \
32 " --help/-h show this help\n" \
33 " --version/-V show version/license info\n" \
34 " --locale/-l use default locale rather than \"C\" locale\n" \
35 " --adjust/-a n add n (which may be negative) to <epochsecs> before formatting\n" \
36 " -- terminate options, next arg is format even if it starts with -\n" \
37 " <epochsecs> if omitted or empty string ('') use current time\n" \
38 " <offset> must be [+|-]HH[MM[SS]] same meaning as strftime(3) '%z' value\n" \
39 "If \"strftime(3) format\" is omitted (or '') then \"%a, %e %b %Y %T %z\" is used.\n" \
40 "If <offset> is omitted default timezone (taking TZ into account) will be used.\n" \
41 "If <offset> is NOT omitted then a strftime(3) '%Z' value will format as either\n" \
42 "\"unknown\" or \"UTC\" (for a zero offset). Note that except for the time zone\n" \
43 "name, `date` and `strftime '%a %b %e %T %Z %Y' $(date +'%s %z')` should match.\n" \
44 "If --locale is NOT used the default format strips leading spaces from %e value.\n" \
45 "The %N format specifier is also supported for a 9-digit nanoseconds but the\n" \
46 "last three digits will always be zero in this implementation and only the first\n" \
47 "occurrence of %N will be replaced. Additionally, if the current time is used\n" \
48 "along with %N then a 1 ms sleep will occur before and after gettimeofday().\n"
57 #define DEFAULT_FORMAT "%a, %e %b %Y %T %z"
58 #define MAXLENGTH 4096
60 static char buff
[MAXLENGTH
];
61 static char tzval
[20]; /* TZ=unknown+HH:MM:SS\0 */
63 int main(int argc
, char *argv
[])
68 char *fmt
= DEFAULT_FORMAT
;
69 int defaultformat
= 1;
74 size_t fmtlen
, i
, firstN
;
77 while (arg
< argc
&& *argv
[arg
] == '-') {
78 if (!strcmp(argv
[arg
], "--help") || !strcmp(argv
[arg
], "-h")) {
82 if (!strcmp(argv
[arg
], "--version") || !strcmp(argv
[arg
], "-V")) {
83 printf("%s", VERSION
);
86 if (!strcmp(argv
[arg
], "--locale") || !strcmp(argv
[arg
], "-l")) {
91 if (!strcmp(argv
[arg
], "--adjust") || !strcmp(argv
[arg
], "-a")) {
94 if (++arg
>= argc
|| !*argv
[arg
]) {
95 fprintf(stderr
, "strftime: missing --adjust value "
96 "(see strftime -h)\n");
99 adjust
= strtol(argv
[arg
], &end
, 10);
101 fprintf(stderr
, "strftime: invalid number: %s\n",
108 if (!strcmp(argv
[arg
], "--")) {
112 fprintf(stderr
, "strftime: invalid option: %s (see strftime -h)\n",
116 if (argc
- arg
> 3) {
117 fprintf(stderr
, "strftime: invalid arguments (see strftime -h)\n");
120 if (argc
- arg
>= 1) {
125 if (argc
- arg
>= 2) {
128 long l
= strtol(argv
[arg
+1], &end
, 10);
131 fprintf(stderr
, "strftime: invalid number: %s\n",
135 tv
.tv_sec
= (time_t)l
;
138 if (argc
- arg
>= 3) {
139 const char *o
= argv
[arg
+2];
140 size_t l
= strlen(o
);
144 if (*o
== '+' || *o
== '-') {
145 tzsign
= (*o
== '+') ? '-' : '+';
149 if (l
< 2 || l
> 6 || (l
& 0x1) || l
!= strspn(o
, "0123456789")) {
150 fprintf(stderr
, "strftime: invalid offset: %s\n",
154 if (l
== strspn(o
, "0")) {
155 memcpy(d
, "TZ=UTC", 6);
158 memcpy(d
, "TZ=unknown", 10);
180 setlocale(LC_ALL
, "");
183 fmtlen
= strlen(fmt
);
185 for (i
=0; i
<fmtlen
; ++i
) {
198 struct timespec milli
;
201 milli
.tv_nsec
= 1000;
202 nanosleep(&milli
, NULL
);
204 gettimeofday(&tv
, NULL
);
206 nanosleep(&milli
, NULL
);
208 tv
.tv_sec
+= (time_t)adjust
;
209 localvals
= *localtime(&tv
.tv_sec
);
211 if (*fmt
&& !strftime(buff
, sizeof(buff
), fmt
, &localvals
)) {
212 fprintf(stderr
, "strftime: format string too long\n");
216 size_t left
= sizeof(buff
) - (fmtlen
= strlen(buff
));
218 fprintf(stderr
, "strftime: format string too long\n");
221 snprintf(buff
+ fmtlen
, left
, "%06u000", (unsigned)tv
.tv_usec
);
226 if (!strftime(buff
+ fmtlen
, left
, fmt
, &localvals
)) {
227 fprintf(stderr
, "strftime: format string too long\n");
232 if (defaultformat
&& !dosetlocale
&& buff
[3] == ',' && buff
[5] == ' ') {
233 memmove(buff
+1, buff
, 4);