line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Time::Activated; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
## no critic (ProhibitSubroutinePrototypes, ProhibitAutomaticExportation) |
4
|
|
|
|
|
|
|
|
5
|
9
|
|
|
9
|
|
226660
|
use strict; |
|
9
|
|
|
|
|
10
|
|
|
9
|
|
|
|
|
190
|
|
6
|
9
|
|
|
9
|
|
28
|
use warnings; |
|
9
|
|
|
|
|
9
|
|
|
9
|
|
|
|
|
151
|
|
7
|
|
|
|
|
|
|
|
8
|
9
|
|
|
9
|
|
91
|
use 5.8.8; |
|
9
|
|
|
|
|
22
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=pod |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=encoding UTF-8 |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=cut |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Time::Activated - Syntactic sugar over time activated code supporting DateTime and ISO8601 (a.k.a. "Javascript dates"). |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 VERSION |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Version 0.12 |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=cut |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $VERSION = 0.12; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 SYNOPSIS |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use Time::Activated; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# simple statements |
33
|
|
|
|
|
|
|
time_activated after '1985-01-01T00:00:00', execute { print "A new feature has been activeted beginning Jan 1st 1985!" }; |
34
|
|
|
|
|
|
|
time_activated before '1986-12-31T00:00:00', execute { print "Support for this feature ends by 1986!" }; |
35
|
|
|
|
|
|
|
time_activated before '2000', execute { print "Let's dance like its 1999!" }; |
36
|
|
|
|
|
|
|
time_activated between '2016-01-01T00:00:00', '2016-12-31T23:59:59', execute { print "Business logic exception for 2016!" }; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# combined statements a la try {} catch {} by Try::Tiny (tm) |
39
|
|
|
|
|
|
|
time_activated |
40
|
|
|
|
|
|
|
after '1985-01T00:00:00-03:00', execute { print "New business logic!" }, # <-- Gotcha! it is a , |
41
|
|
|
|
|
|
|
before '1986-12-31T00:00:00-03:00', execute { print "Old business logic!" }; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# elements get evaluated in order |
44
|
|
|
|
|
|
|
time_activated |
45
|
|
|
|
|
|
|
before '1986-12-31T00:00:00-03:00', execute { print "Old business logic!" }, # <-- Switch that ; |
46
|
|
|
|
|
|
|
after '1985-01-01T00:00:00-03:00', execute { print "New business logic!" }; # <-- Switch that , |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# all overlapping allowed, all matching gets executed |
49
|
|
|
|
|
|
|
time_activated |
50
|
|
|
|
|
|
|
after '2018', execute { print "This is from 2018-01-01 and on." }, |
51
|
|
|
|
|
|
|
after '2018-06-01', execute { print "This is from 2018-06-01 and on only, but on top of what we do after 2018-01-01." }; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# FANCY and... cof... recommended syntax... |
54
|
|
|
|
|
|
|
time_activated |
55
|
|
|
|
|
|
|
after '2018' => execute { print "Welcome to new business process for 2018!" }, |
56
|
|
|
|
|
|
|
after '2019' => execute { print "This is added on top of 2018 processes for 2019!" }; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# DateTime objects can be used to define points in time |
59
|
|
|
|
|
|
|
my $dt = DateTime->new(year=>2018, month=>10, day=>16); |
60
|
|
|
|
|
|
|
time_activated |
61
|
|
|
|
|
|
|
after $dt => execute { print "This happens after 2018-10-16!" }; |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head1 DESCRIPTION |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
This modules aims at managing and documenting time activated code such as that which may araise from migrations and planified process changes in a way that can be |
66
|
|
|
|
|
|
|
integrated and tested in advance. |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
You can use Time::Activated C, C and C to state which parts of code will be executed on certain dates due to changing business rules, |
69
|
|
|
|
|
|
|
programmed web service changes in endpoints/contracts or other time related events. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=cut |
72
|
|
|
|
|
|
|
|
73
|
9
|
|
|
9
|
|
28
|
use Exporter 5.57 'import'; |
|
9
|
|
|
|
|
97
|
|
|
9
|
|
|
|
|
521
|
|
74
|
|
|
|
|
|
|
our @EXPORT = our @EXPORT_OK = qw(time_activated before after between execute); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=head1 EXPORTS |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
By default Time::Activated exports C, C, C, C and C. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
If you need to rename the C, C, C, C or C keyword consider using L to |
81
|
|
|
|
|
|
|
get L's flexibility. |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
If automatic exporting sound nasty: use Time::Activated qw(); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head1 SYNTAX |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
time_activated "CONDITION" "WHEN" "WHAT" |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head2 "CONDITION" |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Can be any of C, C, C. |
92
|
|
|
|
|
|
|
C, accepts a parameters representing a point in time B which the execute statement will be executed. |
93
|
|
|
|
|
|
|
C, accepts a parameters representing a point in time B, which the execute statement will be executed. |
94
|
|
|
|
|
|
|
C, accepts two parameters representing a range in time B, which the execute statement will be executed. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 "WHEN" |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Is either a DateTime object or a scalar representing a iso8601 (a.k.a. Javascript date) |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Expension is supported so '2000', '2000-01', '2000-01-01' and '2000-01-01T00:00' all are equivalents to '2000-01-01T00:00:00'. |
101
|
|
|
|
|
|
|
Timezones are supported and honored. Thus: |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
time_activated |
104
|
|
|
|
|
|
|
after '1999-12-31T23:00:00-01:00' => execute { print('Matches from 2000-01-01T00:00:00 GMT!') }, |
105
|
|
|
|
|
|
|
after '2000-01-01T00:00:00+01:00' => execute { print('Matches from 1999-01-01T23:00:00 GMT!') }; |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
C includes the exact time which is used as parameter, C does not. |
108
|
|
|
|
|
|
|
Thus using C and C with the same time parameter ensures that only one statement gets executed. |
109
|
|
|
|
|
|
|
i.e.: |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
time_activated |
112
|
|
|
|
|
|
|
before SOME_DATE => execute { print "Before!" }, |
113
|
|
|
|
|
|
|
after SOME_DATE => execute { print "After!" }; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=head2 "WHAT" |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Is either an anonymous code block or a reference to subroutine |
119
|
|
|
|
|
|
|
Code that will be executed on a given conditions in many ways: |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
time_activated |
122
|
|
|
|
|
|
|
after '2001' => execute \&my_great_new_feature; #No parameters can be passed with references... |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
time_activated |
125
|
|
|
|
|
|
|
after '2000' => execute { print 'Y2K ready!' }, |
126
|
|
|
|
|
|
|
after '2001' => execute (\&my_great_new_feature), #References with multilines need () |
127
|
|
|
|
|
|
|
after '2002' => execute { &my_great_new_feature("We need parameters by 2002")}; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=head2 CONSTANTS |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
It is cool to use constants documenting both time and intent. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
use constants PROCESS_X_CUTOVER_DATE => '2017-01-01T00:00:00'; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
time_activated after PROCESS_X_CUTOVER_DATE => execute { &new_business_process($some_state) }; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=cut |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=head1 TESTING |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
L is your friend. |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
use Test::More tests => 1; |
144
|
|
|
|
|
|
|
use Time::Activated; |
145
|
|
|
|
|
|
|
use Test::MockTime; |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Test::MockTime::set_absolute_time('1986-05-27T00:00:00Z'); |
148
|
|
|
|
|
|
|
time_activated after '1985-01-01T00:00:00-03:00' => execute { pass('Basic after') }; # this gets executed |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Test::MockTime::set_absolute_time('1984-05-27T00:00:00Z'); |
151
|
|
|
|
|
|
|
time_activated after '1985-01-01T00:00:00-03:00' => execute { fail('Basic after') }; # this does not get executed |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=cut |
154
|
|
|
|
|
|
|
|
155
|
9
|
|
|
9
|
|
30
|
use Carp; |
|
9
|
|
|
|
|
12
|
|
|
9
|
|
|
|
|
564
|
|
156
|
|
|
|
|
|
|
$Carp::Internal{ __PACKAGE__ }++; |
157
|
|
|
|
|
|
|
|
158
|
9
|
|
|
9
|
|
2886
|
use Sub::Name 0.08; |
|
9
|
|
|
|
|
2979
|
|
|
9
|
|
|
|
|
341
|
|
159
|
9
|
|
|
9
|
|
5579
|
use DateTime; |
|
9
|
|
|
|
|
676164
|
|
|
9
|
|
|
|
|
264
|
|
160
|
9
|
|
|
9
|
|
3742
|
use DateTime::Format::ISO8601; |
|
9
|
|
|
|
|
249722
|
|
|
9
|
|
|
|
|
4227
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=head2 time_activated |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
C is both the syntactical placeholder for gramar in C and the internal implementation of the modules functionality. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
Syntactically the structure is like so (note the ','s and ';'): |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
time_activated |
171
|
|
|
|
|
|
|
after ..., execute ..., |
172
|
|
|
|
|
|
|
before ..., execute ..., |
173
|
|
|
|
|
|
|
between ..., ... execute ...; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Alternatively some can be changed for a => for a fancy syntax. This abuses anonymous hashes, some inteligent selections of prototypes (stolen from L) and probably |
176
|
|
|
|
|
|
|
other clever perl-ish syntactical elements that escape my understanding. Note '=>'s, ','s and ';': |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
time_activated |
179
|
|
|
|
|
|
|
after ... => execute ..., |
180
|
|
|
|
|
|
|
before ... => execute ..., |
181
|
|
|
|
|
|
|
between ... => ... => execute ...; #Given. This does not look so fancy but more into the weird side... |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# Blatantly stolen from Try::Tiny since it really makes sence and changing it produces headaches. |
186
|
|
|
|
|
|
|
# Need to prototype as @ not $$ because of the way Perl evaluates the prototype. |
187
|
|
|
|
|
|
|
# Keeping it at $$ means you only ever get 1 sub because we need to eval in a list |
188
|
|
|
|
|
|
|
# context & not a scalar one |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub time_activated (@) { |
191
|
49
|
|
|
49
|
1
|
76
|
my (@stanzas) = @_; |
192
|
49
|
|
|
|
|
42
|
my $activations = 0; |
193
|
|
|
|
|
|
|
|
194
|
49
|
|
|
|
|
111
|
my $now = DateTime->now(); |
195
|
49
|
|
|
|
|
6973
|
foreach my $stanza (@stanzas) { |
196
|
99
|
100
|
|
|
|
612
|
if (ref($stanza) eq 'Time::Activated::Before') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
197
|
26
|
100
|
|
|
|
72
|
if ($now < $stanza->{before}) { |
198
|
16
|
|
|
|
|
1531
|
$stanza->{code}(); |
199
|
16
|
|
|
|
|
4009
|
$activations++; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
} elsif (ref($stanza) eq 'Time::Activated::After') { |
202
|
41
|
100
|
|
|
|
267
|
if ($now >= $stanza->{after}) { |
203
|
33
|
|
|
|
|
3533
|
$stanza->{code}(); |
204
|
33
|
|
|
|
|
6760
|
$activations++; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} elsif (ref($stanza) eq 'Time::Activated::Between') { |
207
|
32
|
100
|
|
|
|
94
|
if ($stanza->{after} > $stanza->{before}) { |
208
|
11
|
|
|
|
|
442
|
my $before = $stanza->{after}; |
209
|
11
|
|
|
|
|
12
|
$stanza->{after} = $stanza->{before}; |
210
|
11
|
|
|
|
|
12
|
$stanza->{before} = $before; |
211
|
|
|
|
|
|
|
} |
212
|
32
|
100
|
66
|
|
|
926
|
if ($now >= $stanza->{after} && $now <= $stanza->{before}) { |
213
|
26
|
|
|
|
|
5146
|
$stanza->{code}(); |
214
|
26
|
|
|
|
|
6706
|
$activations++; |
215
|
|
|
|
|
|
|
}; |
216
|
|
|
|
|
|
|
} else { |
217
|
0
|
0
|
|
|
|
0
|
croak('time_activated() encountered an unexpected argument (' . ( defined $stanza ? $stanza : 'undef' ) . ') - perhaps a missing semi-colon?' ); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
49
|
|
|
|
|
2013
|
return $activations; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head2 before |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
C defines a point in time before B which code is executed. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This does not happen before January 1st 2018 at 00:00 but does happen from that exact point in time and on. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
time_activated |
230
|
|
|
|
|
|
|
before '2018', execute { print "We are awaiting for 1/1/2018..." }; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Another fancy way to say do not do that before January 1st 2018 at 00:00. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
ime_activated |
235
|
|
|
|
|
|
|
before '2018' => execute { print "We are awaiting for 1/1/2018..." }; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
A fancy way to combine before statements. |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
time_activated |
240
|
|
|
|
|
|
|
before '2018' => execute { print "We are awaiting for 1/1/2018..." }, |
241
|
|
|
|
|
|
|
before '2019' => execute { print "Not quite there for 1/1/2019..." }; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=cut |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub before ($$;@) { |
246
|
26
|
|
|
26
|
1
|
39
|
my ( $before, $block, @rest ) = @_; |
247
|
|
|
|
|
|
|
|
248
|
26
|
50
|
|
|
|
54
|
croak 'Useless bare before()' unless wantarray; |
249
|
|
|
|
|
|
|
|
250
|
26
|
|
|
|
|
30
|
my $caller = caller; |
251
|
26
|
|
|
|
|
125
|
subname("${caller}::before {...} " => $block); |
252
|
|
|
|
|
|
|
|
253
|
26
|
|
|
|
|
41
|
return (bless({before => _spawn_dt($before), code => $block},'Time::Activated::Before'), @rest); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=head2 after |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
C defines a point in time after B which code is executed. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
time_activated |
261
|
|
|
|
|
|
|
after '2018' => execute { print "Wea are either at 1/1/2018 or after it..." }; |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
As with C statements can be combined with C, C and C with no limit. |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=cut |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub after ($$;@) { |
268
|
41
|
|
|
41
|
1
|
61
|
my ( $after, $block, @rest ) = @_; |
269
|
|
|
|
|
|
|
|
270
|
41
|
50
|
|
|
|
80
|
croak 'Useless bare after()' unless wantarray; |
271
|
|
|
|
|
|
|
|
272
|
41
|
|
|
|
|
50
|
my $caller = caller; |
273
|
41
|
|
|
|
|
171
|
subname("${caller}::after {...} " => $block); |
274
|
|
|
|
|
|
|
|
275
|
41
|
|
|
|
|
66
|
return (bless({after => _spawn_dt($after), code => $block},'Time::Activated::After'), @rest); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=head2 between |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
C defines two points in time between which code is executes B. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
time_activated |
283
|
|
|
|
|
|
|
between '2018' => '2018-12-31T23:59:59' => execute { print "This is 2018..." }; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
As with C statements can be combined with C, C and C with no limit. |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=cut |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub between ($$$;@) { |
290
|
32
|
|
|
32
|
1
|
45
|
my ( $after, $before, $block, @rest ) = @_; |
291
|
|
|
|
|
|
|
|
292
|
32
|
50
|
|
|
|
74
|
croak 'Useless bare between()' unless wantarray; |
293
|
|
|
|
|
|
|
|
294
|
32
|
|
|
|
|
49
|
my $caller = caller; |
295
|
32
|
|
|
|
|
169
|
subname("${caller}::between {...} " => $block); |
296
|
|
|
|
|
|
|
|
297
|
32
|
|
|
|
|
48
|
return (bless({before => _spawn_dt($before), after => _spawn_dt($after), code => $block},'Time::Activated::Between'), @rest); |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=head2 execute |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Exists for the sole reason of verbosity. |
303
|
|
|
|
|
|
|
Accepts a single parameters that must be a subroutine or anonymous code block. |
304
|
|
|
|
|
|
|
i |
305
|
|
|
|
|
|
|
execute { print "This is a verbose way of saying that this will be executed!" }; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=cut |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub execute(&) { |
310
|
99
|
|
|
99
|
1
|
72286
|
my ($code) = @_; |
311
|
99
|
|
|
|
|
276
|
return $code; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head2 PRIVATES |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=head3 _spawn_dt |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
C<_spawn_dt> is a private function defined in hopes that additional date formats can be used to define points in time. |
319
|
|
|
|
|
|
|
Currently supported formtats for all date time. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=cut |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
sub _spawn_dt { |
324
|
131
|
|
|
131
|
|
122
|
my ($iso8601_or_datetime) = @_; |
325
|
|
|
|
|
|
|
|
326
|
131
|
100
|
66
|
|
|
546
|
my $dt = ref $iso8601_or_datetime && $iso8601_or_datetime->isa('DateTime') |
327
|
|
|
|
|
|
|
? $iso8601_or_datetime |
328
|
|
|
|
|
|
|
: DateTime::Format::ISO8601->parse_datetime($iso8601_or_datetime); |
329
|
|
|
|
|
|
|
|
330
|
131
|
|
|
|
|
28547
|
return $dt; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
1; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
__END__ |