From 3d0b7f7cbeb6955b0d2c8e72f4cc8c856e987ec5 Mon Sep 17 00:00:00 2001 From: hjp Date: Sat, 30 Sep 2006 11:45:21 +0000 Subject: [PATCH] Refactorization: moved computation of ticks into function get_ticks. --- TimeSeries.pm | 330 ++++++++++++++++++++++---------------------------- 1 file changed, 146 insertions(+), 184 deletions(-) diff --git a/TimeSeries.pm b/TimeSeries.pm index 5f317fd..8818f58 100644 --- a/TimeSeries.pm +++ b/TimeSeries.pm @@ -1,3 +1,5 @@ +#!/usr/bin/perl + package TimeSeries; =head1 NAME @@ -21,13 +23,16 @@ implemented. =cut +use warnings; +use strict; + use File::Temp qw(tempfile); use Time::Local; use Data::Dumper; use HTTP::Date qw(parse_date); use Time::Local qw(timegm_nocheck); -$VERSION = do { my @r=(q$Revision: 1.15 $=~/\d+/g);sprintf "%d."."%02d"x$#r,@r}; +our $VERSION = do { my @r=(q$Revision: 1.16 $=~/\d+/g);sprintf "%d."."%02d"x$#r,@r}; =head2 new(%opts) @@ -291,7 +296,7 @@ sub dstcorr { my ($time, $period) = @_; $period = 24 * 3600 unless ($period); - ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); + my ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); my $toff = ($hour * 3600 + $min * 60 * $sec) % $period; if ($toff != 0) { if ($toff > $period/2) { @@ -351,197 +356,22 @@ sub plot { print $ctlfh "set key $self->{legend_position}\n" if ($self->{legend_position}); print $ctlfh "set datafile missing '?'\n"; - # compute ticks - - # The spacing of the ticks a bit tricky: They should be related to - # common time units (1 hour, 1 day, 1 week, ...), which are - # irregular and not even of constant length (a day can be 23, 24 or - # 25 hours, a month 28 to 31 days, a year 365 or 366 days). Also the - # spacing shouldn't be too tight or too sparse. So there's quite a - # bit of special-case code below (but also much code duplication - # which should be cleaned up). - my $firsttime = $self->{data}[0][0]; my $lasttime = $self->{data}[$#{$self->{data}}][0]; - if ($lasttime - $firsttime > 3 * 365 * 24 * 3600) { - # more than 3 years: 1 tick/year - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = $min = $hour = 0; - $mday = 1; - $mon = int($mon/3) * 3; - $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - my $time; - for (;;) { - $time = timelocal($sec,$min,$hour,$mday,$mon,$year); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d" %d|, $year+1900, $mon+1, $mday, $time; - $mon += 3; - if ($mon >= 12) { - $mon -= 12; $year++; - } - if ($time > $lasttime) {last} - } - $lasttime = $time; - print $ctlfh ")\n"; - } elsif ($lasttime - $firsttime > 3 * 30 * 24 * 3600) { - # 3 to 36 months: 1 tick/month + my @tics = get_ticks($firsttime, $lasttime); - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = $min = $hour = 0; - $mday = 1; - $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - my $time; - for (;;) { - $time = timelocal($sec,$min,$hour,$mday,$mon,$year); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d" %d|, $year+1900, $mon+1, $mday, $time; - if (++$mon >= 12) { - $mon = 0; $year++; - } - if ($time > $lasttime) {last} - } - $lasttime = $time; - print $ctlfh ")\n"; - } elsif ($lasttime - $firsttime > 30 * 24 * 3600) { - # 30 ... 90 days: 1 tick/week. - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $firsttime -= 86400 * $wday; - - ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = $min = $hour = 0; - my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - for (;;) { - ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d" %d|, $year+1900, $mon+1, $mday, $time; - if ($time > $lasttime) {last} - - $time += 7 * 24 * 3600; - $time = dstcorr($time); - } - $lasttime = $time; - print $ctlfh ")\n"; - } elsif ($lasttime - $firsttime > 8 * 24 * 3600) { - # 8 .. 30 days: 1 tick per day. - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = $min = $hour = 0; - my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - for (;;) { - ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d" %d|, $year+1900, $mon+1, $mday, $time; - if ($time > $lasttime) {last} - - $time += 24 * 3600; - $time = dstcorr($time); - } - $lasttime = $time; - print $ctlfh ")\n"; - } elsif ($lasttime - $firsttime > 2 * 24 * 3600) { - # 2 .. 8 days: 1 tick/4 hours - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = $min = $hour = 0; - my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - for (;;) { - ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d %02d:%02d" %d|, $year+1900, $mon+1, $mday, $hour, $min, $time; - if ($time > $lasttime) {last} - - $time += 4 * 3600; - $time = dstcorr($time, 4 * 3600); - } - $lasttime = $time; - print $ctlfh ")\n"; - } elsif ($lasttime - $firsttime > 6 * 3600) { - # 6 hours to 2 days: 1 tick per hour. - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = $min = 0; - my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - for (;;) { - ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d %02d:%02d" %d|, $year+1900, $mon+1, $mday, $hour, $min, $time; - if ($time > $lasttime) {last} - - $time += 3600; - } - $lasttime = $time; - print $ctlfh ")\n"; - } else { - # less than 6 hours: 1 tick per minute. - # (ok, that's too much - need to find some intermediate steps, - # but for now I need graphs over a few minutes. - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); - $sec = 0; - my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); - print $ctlfh "set xtics rotate ("; - my $comma = 0; - for (;;) { - ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); - if ($comma) { - print $ctlfh ", "; - } else { - $comma = 1; - } - printf $ctlfh qq|"%04d-%02d-%02d %02d:%02d" %d|, $year+1900, $mon+1, $mday, $hour, $min, $time; - if ($time > $lasttime) {last} - - $time += 60; - } - $lasttime = $time; - print $ctlfh ")\n"; - } + print $ctlfh "set xtics rotate (", + join(", ", map sprintf(qq|"%s" %d|, $_->[1], $_->[0]), @tics), + ")\n"; # what to plot print $ctlfh "plot "; - $comma = 0; - $col = 2; + my $comma = 0; + my $col = 2; - for $i (@{$self->{legend}}) { + for my $i (@{$self->{legend}}) { if ($comma) { print $ctlfh ", "; } else { @@ -591,4 +421,136 @@ sub plot { return $graph; } +=head2 get_ticks($firsttime, $lasttime) + +Compute a "reasonable" set of ticks +covering the interval between $firsttime and $lasttime. + +The spacing of the ticks a bit tricky: They should be related to +common time units (1 hour, 1 day, 1 week, ...), which are +irregular and not even of constant length (a day can be 23, 24 or +25 hours, a month 28 to 31 days, a year 365 or 366 days). Also the +spacing shouldn't be too tight or too sparse. + +The function returns an ordered list of [ $timestamp, $label ] pairs. +$firsttime falls into the interval between the first and second timestamp. + +=cut + +sub get_ticks { + my ($firsttime, $lasttime) = @_; + + my @ticks = (); + + if ($lasttime - $firsttime > 3 * 365 * 24 * 3600) { + # more than 3 years: 4 ticks/year + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = $min = $hour = 0; + $mday = 1; + $mon = int($mon/3) * 3; + $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + my $time; + for (;;) { + $time = timelocal($sec,$min,$hour,$mday,$mon,$year); + push @ticks, [$time, sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday)]; + $mon += 3; + if ($mon >= 12) { + $mon -= 12; $year++; + } + if ($time > $lasttime) {last} + } + } elsif ($lasttime - $firsttime > 3 * 30 * 24 * 3600) { + # 3 to 36 months: 1 tick/month + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = $min = $hour = 0; + $mday = 1; + $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + my $time; + for (;;) { + $time = timelocal($sec,$min,$hour,$mday,$mon,$year); + push @ticks, [$time, sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday)]; + if (++$mon >= 12) { + $mon = 0; $year++; + } + if ($time > $lasttime) {last} + } + } elsif ($lasttime - $firsttime > 30 * 24 * 3600) { + # 30 ... 90 days: 1 tick/week. + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $firsttime -= 86400 * $wday; + + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = $min = $hour = 0; + my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + for (;;) { + ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); + push @ticks, [$time, sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday)]; + if ($time > $lasttime) {last} + + $time += 7 * 24 * 3600; + $time = dstcorr($time); + } + } elsif ($lasttime - $firsttime > 8 * 24 * 3600) { + # 8 .. 30 days: 1 tick per day. + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = $min = $hour = 0; + my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + for (;;) { + ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); + push @ticks, [$time, sprintf('%04d-%02d-%02d', $year+1900, $mon+1, $mday)]; + if ($time > $lasttime) {last} + + $time += 24 * 3600; + $time = dstcorr($time); + } + } elsif ($lasttime - $firsttime > 2 * 24 * 3600) { + # 2 .. 8 days: 1 tick/4 hours + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = $min = $hour = 0; + my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + for (;;) { + ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); + push @ticks, [$time, sprintf('%04d-%02d-%02d %02d:%02d', $year+1900, $mon+1, $mday, $hour, $min)]; + if ($time > $lasttime) {last} + + $time += 4 * 3600; + $time = dstcorr($time, 4 * 3600); + } + } elsif ($lasttime - $firsttime > 6 * 3600) { + # 6 hours to 2 days: 1 tick per hour. + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = $min = 0; + my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + for (;;) { + ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); + push @ticks, [$time, sprintf('%04d-%02d-%02d %02d:%02d', $year+1900, $mon+1, $mday, $hour, $min)]; + if ($time > $lasttime) {last} + + $time += 3600; + } + } else { + # less than 6 hours: 1 tick per minute. + # (ok, that's too much - need to find some intermediate steps, + # but for now I need graphs over a few minutes. + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($firsttime); + $sec = 0; + my $time = $firsttime = timelocal($sec,$min,$hour,$mday,$mon,$year); + for (;;) { + ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); + push @ticks, [$time, sprintf('%04d-%02d-%02d %02d:%02d', $year+1900, $mon+1, $mday, $hour, $min)]; + if ($time > $lasttime) {last} + + $time += 60; + } + } + return @ticks; +} + 1;