[comp.lang.c] need "yy-ddd-hh:mm:ss ==>

tsingle@sunland.gsfc.nasa.gov (Tim Singletary) (01/19/91)

Help!  I need something to convert yy-ddd-hh:mm:ss (i.e. year,
day_of_year, hour, minute, second) to a unix-style
_number_of_seconds_since_00:00:00_GMT,_Jan._1,_1970_.

I tried to use Sun's timelocal() function but couldn't get it to work (it
lets you pass both _day_of_year_ and _month,_day_of_month_ with no way
to specify which is correct!).

Ideally what I'm looking for is source code to a timelocal() function,
but any tips or suggestions will be appreciated.

Thanks in advance, 

tim

--
Tim Singletary, August Automation Inc., (301) 286-7942
--
NRA extremist, etc.

jap@convex.cl.msu.edu (Joe Porkka) (01/19/91)

tsingle@sunland.gsfc.nasa.gov (Tim Singletary) writes:

>Help!  I need something to convert yy-ddd-hh:mm:ss (i.e. year,
>day_of_year, hour, minute, second) to a unix-style
>_number_of_seconds_since_00:00:00_GMT,_Jan._1,_1970_.

>I tried to use Sun's timelocal() function but couldn't get it to work (it

Since your using a Sun you can use strptime(), at least
in SunOS 4.1.

I have no idea if this is a Sun defined function, or an ANSI
function.

mike@bria.UUCP (Michael Stefanik) (01/20/91)

In article <TSINGLE.91Jan18120847@sunland.gsfc.nasa.gov> sunland.gsfc.nasa.gov!tsingle (Tim Singletary) writes:
>Help!  I need something to convert yy-ddd-hh:mm:ss (i.e. year,
>day_of_year, hour, minute, second) to a unix-style
>_number_of_seconds_since_00:00:00_GMT,_Jan._1,_1970_.

I am doing this with the full knowledge that people are going to send me
flaming mail about what an ugly solution this is, but ... here is a little
ditty I wrote that seems to even work ...

/*	@(#)tmparse.c	1.1 	parse a time specification
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#define ONE_DAY		86400L
#define ONE_YEAR	31564800L
#define ONE_THIRD	28800L

int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

main()
{
long	now;
char	buf[64];

	for (;;) {
		printf("Enter date: ");
		if ( gets(buf) == NULL )
			break;

		tmparse(buf,&now);
		printf("%s",ctime(&now));
		}
}

long tmparse(buf,when)
char	*buf;
long	*when;
{
long		date, now;
int		year = 1970, month = 0, day = 0;
int		hours = 0, mins = 0, secs = 0, count;
char		*ptr, *next;
struct tm	*lt;

	time(&now);
	lt = localtime(&now);

	if ( strchr(buf,'/') == NULL && strchr(buf,'-') == NULL ) {
		month = lt->tm_mon;
		day = lt->tm_mday - 1;
		year = lt->tm_year + 1900;
		next = buf;
		}
	else {
		month = atoi(strtok(buf,"/-")) - 1;
		day = atoi(strtok(NULL,"/-")) - 1;
		year = atoi(strtok(NULL," "));
		next = NULL;

		if ( year == 0 )
			year = lt->tm_year + 1900;
		else if ( year < 100 )
			year += 1900;
		}

	if ( year < 1970 ) {
		*when = 0L;
		return(0L);
		}

	if ( (ptr = strtok(next,":")) != NULL ) {
		while ( isspace(*ptr) )
			++ptr;
		hours = atoi(ptr);
		mins = atoi(strtok(NULL,":"));
		secs = atoi(strtok(NULL,""));
		}

	date = 0L;
	for ( count = 1970; count < year; count++) {
		date += ONE_YEAR;
		if ( count % 4 == 0 || count % 400 == 0 )
			date += ONE_DAY;
		}
	date -= (year - 1971) * ONE_THIRD;

	for ( count = 0; count < month; count++) {
		date += mdays[count] * ONE_DAY;
		if ( count == 1 && (year % 4 == 0 || year % 400 == 0) )
			date += ONE_DAY;
		}

	date += (day * ONE_DAY) + secs + (mins * 60) + (hours * 3600);

	lt = localtime(&date);
	date += (hours - lt->tm_hour) * 3600;

	*when = date;
	return(date);
}
-- 
Michael Stefanik, Systems Engineer (JOAT), Briareus Corporation
UUCP: ...!uunet!bria!mike
--
technoignorami (tek'no-ig'no-ram`i) a group of individuals that are constantly
found to be saying things like "Well, it works on my DOS machine ..."

guy@auspex.auspex.com (Guy Harris) (01/21/91)

In article <TSINGLE.91Jan18120847@sunland.gsfc.nasa.gov>
  tsingle@sunland.gsfc.nasa.gov (Tim Singletary) writes:
> Help!  I need something to convert yy-ddd-hh:mm:ss (i.e. year,
> day_of_year, hour, minute, second) to a unix-style
> _number_of_seconds_since_00:00:00_GMT,_Jan._1,_1970_.
> 
> I tried to use Sun's timelocal() function but couldn't get it to work (it
> lets you pass both _day_of_year_ and _month,_day_of_month_ with no way
> to specify which is correct!).

It lets you pass day-of-year, but it ignores it.  It looks at month and
day-of-month.  (As one of the Timezone Caballeros, and as the person who
put the "Arthur Olson" timezone stuff, including "timelocal()" - which
was adapted from code by Robert Elz, from an idea by Bob Kridle - into
SunOS 4.x, I know whereof I speak here.)

> Ideally what I'm looking for is source code to a timelocal() function,
> but any tips or suggestions will be appreciated.

Try grabbing the source to the "Arthur Olson" timezone code - the SunOS
code, as indicated, comes from that - from some archive site; it was
posted to "comp.sources.unix" or somesuch at some point.

In article <1991Jan18.200700.11045@msuinfo.cl.msu.edu>
  jap@convex.cl.msu.edu (Joe Porkka) writes:
>Since your using a Sun you can use strptime(), at least
>in SunOS 4.1.

No, not entirely.  "strptime()" converts a character string into a
"struct tm"; "timelocal()" - and "mktime()", in ANSI C and/or
POSIX-compliant systems such as SunOS 4.1 (POSIX, not ANSI C, in the
case of 4.1) - convert a "struct tm" into a UNIX time.

Think of "strptime()" as being the "inverse" of "strftime()", and
"timelocal()"/"mktime()" as being the "inverse" of "localtime()".

>I have no idea if this is a Sun defined function, or an ANSI
>function.

It's a Sun-defined function, and it doesn't really do what such a
function should do (as the person who defined it, I have the right to
say it's the wrong answer :-)).

It shouldn't be responsible for figuring out which fields of a time/date
specification may be elided, because that means it has to interpret the
format specification in more detail than is appropriate.  Instead, it
should blindly match the string against the format, and some
higher-level routine should try matching various format strings, e.g. 
one with the "seconds" field of a time and then one without. 

Unfortunately, AT&T's S5R4 "getdate()" routine, which operates similarly to
the aforementioned higher-level routine, doesn't seem to be quite right,
either; it's not affected, as far as I know, by the LANG or LC_TIME
environment variables except to the extent that it might change the
names it's willing to accept for months - I don't think you can set LANG
and have it change the syntaxes it's willing to accept for dates, times,
or date+time.

timr@gssc.UUCP (Tim Roberts) (02/12/91)

> int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

I have seen this construct in date solutions several times over the past
two weeks.  In my experience, it is almost always better to use an array of
the _accumulated_ days through the end of the month, rather than the actual
days in the month.  This generally lets one eliminate at least one loop.

For example:

int mdays[12] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

-- 
timr@gssc.gss.com	Tim N Roberts, CCP	Graphic Software Systems
						Beaverton, OR

This is a very long palindrome. .emordnilap gnol yrev a si sihT

ansok@stsci.EDU (Gary Ansok) (02/12/91)

In article <6586@gssc.UUCP> timr@gssc.UUCP (Tim Roberts) writes:
>> int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
>
>I have seen this construct in date solutions several times over the past
>two weeks.  In my experience, it is almost always better to use an array of
>the _accumulated_ days through the end of the month, rather than the actual
>days in the month.  This generally lets one eliminate at least one loop.
>
>For example:
>
>int mdays[12] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

This is likely to go off into a "speed vs. style" war, but I tend to
agree with those who say that unless this is a very time-critical
piece of code (hard to imagine for a date conversion routine, but I
suppose it's possible), the benefits of the first in readability and
maintainability far outweigh any speed gained by the second.

Another suggestion -- depending on how the rest of your code represents
months (and unless you're extremely tight on memory), use
    int mdays[13] = { 0, 31, 28, ...

This makes mdays[1] correspond to January, the way most people expect.
Of course, if January is 0 throughout your code, you may as well be 
consistenly confusing.

Besides, it doesn't necessarily mean another loop to do it this way.
See _The Elements of Programming Style_, pp. 52-54 (by Kernighan and
Plauger).  True, it did add another statement to the body of the loop.

Gary Ansok
ansok@stsci.edu

ts@cup.portal.com (Tim W Smith) (02/18/91)

If your standard libraries have a seconds to date string converter,
then there is a simple way to convert the other way.

	1. Get rough lower and upper bounds on the answer, such
	as YEAR * 365 * 24 * 3600 and (YEAR+1) * 366 * 24 * 3600.

	2. Write a function that compares two dates in string form
	and determines which is first.

	3. Do a binary search of the 3e6 possible values between
	the bounds determined in step #1, using the library routine
	that converts a time from seconds to a date string and
	the function your wrote in step #2.

With this method, you don't have to know about leap years, or other
funny things.  For example, if the next release of the library for
your system knows about certain leap seconds, all you have to do
is relink, and they your code knows about them too.

						Tim Smith

hugh@fivegl.co.nz (Hugh Grierson) (02/18/91)

In article <2323@stsci.EDU> ansok@stsci.EDU (Gary Ansok) writes:
>In article <6586@gssc.UUCP> timr@gssc.UUCP (Tim Roberts) writes:
>>> int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

>>int mdays[12] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

>This is likely to go off into a "speed vs. style" war, but I tend to
>agree with those who say that unless this is a very time-critical
>piece of code (hard to imagine for a date conversion routine, but I
>suppose it's possible), the benefits of the first in readability and
>maintainability far outweigh any speed gained by the second.

Maintainability????  How often *do* you have to change the number
of days in a month?????  (Sept 1752 notwithstanding)

Readability? A one-line comment...

-- 
Hugh Grierson     hugh@fivegl.co.nz | Perfection is attained not when there is
5GL International Ltd,              | no more that can be added, but when there
Auckland, New Zealand               | is no more that can be taken away.

pwb@newt.phys.unsw.OZ.AU (Paul W. Brooks) (02/21/91)

In article <1991Feb18.133937.28469@fivegl.co.nz>, hugh@fivegl.co.nz (Hugh Grierson) writes:
> In article <2323@stsci.EDU> ansok@stsci.EDU (Gary Ansok) writes:
> >In article <6586@gssc.UUCP> timr@gssc.UUCP (Tim Roberts) writes:
> >>> int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
> 
> >>int mdays[12] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
	:
	:
	:
> >suppose it's possible), the benefits of the first in readability and
> >maintainability far outweigh any speed gained by the second.
> 
> Maintainability????  How often *do* you have to change the number
> of days in a month?????  (Sept 1752 notwithstanding)

And how many times do you have to account for leap years? With the
first form, if leap year add 1 to mdays[1]. With the second form add
1 to each of mdays[1] to mdays[11]. 

I know which I'd rather!

Paul Brooks        |Internet: pwb@newt.phys.unsw.edu.au
Uni. of N.S.W.     |If you have trouble sleeping, try lying on the end of
Kensington NSW 2033|   your bed. With a little luck you'll drop off. 
AUSTRALIA          |                              - Mark Twain. 

steve@taumet.com (Stephen Clamage) (02/23/91)

In article <1991Feb18.133937.28469@fivegl.co.nz>, hugh@fivegl.co.nz (Hugh Grierson) writes:
| In article <2323@stsci.EDU> ansok@stsci.EDU (Gary Ansok) writes:
| >In article <6586@gssc.UUCP> timr@gssc.UUCP (Tim Roberts) writes:
| >>> int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
| 
| >>int mdays[12] = { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
|
| >suppose it's possible), the benefits of the first in readability and
| >maintainability far outweigh any speed gained by the second.
| 
| Maintainability????  How often *do* you have to change the number
| of days in a month?????  (Sept 1752 notwithstanding)

But what about when the program isn't working?  As Kernighan and Plauger
point out in "The Elements of Programming Style", it is easy to look at
the first version and know it is right ("Thirty days hath September," etc).
This allows you to move on to look for errors elsewhere.

Quick Quiz: is the second version (31, 59, ...) quoted in this posting
correct?  Did I change any of the numbers accidently (or on purpose)?
To find out, you have to do some arithmetic.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

dgil@pa.reuter.COM (Dave Gillett) (02/25/91)

In <1991Feb18.133937.28469@fivegl.co.nz> hugh@fivegl.co.nz (Hugh Grierson) writes:

>Maintainability????  How often *do* you have to change the number
>of days in a month?????  (Sept 1752 notwithstanding)

>Readability? A one-line comment...


     Sorry.  If I'm trying to find a defect in the program's date-handling, I
can verify by eye the {31, 28, ... } form.  Even with a comment, it may take
several moments to understand what the {31, 59, ... } form is trying to do --
especially if I didn't write this piece of code (which happens, you know).
     And it will take several minutes to verify that these magic numbers are
in fact calculated correctly, a waste of both the author's and the maintainer's
time and effort.  If your algorithm, for efficiency's sake, really must use
the numbers in this form, then the table should be built by the machine at run
time, and the numbers in the source should be the familiar ones that anyone can
easily verify.
                                                   Dave

enag@ifi.uio.no (Erik Naggum) (02/26/91)

In article <790@saxony.pa.reuter.COM>, Dave Gillett writes:
| In article <1991Feb18.133937.28469@fivegl.co.nz>, Hugh Grierson writes:
| >Maintainability????  How often *do* you have to change the number
| >of days in a month?????  (Sept 1752 notwithstanding)
| >Readability? A one-line comment...

| Sorry.  If I'm trying to find a defect in the program's date-
| handling, I can verify by eye the {31, 28, ... } form.  Even with a
| comment, it may take several moments to understand what the {31, 59,
| ... } form is trying to do -- especially if I didn't write this
| piece of code (which happens, you know).

Hey, that's what comments are for.

| And it will take several minutes to verify that these magic numbers
| are in fact calculated correctly, a waste of both the author's and
| the maintainer's time and effort.  If your algorithm, for
| efficiency's sake, really must use the numbers in this form, then
| the table should be built by the machine at run time, and the
| numbers in the source should be the familiar ones that anyone can
| easily verify.

I can't believe this.  At run-time, you do useful things, not table
generation.  You can easily do it pre-compile-time, once.  You write a
few shell commands to do that, or some simple-minded elisp code, and
include the code as a comment:

/*
 * cumulative days into year at beginning of month
 */
int cumulative_yday[] = {
	/*
	 * This list was generated by piping these three lines into
	 * /bin/sh.  Repeat with "3!!sh<CR>" in vi.
	 	days=0
	 	for i in 31 28 31 30 31 30 31 31 30 31 30 31
	 	do echo -n $days,; days=`expr $days + $i`; done
	 */
	0,31,59,90,120,151,181,212,243,273,304,334,
};

Ok, so this is UNIX-centric par excellence.

/*
 * cumulative days into year at beginning of month
 */
int cumulative_yday[] = {
	/*
	 * This list was generated by eval'ing this code (C-X C-E):
	   (let
	       ((mday '(31 28 31 30 31 30 31 31 30 31 30 31))
		(ydays 0))
	     (while mday
	       (insert (int-to-string ydays) ",")
	       (setq ydays (+ ydays (car mday))
		     mday (cdr mday))))
	 */
	0,31,59,90,120,151,181,212,243,273,304,334,
};

Ok, this is GNU Emacs-centric.  So sue me.

If you don't have quality editors, you should get one before you do
anything else.

This code was the direct result of a desire to amend a perceived lack
of tool-building initiative.  I also have flint stones to make fire,
if you're interested.  The little tool built above is not necessarily
the best possible tool, but it should give an indication of the desire
to minimize the work you would do.  (I spent 45 seconds writing the
shell lines, and a few minutes fumbling with the simple elisp code.)

Morale: Let the machines do the chores.

--
[Erik Naggum]					     <enag@ifi.uio.no>
Naggum Software, Oslo, Norway			   <erik@naggum.uu.no>

wirzenius@cc.helsinki.fi (Lars Wirzenius) (02/27/91)

In article <ENAG.91Feb26020137@holmenkollen.ifi.uio.no>, enag@ifi.uio.no (Erik Naggum) writes:
[ A Unix shell script and a GNU Emacs lisp-routine that generate a table
with cumulative counts from the beginning of the year deleted ]

Ok, suppose you want to have the cumulative counts, but don't run either
Unix nor GNU Emacs? You use the compiler:

int cumulative_yday[] = {
	0,			/* days before Jan 1st */
	0+31,			/* Feb 1st */
	0+31+28,		/* March 1st */
	0+31+28+31,		/* April 1st */
	...
};

Or is there anything I miss?
-- 
Lars Wirzenius    wirzenius@cc.helsinki.fi

ts@cup.portal.com (Tim W Smith) (02/28/91)

< I can't believe this.  At run-time, you do useful things, not table
< generation.  You can easily do it pre-compile-time, once.  You write a
< few shell commands to do that, or some simple-minded elisp code, and
< include the code as a comment:

[He then gives examples of including in comments in the source code
suggested shell or elisp commands that the user can use to generate
the table]

Well, I can't believe this, either!  What about simply doing this:

	#define	JAN	0
	#define	FEB	JAN+31
	#define	MAR	FEB+28
	#define	APR	MAR+31
	#define	MAY	APR+30
	#define	JUN	MAY+31
	#define	JUL	JUN+30
	#define	AUG	JUL+31
	#define	SEP	AUG+31
	#define	OCT	SEP+30
	#define	NOV	OCT+31
	#define	DEC	NOV+30

	int days[] =
	{
		JAN, FEB, MAR, APR,
		MAY, JUN, JUL, AUG,
		SEP, OCT, NOV, DEC
	};

When someone wishes to port this program to a different machine, they
don't have to have the shell or emacs or other tools to generate the
table.

< Morale: Let the machines do the chores.

But going out of our way to create these chores for the machines to do
seems to me to be counterproductive!

							Tim Smith