summaryrefslogtreecommitdiff
path: root/ts
diff options
context:
space:
mode:
Diffstat (limited to 'ts')
-rwxr-xr-xts154
1 files changed, 154 insertions, 0 deletions
diff --git a/ts b/ts
new file mode 100755
index 0000000..2a04392
--- /dev/null
+++ b/ts
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+ts - timestamp input
+
+=head1 SYNOPSIS
+
+ts [-r] [-i] [format]
+
+=head1 DESCRIPTION
+
+ts adds a timestamp to the beginning of each line of input.
+
+The optional format parameter controls how the timestamp is formatted,
+as used by L<strftime(3)>. The default format is "%b %d %H:%M:%S". In
+addition to the regular strftime conversion specifications, "%.S" and "%.s"
+are like "%S" and "%s", but provide subsecond resolution
+(ie, "30.00001" and "1301682593.00001").
+
+If the -r switch is passed, it instead converts existing timestamps in
+the input to relative times, such as "15m5s ago". Many common timestamp
+formats are supported. Note that the Time::Duration and Date::Parse perl
+modules are required for this mode to work. Currently, converting localized
+dates is not supported.
+
+If both -r and a format is passed, the existing timestamps are
+converted to the specified format.
+
+If the -i switch is passed, ts timestamps incrementally instead. Every
+timestamp will be the time elapsed since the last timestamp.
+The default format changes to "%H:%M:%S", and "%.S" and "%.s" can be used
+as well.
+
+=head1 ENVIRONMENT
+
+The standard TZ environment variable controls what time zone dates
+are assumed to be in, if a timezone is not specified as part of the date.
+
+=head1 AUTHOR
+
+Copyright 2006 by Joey Hess <joey@kitenet.net>
+
+Licensed under the GNU GPL.
+
+=cut
+
+use warnings;
+use strict;
+use POSIX q{strftime};
+
+$|=1;
+
+my $rel=0;
+my $inc=0;
+use Getopt::Long;
+GetOptions("r" => \$rel, "i" => \$inc) || die "usage: ts [-r] [-i] [format]\n";
+
+if ($rel) {
+ eval q{
+ use Date::Parse;
+ use Time::Duration;
+ };
+ die $@ if $@;
+}
+
+my $use_format=@ARGV;
+my $format="%b %d %H:%M:%S";
+if ($inc) {
+ $format="%H:%M:%S";
+ $ENV{TZ}='GMT';
+}
+$format=shift if @ARGV;
+
+# For subsecond resolution, Time::HiRes is needed.
+my $hires=0;
+if ($format=~/\%\.[Ss]/) {
+ require Time::HiRes;
+ $hires=1;
+}
+
+my $lastseconds = 0;
+my $lastmicroseconds = 0;
+
+if ($hires) {
+ ($lastseconds, $lastmicroseconds) = Time::HiRes::gettimeofday();
+} else {
+ $lastseconds = time;
+}
+
+
+while (<>) {
+ if (! $rel) {
+ if ($hires) {
+ my $f=$format;
+ my ($seconds, $microseconds) = Time::HiRes::gettimeofday();
+ if ($inc) {
+ my $deltaseconds = $seconds - $lastseconds;
+ my $deltamicroseconds = $microseconds - $lastmicroseconds;
+ if ($deltamicroseconds < 0) {
+ $deltaseconds -= 1;
+ $deltamicroseconds += 1000000;
+ }
+ $lastseconds = $seconds;
+ $lastmicroseconds = $microseconds;
+ $seconds = $deltaseconds;
+ $microseconds = $deltamicroseconds;
+ }
+ my $s=sprintf("%06i", $microseconds);
+ $f=~s/\%\.([Ss])/%$1.$s/g;
+ print strftime($f, localtime($seconds));
+ }
+ else {
+ if ($inc) {
+ my $seconds = time;
+ my $deltaseconds = $seconds - $lastseconds;
+ $lastseconds = $seconds;
+ print strftime($format, localtime($deltaseconds));
+ } else {
+ print strftime($format, localtime);
+ }
+ }
+ print " ".$_;
+ }
+ else {
+ s{\b(
+ \d\d[-\s\/]\w\w\w # 21 dec 17:05
+ (?:\/\d\d+)? # 21 dec/93 17:05
+ [\s:]\d\d:\d\d # (time part of above)
+ (?::\d\d)? # (optional seconds)
+ (?:\s+[+-]\d\d\d\d)? # (optional timezone)
+ |
+ \w{3}\s+\d{1,2}\s+\d\d:\d\d:\d\d # syslog form
+ |
+ \d\d\d[-:]\d\d[-:]\d\dT\d\d:\d\d:\d\d.\d+ # ISO-8601
+ |
+ (?:\w\w\w,?\s+)? # (optional Day)
+ \d+\s+\w\w\w\s+\d\d+\s+\d\d:\d\d:\d\d
+ # 16 Jun 94 07:29:35
+ (?:\s+\w\w\w|\s[+-]\d\d\d\d)?
+ # (optional timezone)
+ |
+ \w\w\w\s+\w\w\w\s+\d\d\s+\d\d:\d\d
+ # lastlog format
+ )\b
+ }{
+ $use_format
+ ? strftime($format, localtime(str2time($1)))
+ : concise(ago(time - str2time($1), 2))
+ }exg;
+
+ print $_;
+ }
+}