从日期计算年份差异的Delta

3

我正在尝试找出一种方法,在Perl中,当给定一个日期和年龄的两个小数时,计算记录的出生年份。

为了说明这个例子,请考虑以下两个记录:

date, age at date
25 Nov 2005, 74.23
21 Jan 2007, 75.38

我想做的是根据这些记录获取出生年份 - 理论上应该是一致的。问题在于,当我尝试通过计算日期字段中的年份减去年龄的差来推导它时,我遇到了舍入误差,使结果看起来不正确,而实际上它们是正确的。
我尝试使用一些“聪明”的int()或sprintf()组合来四舍五入,但都无济于事。我查看了Date::Calc,但找不到可以使用的内容。
附:由于许多日期早于1970年,因此我不幸不能使用UNIX纪元来解决这个问题。

1
每一百分之一代表多少天?(可能有几个答案。) - ysth
在1970年之前不是问题:Unix的time()是一个有符号整数。然而,不使用它有更好的理由:闰年、闰秒等。 - dolmen
4个回答

6

你尝试过DateTime吗?它可以处理解析和减法。


谢谢你的提示 - 我还没有尝试过,但我会记在心里以备将来参考。 - Spiros

4
Perl的gmtimelocaltime函数可以很好地处理负数输入和1970年之前的日期,无需担心。
use Time::Local;
$time = timegm(0,0,0,25,11-1,2005-1900);          # 25 Nov 2005
$birthtime = $time - (365.25 * 86400) * 74.23;    # ~74.23 years
print scalar gmtime($birthtime);                  # ==> Wed Sep 2 11:49:12 1931

实际出生日期可能会相差几天,因为一个百分之一的年份只能给你一个3-4天的分辨率。


谢谢 - 这似乎很好用。我之前使用的是Date::Calc,但它似乎无法处理1970年以前的日期。分辨率不是问题,因为我只需要比较年份。 - Spiros
1
根据您的系统,gmtime 函数族可能会在 1902 年前或 2038 年后的年份上出现问题。如果您的 Perl 版本属于这种情况并且有任何与百岁老人打交道的机会,为了安全起见,建议使用 DateTime - hobbs

2

使用 DateTime 和 DateTime::Duration。

当您从 DateTime 中减去一个 DateTime::Duration 时,您会得到另一个 DateTime。

use strict;
use warnings;
use DateTime::Format::Strptime;
use DateTime::Duration;

my $fmt = DateTime::Format::Strptime->new(
    pattern => '%d %b %Y',
    locale  => 'en_US',
);

my $start = $fmt->parse_datetime($ARGV[0]);
my $age = DateTime::Duration->new(years => $ARGV[1]);

my $birth = $start - $age;
print $fmt->format_datetime($birth), "\n";

以下是如何调用它的示例:
$ perl birth.pl "25 Nov 2005" 74.23
25 Sep 1931
$ perl birth.pl "21 Jan 2007" 75.38
21 Sep 1931

嗯,你正在使用 $age = DateTime::Duration->new(years => $ARGV[1]);,而 $ARGV[1] 的值是 74.23。但是 Date::Manip 的手册对于 new( ... ) 方法说:<cite>该方法接受参数 "years"、"months"、"weeks"、"days"、"hours"、"minutes"、"seconds"、"nanoseconds" 和 "end_of_month"。除了 "end_of_month" 之外,所有这些参数都是数字。如果其中任何一个数字为负数,则整个持续时间为负。所有数字都必须是整数。</cite> 如果使用非整数数字时,你有没有遇到任何问题? - Pablo Marin-Garcia

1

我赞同Oesor的建议(今天第二次),并重申mobrule的提醒,即perl可以处理负日期。因此,DateTime更可取。

但我想说明的是,这也可以使用POSIX::mktime完成:

my ( $year1, $mon1, $day1 ) = qw<1944 7 1>;
my ( $year2, $mon2, $day2 ) = qw<2006 5 4>;

my $time1 = POSIX::mktime( (0) x 3, $day1, $mon1 - 1, 72 );
my $time2 = POSIX::mktime( (0) x 3, $day2, $mon2 - 1, 72 );
my $years = $year2 - $year1 - ( $time2 < $time1 ? 1 : 0 );
# 61 years

需要注意的是,Perl内部时钟处理日期可以追溯到1902年12月14日(实际上是下午12点之后和下午6点之前的13日),在此之前mktime开始返回undef。因此,对于今天活着的99%的人来说,这可能已经足够了。

无意义的琐事:scalar localtime( 0x80000000 ) : 'Fri Dec 13 15:45:52 1901' <- 这是截止日期(0x80000000是二进制补码最小整数)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接