Below is code as a starting point, the self-tests produces: 7 weeks 5 days 12 hours 44 minutes 31 seconds 7 weeks 5 days 12 hours 44 minutes 1 second 7 weeks 5 days 12 hours 1 minute 11 seconds 7 weeks 5 days 1 hour 44 minutes 31 seconds 7 weeks 1 day 12 hours 44 minutes 31 seconds 1 week 5 days 12 hours 44 minutes 31 seconds 1 week 1 day 1 hour 1 minute 1 second
Which seems OK. The self test should strcmp these strings instead of printing them, but right now printing is more useful. I know the code can be cleaned up, and comments added, but right now I'm mostly looking for comments on the general approach and on the function prototype: char * human_readable_time (time_t age, unsigned int units) I'd prefer if the module didn't use xvasprintf because I probably want to use this in a library, and x* doesn't work well there (and there are license problems too). If we don't worry about translations, the largest string for one unit would be '%d minutes', so we could put the output string in a buffer and require the caller to allocate sufficient large buffer. What do you think? Something like this: char * human_readable_time (time_t age, unsigned int units, char *buf, size_t bufsiz); Or possibly, to be able to find out the total length: size_t human_readable_time (time_t age, unsigned int units, char *buf, size_t bufsiz); this one would return the size of the string, regardless of whether that fitted in the buf+bufsiz buffer. The last one could be OK even if we allow translated strings. However, we could also do it like: char * human_readable_time (time_t age, unsigned int units); and allow the function to return NULL on memory allocation errors (and errno would be set). Maybe this is cleaner, I find that functions that use fixed-size buffers are difficult to use. And there are no performance issues to consider here, I think, so the cost of memory allocation should be irrelevant. Hm. I just realized the function doesn't do rounding properly. So if only one unit is requested, and the time is '7 weeks 6 days' the function would return '7 weeks' rather than the expected '8 weeks'. Also, there is the problem of how long a month and a year is, it isn't really well-defined. This is mostly relevant if you use full precision, otherwise the string is just an approximation anyway. Thoughts? /Simon
/* Test printing of human time distances. Copyright (C) 2008 Free Software Foundation, Inc. Written by Simon Josefsson. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <config.h> #include "human-time.h" #include <stdlib.h> #include <stdio.h> int main (int ac, char *av[]) { char *s; s = human_readable_time (4711471, 99); puts (s); free (s); s = human_readable_time (4711441, 99); puts (s); free (s); s = human_readable_time (4708871, 99); puts (s); free (s); s = human_readable_time (4671871, 99); puts (s); free (s); s = human_readable_time (4365871, 99); puts (s); free (s); s = human_readable_time (1082671, 99); puts (s); free (s); s = human_readable_time (694861, 99); puts (s); free (s); return 0; }
/* human-time.h -- human readable time distance conversion Copyright (C) 2008 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Simon Josefsson. */ #ifndef HUMAN_TIME_H_ # define HUMAN_TIME_H_ 1 #include <time.h> char *human_readable_time (time_t age, unsigned int units); #endif /* HUMAN_H_ */
/* human-time.h -- human readable time distance conversion Copyright (C) 2008 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Simon Josefsson. */ #include <config.h> #include "human-time.h" #include <stdlib.h> #include <xvasprintf.h> char * human_readable_time (time_t age, unsigned int units) { char *str; if (age == 1) return xasprintf ("1 second"); else if (age < 60) return xasprintf ("%d seconds", age); else if (age < 2 * 60) str = xasprintf ("1 minute"), age -= 60; else if (age < 60 * 60) str = xasprintf ("%d minutes", age / 60), age -= 60 * (age / 60); else if (age < 2 * 60 * 60) str = xasprintf ("1 hour"), age -= 60 * 60; else if (age < 24 * 60 * 60) str = xasprintf ("%d hours", age / 60 / 60), age -= 60 * 60 * (age / 60 / 60); else if (age < 2 * 24 * 60 * 60) str = xasprintf ("1 day"), age -= 24 * 60 * 60; else if (age < 7 * 24 * 60 * 60) str = xasprintf ("%d days", age / 24 / 60 / 60), age -= 24 * 60 * 60 * (age / 60 / 60 / 24); else if (age < 2 * 7 * 24 * 60 * 60) str = xasprintf ("1 week"), age -= 7 * 24 * 60 * 60; else str = xasprintf ("%d weeks", age / 7 / 24 / 60 / 60), age -= 7 * 24 * 60 * 60 * (age / 60 / 60 / 24 / 7); if (units > 0 && age > 0) { char *next; char *whole; next = human_readable_time (age, units - 1); whole = xasprintf ("%s %s", str, next); free (str); free (next); str = whole; } return str; }
Description: Convert a time distance to a human readable string. Files: lib/human-time.h lib/human-time.c m4/human-time.m4 Depends-on: xvasprintf configure.ac: gl_HUMAN_TIME Makefile.am: Include: "human_time.h" License: LGPLv2+ Maintainer: Simon Josefsson
Files: tests/test-human-time.c Depends-on: configure.ac: Makefile.am: TESTS += test-human-time check_PROGRAMS += test-human-time License: LGPLv2+
#serial 1 dnl Copyright (C) 2008 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_HUMAN_TIME], [ AC_LIBOBJ([human-time]) dnl Prerequisites of lib/human-time.c. : ])