[ic] [datetime]
Stefan Hornburg (Racke)
racke at linuxia.de
Sun Mar 22 09:43:44 UTC 2009
Hello,
DateTime (http://datetime.perl.org/) is set of Perl modules which strives
to become the standard in the Perl world.
I wrote a tag/check/filter a while ago for calendaring and time
calculations. It's GPL and you are free to use and comment on it.
Regards
Racke
# example setting for central european timezone
# Variable DATETIME_TIMEZONE Europe/Berlin
# Require module DateTime::TimeZone::Europe::Berlin
Require module DateTime
Require module DateTime::Event::Recurrence
CodeDef datetime Filter
CodeDef datetime Routine <<EOF
sub {
my ($val) = @_;
my ($newval, %dthash, $tz);
# try to normalize input
if ($val !~ /^\d{8,14}$/) {
$newval = $Tag->filter('date_change', $val);
if ($newval =~ /^\d{8,14}$/) {
$val = $newval;
} else {
return;
}
}
%dthash = (year => substr($val, 0, 4),
month => substr($val, 4, 2),
day => substr($val, 6, 2),
hour => substr($val, 8, 2) || 0,
minute => substr($val, 10, 2) || 0,
second => substr($val, 12, 2) || 0,
);
if ($tz = $Tag->var('DATETIME_TIMEZONE', 1)) {
$dthash{time_zone} = $tz;
}
return \%dthash;
}
EOF
UserTag datetime Order function scope from to fmt
UserTag datetime AddAttr
UserTag datetime Routine <<EOR
sub {
my ($function, $scope, $from, $to, $fmt, $opt) = @_;
my ($from_dt, $to_dt, $from_now, $tz, %now_hash);
if ($tz = $Tag->var('DATETIME_TIMEZONE', 1)) {
%now_hash = (time_zone => $tz);
}
if (ref($from)) {
# DateTime object passed directly ?
if ($from->isa('DateTime')) {
$from_dt = $from;
} elsif ($from->isa('DateTime::Duration') && $function eq 'dump') {
$from_dt = $from;
}
} elsif ($from =~ /\S/) {
eval {
$from_dt = new DateTime(%{$Tag->filter('datetime',$from)});
};
if ($@) {
unless ($function eq 'check') {
$Tag->error({name => 'from',
set => errmsg('invalid date %s', $from)});
}
return;
}
} else {
$from_dt = DateTime->now(%now_hash);
$from_now = 1;
}
if ($function eq 'check') {
# date is valid, return DateTime object
return $from_dt;
} elsif ($function eq 'dump') {
my (%dtdmp, $dtlocdmp);
# dumping with uneval gives empty string
my %dtdmp = %$from_dt;
if (ref($dtdmp{locale})) {
$dtdmp{locale} = {%{$dtdmp{locale}}};
}
return uneval(\%dtdmp);
}
unless ($function eq 'sub' || $function eq 'add') {
if (ref($to) && $to->isa('DateTime')) {
$to_dt = $to;
} elsif ($to =~ /\S/) {
eval {
$to_dt = new DateTime(%{$Tag->filter('datetime', $to)});
};
if ($@) {
$Tag->error({name => 'to',
set => errmsg('invalid date %s', $to)});
return;
}
}
}
if ($function eq 'compare') {
return DateTime->compare_ignore_floating($from_dt, $to_dt);
} elsif ($function eq 'compose') {
# build date time object out of individual parameters
my %dthash = ();
for (qw(year month day hour minute second)) {
if (exists $opt->{$_}) {
$dthash{$_} = $opt->{$_};
}
}
$from_dt = new DateTime(%dthash);
if ($fmt) {
return $from_dt->strftime($fmt);
} else {
return $from_dt;
}
} elsif ($function eq 'diff') {
my $duration;
unless ($to_dt) {
if ($from_now) {
$Tag->error({name => 'to',
set => errmsg('missing to date for diff')});
return;
} else {
$to_dt = DateTime->now(%now_hash);
}
}
if ($scope eq 'days') {
if ($duration = $from_dt->delta_days($to_dt)) {
return $duration->weeks * 7 + $duration->days;
} else {
return 0;
}
} elsif ($scope eq 'minutes') {
my $cmp;
# determine first whether difference is positive or negative
unless ($cmp = DateTime->compare($from_dt, $to_dt)) {
return 0;
}
if ($duration = $from_dt->delta_ms($to_dt)) {
return $cmp * $duration->hours * 60 + $duration->minutes;
} else {
return 0;
}
} else {
$duration = $from_dt->subtract_datetime($to_dt);
}
} elsif ($function eq 'sub' || $function eq 'add') {
my $duration;
my $amount = $to;
if ($function eq 'sub') {
$amount = - $to;
}
if ($scope eq 'days') {
$from_dt->add(days => $amount);
} elsif ($scope eq 'business_days') {
while($amount){
$from_dt->add(days => 1);
if($from_dt->day_of_week() < 6){
$amount--;
}
}
} elsif ($scope eq 'weeks') {
$from_dt->add(weeks => $amount);
} elsif ($scope eq 'months') {
$from_dt->add(months => $amount);
}
if ($fmt) {
return $from_dt->strftime($fmt);
} else {
return $from_dt;
}
} elsif ($function eq 'list') {
my ($daily, $span, @list, $duration, $days);
# daily is default scope
$scope ||= 'day';
unless ($from_dt) {
$Tag->error({name => 'from',
set => errmsg('missing from date for diff')});
return;
}
unless ($to_dt) {
$Tag->error({name => 'to',
set => errmsg('missing to date for diff')});
return;
}
if ($scope eq 'year') {
$daily = DateTime::Event::Recurrence->yearly();
} elsif ($scope eq 'month') {
$daily = DateTime::Event::Recurrence->monthly();
} elsif ($scope eq 'week') {
$daily = DateTime::Event::Recurrence->weekly();
} elsif ($scope eq 'day') {
$daily = DateTime::Event::Recurrence->daily();
} else {
$Tag->error({name => 'datetime',
set => errmsg('invalid scope for list function')});
return;
}
# sanity check
$duration = $to_dt->subtract_datetime($from_dt);
if ($duration->is_negative()) {
$Tag->error({name => 'datetime',
set => sprintf('dates %s and %s are in the wrong order',
$from_dt->strftime('%Y%M%D'),
$to_dt->strftime('%Y%M%D'))});
return;
}
if ($duration->in_units('months') > 24) {
$Tag->error({name => 'datetime',
set => sprintf('exceeded maximum length of daily list')});
return;
}
@list = $daily->as_list(start => $from_dt, end => $to_dt);
if ($fmt) {
@list = map {$_->strftime($fmt)} @list;
}
return @list;
} elsif ($function eq 'month') {
return $from_dt->month();
} elsif ($function eq 'weekday') {
return $from_dt->day_of_week();
} elsif ($function eq 'year') {
return $from_dt->year();
}
}
EOR
CodeDef datetime OrderCheck 1
CodeDef datetime Routine <<EOR
sub {
my ($ref, $name, $value, $code) = @_;
my ($function, $cmp, $ret);
use vars qw/$CGI/;
if ($code =~ s/(\w+)(:+(\w+))?\s*//) {
$function = $1;
} else {
$function = 'check';
}
$cmp = Vend::Interpolate::filter_value('date_change', $CGI->{$3});
if ($function eq 'check') {
unless ($Tag->datetime('check', '', $value)) {
return (0, $name, "invalid date '$value'");
}
return (1, $name);
}
$ret = $Tag->datetime('compare', '', $value, $cmp);
unless (defined $ret) {
if ($Session->{errors}->{from}) {
return (0, $name, errmsg("Invalid date(s) %s", $value));
}
return (0, $name, errmsg("Invalid date(s) %s", $cmp));
}
if ($function eq 'after') {
if ($ret == 1) {
return (1, $name);
} else {
return (0, $name, errmsg("Date %s must be after %s", $value, $cmp));
}
}
if ($function eq 'notbefore') {
if ($ret >= 0) {
return (1, $name);
} else {
return (0, $name, errmsg("Date %s is before %s", $cmp, $value));
}
}
}
EOR
--
LinuXia Systems => http://www.linuxia.de/
Expert Interchange Consulting and System Administration
ICDEVGROUP => http://www.icdevgroup.org/
Interchange Development Team
More information about the interchange-users
mailing list