our
@EXPORT
=
qw( str2time time2str )
;
our
@EXPORT_OK
=
qw( nap str2date_time str2time time2str )
;
sub
nap ($) {
my
$period
=
shift
;
$period
=
$period
&&
$period
=~ m{ \A [\d._]+ \z }msx &&
$period
> 0
?
$period
: 1;
return
usleep( 1_000_000 *
$period
);
}
sub
str2date_time ($;$) {
my
(
$dstr
,
$zone
) =
@_
; ensure_class_loaded
'DateTime::Format::Epoch'
;
my
$dt
= DateTime->new(
year
=> 1970,
month
=> 1,
day
=> 1, );
my
$formatter
= DateTime::Format::Epoch->new
(
epoch
=>
$dt
,
unit
=>
'seconds'
,
type
=>
'int'
,
skip_leap_seconds
=> 1,
start_at
=> 0,
local_epoch
=>
undef
, );
my
$time
= str2time(
$dstr
,
$zone
);
defined
$time
or throw DateTimeCoercion,
args
=> [
$dstr
];
return
$formatter
->parse_datetime(
$time
);
}
sub
str2time ($;$) {
my
(
$dtstr
,
$zone
) =
@_
;
my
(
$year
,
$month
,
$day
,
$hh
,
$mm
,
$ss
,
$dst
,
$frac
,
$m
,
$h
,
$result
);
my
%day
=
(
sunday
=> 0,
monday
=> 1,
tuesday
=> 2,
tues
=> 2,
wednesday
=> 3,
wednes
=> 3,
thursday
=> 4,
thur
=> 4,
thurs
=> 4,
friday
=> 5,
saturday
=> 6, );
my
%month
=
(
january
=> 0,
february
=> 1,
march
=> 2,
april
=> 3,
may
=> 4,
june
=> 5,
july
=> 6,
august
=> 7,
september
=> 8,
sept
=> 8,
october
=> 9,
november
=>10,
december
=> 11, );
my
@suf
= (
qw( th st nd rd th th th th th th )
) x 3;
@suf
[11, 12, 13] =
qw( th th th )
;
$day
{
substr
$_
, 0, 3 } =
$day
{
$_
}
for
(
keys
%day
);
$month
{
substr
$_
, 0, 3 } =
$month
{
$_
}
for
(
keys
%month
);
my
$daypat
=
join
'|'
,
reverse
sort
keys
%day
;
my
$monpat
=
join
'|'
,
reverse
sort
keys
%month
;
my
$sufpat
=
join
'|'
,
reverse
sort
@suf
;
my
$dstpat
=
'bst|dst'
;
my
%ampm
= (
a
=> 0,
p
=> 12 );
my
(
$AM
,
$PM
) = ( 0, 12 );
my
$merid
= 24;
my
@lt
=
localtime
time
;
$dtstr
=
lc
$dtstr
;
$zone
= tz_offset(
$zone
)
if
(
$zone
);
1
while
(
$dtstr
=~ s{\([^\(\)]*\)}{ }mox);
$dtstr
=~ s{ (\A|\n|\z) }{ }gmox;
$dtstr
=~ s{ ([\d\w\s]) [\.\,] \s }{$1 }gmox;
$dtstr
=~ s{ , }{ }gmx;
$dtstr
=~ s{ (
$daypat
) \s* (den\s)? }{ }mox;
return
unless
(
$dtstr
=~ m{ \S }mx);
if
(
$dtstr
=~ s{ \s (\d{4}) ([-:]?)
(\d\d?) \2
(\d\d?)
(?:[Tt ]
(\d\d?)
(?:([-:]?)
(\d\d?)
(?:\6
(\d\d?)
(?:[.,]
(\d+) )?
)? )? )?
(?=\D)
}{ }mx) {
(
$year
,
$month
,
$day
,
$hh
,
$mm
,
$ss
,
$frac
)
= ($1, $3-1, $4, $5, $7, $8, $9);
}
unless
(
defined
$hh
) {
if
(
$dtstr
=~ s{ [:\s] (\d\d?) : (\d\d?) ( : (\d\d?) (?:\.\d+)? )? \s*
(?:([ap]) \.?m?\.? )? \s }{ }mox) {
(
$hh
,
$mm
,
$ss
) = ($1, $2, $4 || 0);
$merid
=
$ampm
{ $5 }
if
($5);
}
elsif
(
$dtstr
=~ s{ \s (\d\d?) \s* ([ap]) \.?m?\.? \s }{ }mox) {
(
$hh
,
$mm
,
$ss
) = ($1, 0, 0);
$merid
=
$ampm
{ $2 };
}
}
if
(
defined
$hh
&&
$hh
<= 12 &&
$dtstr
=~ s{ ([ap]) \.?m?\.? \s }{ }mox) {
$merid
=
$ampm
{ $1 };
}
unless
(
defined
$year
) {
TRY: {
if
(
$dtstr
=~ s{ \s (\d\d?) ([^\d_]) (
$monpat
) (\2(\d\d+))? \s}{ }mox) {
(
$year
,
$month
,
$day
) = ($5,
$month
{ $3 }, $1);
last
TRY;
}
if
(
$dtstr
=~ s{ \s (\d+) ([\-\./]) (\d\d?) (\2(\d+))? \s }{ }mox) {
(
$year
,
$month
,
$day
) = ($5, $3 - 1, $1);
(
$year
,
$day
) = ($1, $5)
if
(
$day
> 31);
return
if
(
length
$year
> 2 and
$year
< 1901);
last
TRY;
}
if
(
$dtstr
=~ s{ \s (\d+) \s* (
$sufpat
)? \s* (
$monpat
) }{ }mox) {
(
$month
,
$day
) = (
$month
{ $3 }, $1);
last
TRY;
}
if
(
$dtstr
=~ s{ (
$monpat
) \s* (\d+) \s* (
$sufpat
)? \s }{ }mox) {
(
$month
,
$day
) = (
$month
{ $1 }, $2);
last
TRY;
}
if
(
$dtstr
=~ s{ \s (\d\d) (\d\d) (\d\d) \s }{ }mox) {
(
$year
,
$month
,
$day
) = ($3, $2 - 1, $1);
}
}
if
(!
defined
$year
&&
$dtstr
=~ s{ \s (\d{2} (\d{2})?)[\s\.,] }{ }mox) {
$year
= $1;
}
}
$dst
= 1
if
(
$dtstr
=~ s{ \b (
$dstpat
) \b }{}mox);
if
(
$dtstr
=~ s{ \s \"? ([a-z]{3,4})
(
$dstpat
|\d+[a-z]*|_[a-z]+)? \"? \s }{ }mox) {
$zone
= tz_offset( $1 || 0 );
$dst
= 1
if
($2 && $2 =~ m{
$dstpat
}msx);
return
unless
(
defined
$zone
);
}
elsif
(
$dtstr
=~ s{ \s ([a-z]{3,4})? ([\-\+]?) -?
(\d\d?) :? (\d\d)? (00)? \s }{ }mox) {
$zone
= tz_offset( $1 || 0 );
return
unless
(
defined
$zone
);
$h
=
"$2$3"
;
$m
=
defined
$4 ?
"$2$4"
: 0;
$zone
+= 60 * (
$m
+ (60 *
$h
));
}
if
(
$dtstr
=~ m{ \S }msx) {
if
(
$dtstr
=~ s{ \A \s*(ut?|z)\s* \z }{}msx) {
$zone
= 0;
}
elsif
(
$dtstr
=~ s{ \s ([a-z]{3,4})? ([\-\+]?) -?
(\d\d?) (\d\d)? (00)? \s }{ }mox) {
$zone
= tz_offset( $1 || 0 );
return
unless
(
defined
$zone
);
$h
=
"$2$3"
;
$m
=
defined
$4 ?
"$2$4"
: 0;
$zone
+= 60 * (
$m
+ (60 *
$h
));
}
return
if
(
$dtstr
=~ m{ \S }mox);
}
if
(
defined
$hh
) {
if
(
$hh
== 12) {
$hh
= 0
if
(
$merid
==
$AM
) }
elsif
(
$merid
==
$PM
) {
$hh
+= 12 }
}
$year
-= 1900
if
(
defined
$year
&&
$year
> 1900);
$zone
+= 3600
if
(
defined
$zone
&&
$dst
);
$month
=
$lt
[4]
unless
(
defined
$month
);
$day
=
$lt
[3]
unless
(
defined
$day
);
unless
(
defined
$year
) {
$year
=
$month
>
$lt
[4] ?
$lt
[5] - 1 :
$lt
[5];
}
$hh
= 0
unless
(
defined
$hh
);
$mm
= 0
unless
(
defined
$mm
);
$ss
= 0
unless
(
defined
$ss
);
$frac
= 0
unless
(
defined
$frac
);
return
unless
(
$month
<= 11 &&
$day
>= 1 &&
$day
<= 31
&&
$hh
<= 23 &&
$mm
<= 59 &&
$ss
<= 59);
if
(
defined
$zone
) {
$result
=
eval
{
local
$SIG
{__DIE__} =
sub
{};
timegm(
$ss
,
$mm
,
$hh
,
$day
,
$month
,
$year
);
};
return
if
(!
defined
$result
||
(
$result
== -1
&& (
join
q()
,
$ss
,
$mm
,
$hh
,
$day
,
$month
,
$year
)
ne
'595923311169'
));
$result
-=
$zone
;
}
else
{
$result
=
eval
{
local
$SIG
{__DIE__} =
sub
{};
timelocal(
$ss
,
$mm
,
$hh
,
$day
,
$month
,
$year
);
};
return
if
(!
defined
$result
||
(
$result
== -1
&& (
join
q()
,
$ss
,
$mm
,
$hh
,
$day
,
$month
,
$year
)
ne
join
q()
, (
localtime
-1)[0 .. 5]));
}
return
$result
+
$frac
;
}
sub
time2str (;$$$) {
my
(
$format
,
$time
,
$zone
) =
@_
;
$format
//=
'%Y-%m-%d %H:%M:%S'
;
$time
//=
time
;
return
Date::Format::Generic->time2str(
$format
,
$time
,
$zone
);
}
1;