【问题标题】:Calculating a delta of years from a date从日期计算年的增量
【发布时间】:2010-06-17 23:45:29
【问题描述】:

我正在尝试找出一种方法来计算记录的出生年份,当在给定日期将年龄保留为小数点后两位 - 在 Perl 中。

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

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

我想做的是根据这些记录获取出生年份 - 从理论上讲,它应该是一致的。问题是,当我尝试通过计算日期字段中的年份减去年龄之间的差异来推导它时,我遇到了舍入错误,导致结果看起来错误,而实际上它们是正确的。

我尝试使用 int() 或 sprintf() 的一些“聪明”组合来四舍五入,但无济于事。我查看了 Date::Calc 但看不到我可以使用的东西。

附言由于许多日期都在 1970 年之前,因此我不能不使用 UNIX 纪元。

【问题讨论】:

  • 每百分之一代表多少天? (可能有几个答案。)
  • pre-1970 不是问题:Unix time() 是有符号整数。但是,有一个更好的理由不使用它:闰年、闰秒……

标签: perl datetime date


【解决方案1】:

你试过DateTime吗?它将处理解析和减法。

【讨论】:

  • 感谢您的提示 - 我没有尝试过,但我会记住它以供将来参考。
【解决方案2】:

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 年前的纪元日期时似乎存在问题。分辨率不是问题,因为我只是比较年份。
  • 根据您的系统,gmtime 系列函数可能在 1902 年之前或 2038 年之后的几年中遇到问题。如果您的 perl 是其中之一,那么您有任何机会将与百岁老人打交道,为了安全起见,您可能需要使用DateTime
【解决方案3】:

使用 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,但是日期的手册::Manip for new( ... ) 说: 此方法采用参数“年”、“月”、“周”、“天”、“小时”、“分钟”、“秒”、“纳秒” ”和“月末”。除了“end_of_month”之外,所有这些都是数字。如果任何数字为负数,则整个持续时间为负数。 **所有数字都必须是整数。**您在使用非整数时是否发现任何问题?
【解决方案4】:

我赞同 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 日(实际上是 13 日,中午之后和下午 6 点之前),在此之前 mktime 开始返回 undef。因此,对于今天 99% 的人来说,这可能是可行的。

毫无意义的琐事: scalar localtime( 0x80000000 ) : 'Fri Dec 13 15:45:52 1901' 0x80000000 是 2 秒补码的最小整数)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多