3 strftime.c -- provide strftime functionality on the command line
4 Copyright (C) 2016 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.1.0\n" \
24 "Copyright (C) 2016 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"
53 #define DEFAULT_FORMAT "%a, %e %b %Y %T %z"
54 #define MAXLENGTH 4096
56 static char buff
[MAXLENGTH
];
57 static char tzval
[20]; /* TZ=unknown+HH:MM:SS\0 */
59 int main(int argc
, char *argv
[])
63 time_t t
= time(NULL
);
64 const char *fmt
= DEFAULT_FORMAT
;
65 int defaultformat
= 1;
70 while (arg
< argc
&& *argv
[arg
] == '-') {
71 if (!strcmp(argv
[arg
], "--help") || !strcmp(argv
[arg
], "-h")) {
75 if (!strcmp(argv
[arg
], "--version") || !strcmp(argv
[arg
], "-V")) {
76 printf("%s", VERSION
);
79 if (!strcmp(argv
[arg
], "--locale") || !strcmp(argv
[arg
], "-l")) {
84 if (!strcmp(argv
[arg
], "--adjust") || !strcmp(argv
[arg
], "-a")) {
87 if (++arg
>= argc
|| !*argv
[arg
]) {
88 fprintf(stderr
, "strftime: missing --adjust value "
89 "(see strftime -h)\n");
92 adjust
= strtol(argv
[arg
], &end
, 10);
94 fprintf(stderr
, "strftime: invalid number: %s\n",
101 if (!strcmp(argv
[arg
], "--")) {
105 fprintf(stderr
, "strftime: invalid option: %s (see strftime -h)\n",
109 if (argc
- arg
> 3) {
110 fprintf(stderr
, "strftime: invalid arguments (see strftime -h)\n");
113 if (argc
- arg
>= 1) {
118 if (argc
- arg
>= 2) {
121 long l
= strtol(argv
[arg
+1], &end
, 10);
124 fprintf(stderr
, "strftime: invalid number: %s\n",
130 if (argc
- arg
>= 3) {
131 const char *o
= argv
[arg
+2];
132 size_t l
= strlen(o
);
136 if (*o
== '+' || *o
== '-') {
137 tzsign
= (*o
== '+') ? '-' : '+';
141 if (l
< 2 || l
> 6 || (l
& 0x1) || l
!= strspn(o
, "0123456789")) {
142 fprintf(stderr
, "strftime: invalid offset: %s\n",
146 if (l
== strspn(o
, "0")) {
147 memcpy(d
, "TZ=UTC", 6);
150 memcpy(d
, "TZ=unknown", 10);
172 setlocale(LC_ALL
, "");
176 localvals
= *localtime(&t
);
177 if (!strftime(buff
, sizeof(buff
), fmt
, &localvals
)) {
178 fprintf(stderr
, "strftime: format string too long\n");
181 if (defaultformat
&& !dosetlocale
&& buff
[3] == ',' && buff
[5] == ' ') {
182 memmove(buff
+1, buff
, 4);