include/timeutils: rewrite iso formatting functions

- use buffers rather than allocate memory
- support .usec and ,usec convention
- use strftime for timezone (we need to care about daylight
  saving time)

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2016-05-24 10:58:52 +02:00
parent 01b47d46d7
commit 33c7ffa303
2 changed files with 78 additions and 44 deletions

View File

@ -58,12 +58,16 @@ int parse_timestamp(const char *t, usec_t *usec);
enum { enum {
ISO_8601_DATE = (1 << 1), ISO_8601_DATE = (1 << 1),
ISO_8601_TIME = (1 << 2), ISO_8601_TIME = (1 << 2),
ISO_8601_USEC = (1 << 3), ISO_8601_DOTUSEC = (1 << 3),
ISO_8601_TIMEZONE = (1 << 4), ISO_8601_COMMAUSEC = (1 << 4),
ISO_8601_SPACE = (1 << 5) ISO_8601_TIMEZONE = (1 << 5),
ISO_8601_SPACE = (1 << 6)
}; };
char *strtimeval_iso(struct timeval *tv, int flags);
char *strtm_iso(struct tm *tm, int flags); #define ISO_8601_BUFSIZ 32
char *strtime_iso(const time_t *t, int flags);
int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz);
int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz);
int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz);
#endif /* UTIL_LINUX_TIME_UTIL_H */ #endif /* UTIL_LINUX_TIME_UTIL_H */

View File

@ -22,6 +22,7 @@
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <sys/time.h>
#include "c.h" #include "c.h"
#include "nls.h" #include "nls.h"
@ -339,47 +340,77 @@ int parse_timestamp(const char *t, usec_t *usec)
return 0; return 0;
} }
static char *format_iso_time(struct tm *tm, suseconds_t usec, int flags) static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz)
{ {
char *s = NULL; char *p = buf;
int len;
if (flags & ISO_8601_DATE) if (flags & ISO_8601_DATE) {
s = strfappend(s, "%4d-%.2d-%.2d", tm->tm_year + 1900, len = snprintf(p, bufsz, "%4d-%.2d-%.2d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday); tm->tm_mon + 1, tm->tm_mday);
if ((flags & ISO_8601_DATE) && (flags & ISO_8601_TIME)) if (len < 0 || (size_t) len > bufsz)
s = strnappend(s, (flags & ISO_8601_SPACE) ? " " : "T", 1); return -1;
bufsz -= len;
if (flags & ISO_8601_TIME) p += len;
s = strfappend(s, "%02d:%02d:%02d", tm->tm_hour,
tm->tm_min, tm->tm_sec);
if (flags & ISO_8601_USEC)
s = strfappend(s, ".%06ld", (long) usec);
if (flags & ISO_8601_TIMEZONE) {
int zhour = - timezone / 60 / 60;
int zmin = labs(timezone / 60 % 60);
s = strfappend(s, "%+02d:%02d", zhour, zmin);
} }
return s; if ((flags & ISO_8601_DATE) && (flags & ISO_8601_TIME)) {
if (bufsz < 1)
return -1;
*p++ = (flags & ISO_8601_SPACE) ? ' ' : 'T';
bufsz--;
}
if (flags & ISO_8601_TIME) {
len = snprintf(p, bufsz, "%02d:%02d:%02d", tm->tm_hour,
tm->tm_min, tm->tm_sec);
if (len < 0 || (size_t) len > bufsz)
return -1;
bufsz -= len;
p += len;
}
if (flags & ISO_8601_DOTUSEC) {
len = snprintf(p, bufsz, ".%06ld", (long) usec);
if (len < 0 || (size_t) len > bufsz)
return -1;
bufsz -= len;
p += len;
} else if (flags & ISO_8601_COMMAUSEC) {
len = snprintf(p, bufsz, ",%06ld", (long) usec);
if (len < 0 || (size_t) len > bufsz)
return -1;
bufsz -= len;
p += len;
}
if (flags & ISO_8601_TIMEZONE) {
if (strftime(p, bufsz, "%z", tm) <= 0)
return -1;
}
return 0;
} }
char *strtimeval_iso(struct timeval *tv, int flags) /* timeval to ISO 8601 */
int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz)
{ {
struct tm tm = *localtime(&tv->tv_sec); struct tm tm = *localtime(&tv->tv_sec);
return format_iso_time(&tm, tv->tv_usec, flags); return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz);
} }
char *strtm_iso(struct tm *tm, int flags) /* struct tm to ISO 8601 */
int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz)
{ {
return format_iso_time(tm, 0, flags); return format_iso_time(tm, 0, flags, buf, bufsz);
} }
char *strtime_iso(const time_t *t, int flags) /* time_t to ISO 8601 */
int strtime_iso(const time_t *t, int flags, char *buf, size_t bufsz)
{ {
struct tm tm = *localtime(t); struct tm tm = *localtime(t);
return format_iso_time(&tm, 0, flags); return format_iso_time(&tm, 0, flags, buf, bufsz);
} }
@ -388,7 +419,7 @@ char *strtime_iso(const time_t *t, int flags)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct timeval tv = { 0 }; struct timeval tv = { 0 };
char *p; char buf[ISO_8601_BUFSIZ];
if (argc < 2) { if (argc < 2) {
fprintf(stderr, "usage: %s <time> [<usec>]\n", argv[0]); fprintf(stderr, "usage: %s <time> [<usec>]\n", argv[0]);
@ -399,21 +430,20 @@ int main(int argc, char *argv[])
if (argc == 3) if (argc == 3)
tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>"); tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>");
p = strtimeval_iso(&tv, ISO_8601_DATE); strtimeval_iso(&tv, ISO_8601_DATE, buf, sizeof(buf));
printf("Date: '%s'\n", p); printf("Date: '%s'\n", buf);
free(p);
p = strtimeval_iso(&tv, ISO_8601_TIME); strtimeval_iso(&tv, ISO_8601_TIME, buf, sizeof(buf));
printf("Time: '%s'\n", p); printf("Time: '%s'\n", buf);
free(p);
p = strtimeval_iso(&tv, ISO_8601_DATE | ISO_8601_TIME | ISO_8601_USEC); strtimeval_iso(&tv, ISO_8601_DATE | ISO_8601_TIME | ISO_8601_COMMAUSEC,
printf("Full: '%s'\n", p); buf, sizeof(buf));
free(p); printf("Full: '%s'\n", buf);
p = strtimeval_iso(&tv, ISO_8601_DATE | ISO_8601_TIME | ISO_8601_USEC | ISO_8601_TIMEZONE | ISO_8601_SPACE); strtimeval_iso(&tv, ISO_8601_DATE | ISO_8601_TIME | ISO_8601_DOTUSEC |
printf("Zone: '%s'\n", p); ISO_8601_TIMEZONE | ISO_8601_SPACE,
free(p); buf, sizeof(buf));
printf("Zone: '%s'\n", buf);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }