summaryrefslogtreecommitdiff
path: root/ts
blob: 2a04392c49a4e3f6d1e99721f3634fc9b97f2e2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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 $_;
	}
}