edh@ux.acs.umn.edu (Merlinus Ambrosius) (08/11/90)
In the standard library in K&R (ANSI) on page 256 there is a very nice looking procedure for formatting the current time into whatever form you like. It doesn't seem to be present on this system, or on a local Sparc. Anybody know where I can get this? What it does (according to the book) is: size_t strftime(char *s, size_t smax, const char *fmt, const struct tm *tp) It formats date and time info from *tp into s according the fmt, using a printf-like format. i.e. "%y%m%d %H%M%S" would put 900810 045835 into s. This is similar to how "date" does it too, of course, but if something like strftime already exists, I could sure use it! All help is appreciated, Eric Hendrickson -- /---------"Oh carrots are divine, you get a dozen for dime, its maaaagic."--- |Eric (the "Mentat-Philosopher") Hendrickson Academic Computing Services |edh@ux.acs.umn.edu The game is afoot! University of Minnesota \----"What does 'masochist' and 'amnesia' mean? Beats me, I don't know."---
henry@zoo.toronto.edu (Henry Spencer) (08/12/90)
In article <2048@ux.acs.umn.edu> edh@ux.acs.umn.edu (Eric D. Hendrickson) writes: >In the standard library in K&R (ANSI) on page 256 there is a very nice >looking procedure for formatting the current time into whatever form you >like. It doesn't seem to be present on this system, or on a local Sparc. Many systems are not yet ANSI-compliant. I don't know of any redistributable strftime() implementation. -- It is not possible to both understand | Henry Spencer at U of Toronto Zoology and appreciate Intel CPUs. -D.Wolfskill| henry@zoo.toronto.edu utzoo!henry
alf@xenon.stgt.sub.org (Ingo Feulner) (08/12/90)
In article <1990Aug11.230626.2706@zoo.toronto.edu>, henry@zoo.toronto.edu (Henry Spencer) writes: > In article <2048@ux.acs.umn.edu> edh@ux.acs.umn.edu (Eric D. Hendrickson) writes: > >In the standard library in K&R (ANSI) on page 256 there is a very nice > >looking procedure for formatting the current time into whatever form you > >like. It doesn't seem to be present on this system, or on a local Sparc. > > Many systems are not yet ANSI-compliant. Hope that will change in the near future... > > I don't know of any redistributable strftime() implementation. Here is one. It comes with a public domain C compiler named Sozobon C (for the Amiga), so I think I can post this. -Enjoy, Ingo. -------------------------snip----------------------snap-------------- /* * STRFTIME.C */ #include <time.h> #include <string.h> #include <stddef.h> static char *AbMonth[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul", "Aug","Sep","Oct","Nov","Dec" }; static char *FuMonth[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static char *AbDow[12] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *FuDow[12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; size_t strftime(buf, max, fmt, tm) char *buf; size_t max; const char *fmt; const struct tm *tm; { short i = 0; buf[i] = 0; while (*fmt) { char *ptr; i += strlen(buf + i); if (*fmt != '%') { buf[i++] = *fmt++; continue; } ptr = buf + i; fmt += 2; switch(fmt[-1]) { case '%': strcpy(ptr, "%"); break; case 'a': /* abbreviated name for dow */ strcpy(ptr, AbDow[tm->tm_wday]); break; case 'A': /* full name for dow */ strcpy(ptr, FuDow[tm->tm_wday]); break; case 'b': /* abbreviated name for month */ strcpy(ptr, AbMonth[tm->tm_mon]); break; case 'B': /* full name for month */ strcpy(ptr, FuMonth[tm->tm_mon]); break; case 'c': /* default rep for date & time */ sprintf(ptr, "%s %s %02d %02d:%02d:%02d %d", AbDow[tm->tm_wday], AbMonth[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year + 1900 ); break; case 'd': /* day of month as integer 01-31 */ sprintf(ptr, "%02d", tm->tm_mday); break; case 'H': /* hour as integer 00-23 */ sprintf(ptr, "%02d", tm->tm_hour); break; case 'I': /* hour as integer 01-12 */ sprintf(ptr, "%02d", (tm->tm_hour % 12) + 1); break; case 'j': /* day of year as int 001-366 */ sprintf(ptr, "%03d", tm->tm_yday); break; case 'm': /* month as integer 01-12 */ sprintf(ptr, "%02d", tm->tm_mon); break; case 'M': /* minute as integer 00-59 */ sprintf(ptr, "%02d", tm->tm_min); break; case 'p': /* 'AM' or 'PM' */ if (tm->tm_hour >= 12) { strcpy(ptr, "PM"); } else { strcpy(ptr, "AM"); } break; case 'S': /* the second as an int 00-59 */ sprintf(ptr, "%02d", tm->tm_sec); break; case 'U': /* week of year as int 00-53, regard sunday as first day in week */ { int bdiw = tm->tm_yday - tm->tm_wday; /* beginning of week */ if (bdiw < 0) bdiw = 0; else bdiw = bdiw / 7; sprintf(ptr, "%02d", bdiw); } break; case 'w': /* day of week as int 0-6, sunday == 0 */ sprintf(ptr, "%d", tm->tm_wday); break; case 'W': /* day of week as int 0-6, monday == 0 */ { int dowmon = tm->tm_wday - 1; if (dowmon < 0) dowmon += 7; sprintf(ptr, "%d", dowmon); } break; case 'x': /* the locale's default rep for date */ sprintf(ptr, "%s %s %02d", AbDow[tm->tm_wday], AbMonth[tm->tm_mon], tm->tm_mday ); break; case 'X': /* the locale's default rep for time */ sprintf(ptr, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec ); break; case 'y': /* year within the century 00-99 */ sprintf(ptr, "%02d", tm->tm_year % 100); break; case 'Y': /* the full year, including century */ sprintf(ptr, "%04d", tm->tm_year + 1900); break; case 'Z': /* the name of the time zone or "" */ strcpy(ptr, ""); break; default: strcpy(ptr, "%?"); break; } } i += strlen(buf + i); return((size_t)i); } -------------------------------snip--------------snap---------------- -- Ingo Feulner - alf@xenon.stgt.sub.org Wolfacher Weg 22 - 7030 Boeblingen - (+49) 7031 272691 - West Germany Love your enemies. It'll make 'em crazy. AMIGA - the only way to go!
alf@xenon.stgt.sub.org (Ingo Feulner) (08/12/90)
In article <2658@xenon.stgt.sub.org>, alf@xenon.stgt.sub.org (Ingo Feulner) writes: > Here is one. It comes with a public domain C compiler named Sozobon C (for > the Amiga), so I think I can post this. Sorry, I must correct me. This routine comes with DICE, the C compiler written by Matt Dillon. -Ingo -- Ingo Feulner - alf@xenon.stgt.sub.org Wolfacher Weg 22 - 7030 Boeblingen - (+49) 7031 272691 - West Germany Love your enemies. It'll make 'em crazy. AMIGA - the only way to go!
karl@haddock.ima.isc.com (Karl Heuer) (08/13/90)
In article <1990Aug11.230626.2706@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes: >I don't know of any redistributable strftime() implementation. There's a strftime() in the Olson-Harris ctime package; I'm not sure of its redistributability. I've heard the whole package is PD, but there's a Berkeley copyright notice in strftime.c. Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
zvs@bby.oz.au (Zev Sero) (08/16/90)
In article <2658@xenon.stgt.sub.org> alf@xenon.stgt.sub.org (Ingo Feulner) supplies a strftime() function. A few problems: size_t strftime(buf, max, fmt, tm) char *buf; size_t max; const char *fmt; const struct tm *tm; { All very well, but nowhere in the function is max ever checked! _______ short i = 0; buf[i] = 0; while (*fmt) { char *ptr; > i += strlen(buf + i); if (*fmt != '%') { buf[i++] = *fmt++; continue; } What on earth is the function of the line indicated? I can't imagine what the author is *trying* to do, but here is how it will actually work (or rather, not work). Imagine the following setup: buf is passed to strftime() containing "Some message", and fmt contains "Date: %A %d %B %Y". The first thing strftime() does is short i = 0; buf[i] = 0; so buf now contains {'\0','o','m','e',' ','m','e','s','s','a','g','e','\0'}. Now *fmt is 'D', so we enter the loop. while (*fmt) { i += strlen(buf + i); strlen (buf + 0) is 0, so i still equals 0. if (*fmt != '%') { buf[i++] = *fmt++; continue; buf now contains {'D','o','m','e',' ','m','e','s','s','a','g','e','\0'}, i is 1, and fmt points to "ate: %A %d %B %Y". Now *fmt is 'a', so we enter the loop. while (*fmt) { i += strlen(buf + i); strlen (buf + 1) is 11, so i is now 12! _________ case '%': strcpy(ptr, "%"); break; And many similar cases. Surely *ptr = '%'; would be much more efficient! _________ case 'I': /* hour as integer 01-12 */ sprintf(ptr, "%02d", (tm->tm_hour % 12) + 1); break; Say what???!!! This should, of course, be something like sprintf(ptr, "%02d", tm->tm_hour % 12 ? tm->tm_hour % 12 : 12); _________ case 'j': /* day of year as int 001-366 */ sprintf(ptr, "%03d", tm->tm_yday); break; case 'm': /* month as integer 01-12 */ sprintf(ptr, "%02d", tm->tm_mon); break; Must add 1 in both cases _________ case 'p': /* 'AM' or 'PM' */ if (tm->tm_hour >= 12) { strcpy(ptr, "PM"); } else { strcpy(ptr, "AM"); } break; Would be better as *ptr++ = tm->tm_hour < 12 ? 'A' : 'P'; *ptr = 'M'; _________ case 'U': /* week of year as int 00-53, regard sunday as first day in week */ { int bdiw = tm->tm_yday - tm->tm_wday; /* beginning of week */ if (bdiw < 0) bdiw = 0; else bdiw = bdiw / 7; sprintf(ptr, "%02d", bdiw); } break; This will only work if the year begins on a Sunday. Consider what happens if the year begins on a Monday. Sunday 7 Jan has a yday of 6, and a wday of 0. (6 - 0) / 7 gives an answer of 0. The correct answer is 1. This code should be: case 'U': /* Sunday week of the year */ sprintf (ptr, "%02d", (tm->yday + 6 - tm->wday) / 7); break; _________ case 'W': /* day of week as int 0-6, monday == 0 */ { int dowmon = tm->tm_wday - 1; if (dowmon < 0) dowmon += 7; sprintf(ptr, "%d", dowmon); } break; This is wrong. %W is `the Monday week of the year, from 00', i.e. it is exactly the same as %U except that the week is considered to run from Monday to Sunday. The correct formula is: case 'W': /* Monday week of the year */ sprintf (ptr, "%02d", (tm->yday + 6 - (tm->wday ? tm->wday - 1 : 6)) / 7); ________ case 'x': /* the locale's default rep for date */ sprintf(ptr, "%s %s %02d", AbDow[tm->tm_wday], AbMonth[tm->tm_mon], tm->tm_mday ); break; case 'X': /* the locale's default rep for time */ sprintf(ptr, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec ); break; These are obviously not fully implemented. Fair enough, provided that the documentation makes this clear. ________ case 'y': /* year within the century 00-99 */ sprintf(ptr, "%02d", tm->tm_year % 100); break; Should be (tm->tm_year + 1900) % 100, to handle years before 1900. (Years BCE are not handled properly anyway by lots of things, and it's too much bother to try). ________ case 'Z': /* the name of the time zone or "" */ strcpy(ptr, ""); break; What does this do? If you've decided to do nothing because you don't know how to find the correct timezone (and I don't know how either), then why not case 'Z': /* I don't know how to do this */ break; Why the useless strcpy()? This should be documented, so that individual users can change the code to put in their local timezone. Our implementation of strftime() here simply puts in "EST", and this is clearly documented. ________ i += strlen(buf + i); What does this do? _______ return((size_t)i); i should have been defined as a size_t in the first place. If you have already made the (quite reasonable) assumption that the size of the buffer will fit comfortably in an int, why cast it now? -- Zev Sero - zvs@bby.oz.au Violence is not a pleasant thing. It has caused much suffering in the world since its invention, and many are convinced that it is Quite A Bad Thing. - Steven Megachiropter Foster
ado@elsie.UUCP (Arthur David Olson) (08/18/90)
> I don't know of any redistributable strftime() implementation.
See pub/tz89.shar.Z on uunet.uu.net for a redistributable strftime
(among other things).
--
Arthur David Olson ado@alw.nih.gov ADO is a trademark of Ampex.