[comp.lang.perl] A head start

merlyn@iwarp.intel.com (Randal Schwartz) (03/01/90)

In article <1176@frankland-river.aaii.oz.au>, pem@frankland-river (Paul E. Maisano) writes:
| I am writing a perl program at the moment which is doing a lot of
| manipulation of dates and times.
| 
| I am maintaining a text file which contains dates such as 24/04/89 and
| to manipulate these dates easily I would like to convert that to "the
| number of seconds since Jan 1970". This is the reverse of what localtime()
| does. [I can't keep the number of seconds in the file rather than the
| date either since the file may be modified by hand.]

Well, as a headstart, here's what I did for 'newslat', which had to
convert a Usenet-standard date into an integer:

%offset = (
	'Jan89', 0, 'Feb89', 31, 'Mar89', 59,
	'Apr89', 90, 'May89', 120, 'Jun89', 151,
	'Jul89', 181, 'Aug89', 212, 'Sep89', 243,
	'Oct89', 273, 'Nov89', 304, 'Dec89', 334,
	'Jan90', 365, 'Feb90', 396, 'Mar90', 425,
	'Apr90', 456, 'May90', 486, 'Jun90', 516
); # that'll do for a while

### lotsa code deleted here ###
### we're in a loop named ARTICLE, $article is a filename, and $_ is the
### string: "Date: 24 June 90 11:23:45 GMT" for example

	unless (/\s(\d\d?)\s+(\w\w\w)\w*\s+(\d\d)\s+(\d\d?):(\d\d):(\d\d)\s+GMT/) {
		/(.*)/;
		warn "$article: unknown date format: $1";
		next ARTICLE;
	}
	($day,$monthname,$year,$hour,$minute,$second) = ($1,$2,$3,$4,$5,$6);
	unless (defined($offset{$monthname . $year})) {
		warn "$article: unknown month/year: $monthname/$year";
		next ARTICLE;
	}
	$when = ($offset{$monthname . $year}+$day-1)*86400 + $hour*3600 +
		$minute * 60 + $second + 599616000;

At this point, $when has the UNIX "seconds past Jan 1 1970 midnite
GMT" integer in it.  This routine is reasonably fast... the key is to
precompute the month/year offsets (I did it by hand :-) so that Perl
has only to perform a simple table lookup.

If you get a generic routine working, please forward it to me (or the
net).  I can also help you by email, if you need further assistance.

$\="hacker,";$,="another ";print"Just ","Perl ";
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

ktl@wag240.caltech.edu (Kian-Tat Lim) (03/01/90)

	Here's something I put together for a perl-based backup
script.  It only takes a specific date format, but it'll work until
2070 if your Unix uses an unsigned long for times.  Things also get
hairy around standard/daylight changeover time; I haven't looked to
see if it works properly there.

===== CUT HERE =====

sub date2secs {

;# Convert output of "date" to seconds since Jan 1 1970 00:00:00

;# Derived from GNU tar (and others) getdate.y, though not nearly as
;# smart about differing formats

;# Call like this:
;#	do "date2secs.pl";
;#	chop($date = `date`);
;#	$secs = &date2secs($date);

;# Days before first of each month

local(%month) = ("Jan",0,"Feb",31,"Mar",59,"Apr",90,"May",120,"Jun",151,
		 "Jul",181,"Aug",212,"Sep",243,"Oct",273,"Nov",304,"Dec",334);

;# Hours added to each zone to get GMT; single letters are military

local(%zone) = ("nst",3.5,			"ast",4,	"adt",3,
		"est",5,	"edt",4,	"cst",6,	"cdt",5,
		"mst",7,	"mdt",6,	"pst",8,	"pdt",7,
		"yst",9,	"ydt",8,	"hst",10,	"hdt",9,
		"gmt",0,	"bst",-1,	"eet",0,	"eest",-1,
		"met",-1,	"mest",-2,	"wet",-2,	"west",-3,
		"jst",-9,			"aest",-10,	"aesst",-11,
		"acst",-9.5,	"acsst",-10.5,	"awst",-8,
	"a",1,	"b",2,	"c",3,	"d",4,	"e",5,	"f",6,	"g",7,	"h",8,
	"i",9,	"k",10,	"l",11,	"m",12,	"n",-1,	"o",-2,	"p",-3,	"q",-4,
	"r",-5,	"s",-6,	"t",-7,	"u",-8,	"v",-9,	"w",-10,"x",-11,"y",-12,
	"z",0
	);

local($wday,$mm,$dd,$tm,$zz,$yy) = split(/ +/,$_[0]);
$zz =~ tr/A-Z/a-z/;
local($hr,$min,$sec) = split(/[:]/,$tm);

;# Note that the next line will fail in 2070 and the one after will
;# fail in 2101 -- but Unix's signed long for times fails in 2038!
;# We also fail to take into account dates before the epoch (1/1/1970).

$yy += 1900 if $yy < 100; $yy += 100 if $yy <= 69;
local($jdate) = 365 * ($yy - 1970) + int(($yy - 1970 + 1) / 4);
$jdate += $mdays{$mm} + $dd - 1;
$jdate++ if $mm ne "Jan" && $mm ne "Feb" && $yy % 4 == 0 &&
	($yy % 100 != 0 || $yy % 400 == 0);

$jdate *= 86400;
$jdate += (($hr + $zone{$zz}) * 60 + $min) * 60 + $sec;

}

;# Return 1 to indicate successful 'do'
1;

===== CUT HERE =====

--
Kian-Tat Lim (ktl@wagvax.caltech.edu, KTL @ CITCHEM.BITNET, GEnie: K.LIM1)

ktl@wag240.caltech.edu (Kian-Tat Lim) (03/02/90)

[Thoroughly embarrassed...]

There's a minor bug in the code I posted for local date to seconds
since GMT conversion.  The associative array %month should be renamed
to %mdays to be consistent with its usage.  Sorry!

For those interested, here's a quickie test to make sure it's working:

	do 'date2secs.pl' || die "Couldn't load file.\n";
	chop($date = `date`);
	print &date2secs($date)," ",time,"\n";

Of course, your `date` may not produce the same output ours does...

--
Kian-Tat Lim (ktl@wagvax.caltech.edu, KTL @ CITCHEM.BITNET, GEnie: K.LIM1)