| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package EBook::Ishmael::Time; |
|
2
|
18
|
|
|
18
|
|
162439
|
use 5.016; |
|
|
18
|
|
|
|
|
69
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '2.03'; |
|
4
|
18
|
|
|
18
|
|
137
|
use strict; |
|
|
18
|
|
|
|
|
38
|
|
|
|
18
|
|
|
|
|
603
|
|
|
5
|
18
|
|
|
18
|
|
117
|
use warnings; |
|
|
18
|
|
|
|
|
33
|
|
|
|
18
|
|
|
|
|
1091
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
18
|
|
|
18
|
|
130
|
use Exporter 'import'; |
|
|
18
|
|
|
|
|
79
|
|
|
|
18
|
|
|
|
|
1236
|
|
|
8
|
|
|
|
|
|
|
our @EXPORT = qw(guess_time format_locale_time format_rfc3339_time); |
|
9
|
|
|
|
|
|
|
|
|
10
|
18
|
|
|
18
|
|
8733
|
use Time::Piece; |
|
|
18
|
|
|
|
|
148274
|
|
|
|
18
|
|
|
|
|
123
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $WEEKDAY_RX = qr/(? |
|
13
|
|
|
|
|
|
|
(Sun | Mon | Tue | Wed | Thu | Fri | Sat) | |
|
14
|
|
|
|
|
|
|
(Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday) |
|
15
|
|
|
|
|
|
|
)/x; |
|
16
|
|
|
|
|
|
|
my $MONTH_NAME_RX = qr/(? |
|
17
|
|
|
|
|
|
|
( |
|
18
|
|
|
|
|
|
|
Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec |
|
19
|
|
|
|
|
|
|
) | |
|
20
|
|
|
|
|
|
|
( |
|
21
|
|
|
|
|
|
|
January | Febuary | March | April | May | June | July | August | |
|
22
|
|
|
|
|
|
|
September | October | November | December |
|
23
|
|
|
|
|
|
|
) |
|
24
|
|
|
|
|
|
|
)/x; |
|
25
|
|
|
|
|
|
|
my $CENTURY_NUM_RX = qr/(?[0-9]?[0-9])/; |
|
26
|
|
|
|
|
|
|
my $MONTH_DAY_RX = qr/(?0?[1-9]|[12][0-9]|3[01])/; |
|
27
|
|
|
|
|
|
|
my $HOUR_24_RX = qr/(?[01]?[0-9]|2[0-3])/; |
|
28
|
|
|
|
|
|
|
my $HOUR_12_RX = qr/(?0?[1-9]|1[0-2])/; |
|
29
|
|
|
|
|
|
|
my $YEAR_DAY_RX = qr/(?0?(0?[1-9]|[1-9]{2})|[1-2][1-9]{2}|3[0-6]{2})/; |
|
30
|
|
|
|
|
|
|
my $MONTH_NUM_RX = qr/(?0?[1-9]|1[0-2])/; |
|
31
|
|
|
|
|
|
|
my $MINUTE_RX = qr/(?[0-5]?[0-9])/; |
|
32
|
|
|
|
|
|
|
my $AM_PM_RX = qr/(? AM|PM>)/i; |
|
33
|
|
|
|
|
|
|
my $SECONDS_RX = qr/(?[0-5]?[0-9]|6[01])/; |
|
34
|
|
|
|
|
|
|
my $WEEK_NUM_RX = qr/(?[0-4]?[0-9]|5[0-3])/; |
|
35
|
|
|
|
|
|
|
my $ORD_WEEK_DAY_RX = qr/(?[0-6])/; |
|
36
|
|
|
|
|
|
|
my $CENTURY_YEAR_RX = qr/(?[0-9]?[0-9])/; |
|
37
|
|
|
|
|
|
|
my $YEAR_RX = qr/(?[0-9]{1,4})/; |
|
38
|
|
|
|
|
|
|
my $TZ_SPEC_RX = qr/(?Z|[+\-][0-9]{2}:?[0-9]{2})/; |
|
39
|
|
|
|
|
|
|
my $TZ_NAME_RX = qr/(?[A-Z]{3})/; |
|
40
|
|
|
|
|
|
|
my $EPOCH_RX = qr/(?-?[0-9]+)/; |
|
41
|
|
|
|
|
|
|
my $HHMMSS_RX = qr/$HOUR_24_RX:$MINUTE_RX:$SECONDS_RX/; |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my @DATE_RXS = ( |
|
44
|
|
|
|
|
|
|
# strftime '%c' + '%Z' |
|
45
|
|
|
|
|
|
|
qr/$WEEKDAY_RX\s+$MONTH_NAME_RX\s+$MONTH_DAY_RX\s+$HHMMSS_RX\s+$YEAR_RX\s+$TZ_NAME_RX/, |
|
46
|
|
|
|
|
|
|
# strftime '%c' on my system (en_US.UTF-8) |
|
47
|
|
|
|
|
|
|
qr/$WEEKDAY_RX\s+$MONTH_NAME_RX\s+$MONTH_DAY_RX\s+$HHMMSS_RX\s+$YEAR_RX/, |
|
48
|
|
|
|
|
|
|
# RFC3339 |
|
49
|
|
|
|
|
|
|
qr/$YEAR_RX-$MONTH_NUM_RX-$MONTH_DAY_RX\x54$HHMMSS_RX$TZ_SPEC_RX/, |
|
50
|
|
|
|
|
|
|
# RFC822 |
|
51
|
|
|
|
|
|
|
qr/$MONTH_DAY_RX\s+$MONTH_NAME_RX\s+$CENTURY_YEAR_RX\s+$HOUR_24_RX:$MINUTE_RX\s+$TZ_NAME_RX/, |
|
52
|
|
|
|
|
|
|
qr/$MONTH_DAY_RX\s+$MONTH_NAME_RX\s+$CENTURY_YEAR_RX\s+$HOUR_24_RX:$MINUTE_RX\s+$TZ_SPEC_RX/, |
|
53
|
|
|
|
|
|
|
# RFC1123 |
|
54
|
|
|
|
|
|
|
qr/$WEEKDAY_RX,\s+$MONTH_DAY_RX\s+$MONTH_NAME_RX\s+$YEAR_RX\s+$HHMMSS_RX\s+$TZ_NAME_RX/, |
|
55
|
|
|
|
|
|
|
qr/$WEEKDAY_RX,\s+$MONTH_DAY_RX\s+$MONTH_NAME_RX\s+$YEAR_RX\s+$HHMMSS_RX\s+$TZ_SPEC_RX/, |
|
56
|
|
|
|
|
|
|
# RFC850 |
|
57
|
|
|
|
|
|
|
qr/$WEEKDAY_RX,\s+$MONTH_DAY_RX-$MONTH_NAME_RX-$CENTURY_YEAR_RX\s+$HHMMSS_RX\s+$TZ_NAME_RX/, |
|
58
|
|
|
|
|
|
|
# output of my date(1) |
|
59
|
|
|
|
|
|
|
qr/$WEEKDAY_RX\s+$MONTH_NAME_RX\s+$MONTH_DAY_RX\s+$HHMMSS_RX\s+$AM_PM_RX\s+$TZ_NAME_RX\s+$YEAR_RX/, |
|
60
|
|
|
|
|
|
|
# pdfinfo time format |
|
61
|
|
|
|
|
|
|
qr/$WEEKDAY_RX\s+$MONTH_NAME_RX\s+$MONTH_DAY_RX\s+$HHMMSS_RX\s+$YEAR_RX\s+$TZ_NAME_RX/, |
|
62
|
|
|
|
|
|
|
# Ruby date |
|
63
|
|
|
|
|
|
|
qr/$WEEKDAY_RX\s+$MONTH_NAME_RX\s+$MONTH_DAY_RX\s+$HHMMSS_RX\s+$TZ_SPEC_RX\s+$YEAR_RX/, |
|
64
|
|
|
|
|
|
|
# misc. date formats |
|
65
|
|
|
|
|
|
|
qr/$MONTH_DAY_RX\.$MONTH_NUM_RX\.$CENTURY_YEAR_RX/, |
|
66
|
|
|
|
|
|
|
qr/$MONTH_NUM_RX\.$MONTH_DAY_RX\.$CENTURY_YEAR_RX/, |
|
67
|
|
|
|
|
|
|
qr/$MONTH_DAY_RX\/$MONTH_NUM_RX\/$CENTURY_YEAR_RX/, |
|
68
|
|
|
|
|
|
|
qr/$MONTH_NUM_RX\/$MONTH_DAY_RX\/$CENTURY_YEAR_RX/, |
|
69
|
|
|
|
|
|
|
qr/$MONTH_DAY_RX\.$MONTH_NUM_RX\.$YEAR_RX/, |
|
70
|
|
|
|
|
|
|
qr/$MONTH_NUM_RX\.$MONTH_DAY_RX\.$YEAR_RX/, |
|
71
|
|
|
|
|
|
|
qr/$MONTH_DAY_RX\/$MONTH_NUM_RX\/$YEAR_RX/, |
|
72
|
|
|
|
|
|
|
qr/$MONTH_NUM_RX\/$MONTH_DAY_RX\/$YEAR_RX/, |
|
73
|
|
|
|
|
|
|
qr/$YEAR_RX-$MONTH_NUM_RX-$MONTH_DAY_RX/, |
|
74
|
|
|
|
|
|
|
qr/$YEAR_RX/, |
|
75
|
|
|
|
|
|
|
qr/$EPOCH_RX/, |
|
76
|
|
|
|
|
|
|
); |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my @FULL_MATCH_DATE_RXS = map { qr/^\s*$_\s*$/ } @DATE_RXS; |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
my %POST_PROCS = ( |
|
81
|
|
|
|
|
|
|
# Some versions of Time::Piece can't handle colons in time zone specs |
|
82
|
|
|
|
|
|
|
'z' => sub { $_[0] eq 'Z' ? '+0000' : $_[0] =~ s/://r }, |
|
83
|
|
|
|
|
|
|
); |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub guess_time { |
|
86
|
|
|
|
|
|
|
|
|
87
|
50
|
|
|
50
|
1
|
249572
|
my ($str) = @_; |
|
88
|
|
|
|
|
|
|
|
|
89
|
50
|
|
|
|
|
95
|
my %matches; |
|
90
|
50
|
|
|
|
|
87
|
my $found_match = 0; |
|
91
|
50
|
|
|
|
|
132
|
for my $rx (@FULL_MATCH_DATE_RXS) { |
|
92
|
384
|
100
|
|
|
|
2401
|
if ($str =~ $rx) { |
|
93
|
50
|
|
|
|
|
101
|
$found_match = 1; |
|
94
|
50
|
|
|
|
|
1776
|
%matches = %+; |
|
95
|
50
|
|
|
|
|
284
|
last; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
} |
|
98
|
50
|
50
|
|
|
|
149
|
if (!$found_match) { |
|
99
|
0
|
|
|
|
|
0
|
die "'$str' does not match any recognized date layout\n"; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
50
|
|
|
|
|
137
|
my @codes; |
|
103
|
|
|
|
|
|
|
my @parts; |
|
104
|
50
|
|
|
|
|
172
|
for my $k (keys %matches) { |
|
105
|
284
|
|
|
|
|
528
|
push @codes, "%$k"; |
|
106
|
284
|
100
|
|
|
|
592
|
if (exists $POST_PROCS{ $k }) { |
|
107
|
33
|
|
|
|
|
135
|
push @parts, $POST_PROCS{ $k }->($matches{ $k }); |
|
108
|
|
|
|
|
|
|
} else { |
|
109
|
251
|
|
|
|
|
536
|
push @parts, $matches{ $k }; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
50
|
|
|
|
|
140
|
my $tp = eval { Time::Piece->strptime(join(' ', @parts), join(' ', @codes)) }; |
|
|
50
|
|
|
|
|
680
|
|
|
114
|
50
|
100
|
|
|
|
2414
|
if ($@ ne '') { |
|
115
|
30
|
|
|
|
|
367
|
die "Failed to parse '$str'\n"; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
20
|
|
|
|
|
64
|
return $tp->epoch; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub format_locale_time { |
|
122
|
|
|
|
|
|
|
|
|
123
|
12
|
|
|
12
|
1
|
30
|
my $time = shift; |
|
124
|
|
|
|
|
|
|
|
|
125
|
12
|
|
|
|
|
57
|
return gmtime($time)->strftime("%c"); |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub format_rfc3339_time { |
|
130
|
|
|
|
|
|
|
|
|
131
|
34
|
|
|
34
|
1
|
13026
|
my $time = shift; |
|
132
|
|
|
|
|
|
|
|
|
133
|
34
|
|
|
|
|
151
|
return gmtime($time)->strftime("%Y-%m-%dT%H:%M:%S%z"); |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
1; |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head1 NAME |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
EBook::Ishmael::Time - Time-handling subroutines for ishmael |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
use EBook::Ishmael::Time qw(guess_time); |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
my $t = guess_time("01.14.2025"); |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
B is a module that provides various time-handling |
|
152
|
|
|
|
|
|
|
subroutines for L. This is a private module, please consult the |
|
153
|
|
|
|
|
|
|
L manual for user documentation. |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head1 SUBROUTINES |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=over 4 |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item $epoch = guess_time($str) |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
C guesses the timestamp format of C<$str> and returns the number |
|
162
|
|
|
|
|
|
|
seconds since the Unix epoch, or C if it could not identify the |
|
163
|
|
|
|
|
|
|
timestamp format. |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item $locale = format_locale_time($epoch) |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Formats the given time in the preferred format of the current locale. |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item $rfc3339 = format_rfc3339_time($epoch) |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Formats the given time as an RFC3339 timestamp. |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=back |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head1 AUTHOR |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Written by Samuel Young, Esamyoung12788@gmail.comE. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
This project's source can be found on its |
|
180
|
|
|
|
|
|
|
L. Comments and pull |
|
181
|
|
|
|
|
|
|
requests are welcome! |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Copyright (C) 2025-2026 Samuel Young |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify |
|
188
|
|
|
|
|
|
|
it under the terms of the GNU General Public License as published by |
|
189
|
|
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or |
|
190
|
|
|
|
|
|
|
(at your option) any later version. |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=cut |