Расчет дельты лет от даты - PullRequest
3 голосов
/ 16 июня 2010

Я пытаюсь найти способ подсчета года рождения для записей, когда задан возраст до двух десятичных знаков на определенную дату - в Perl.

Чтобы проиллюстрировать этот пример, рассмотрим эти две записи:

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

То, что я хочу сделать, это получить год рождения на основе этих записей - теоретически он должен быть последовательным. Проблема заключается в том, что когда я пытаюсь вывести его путем вычисления разницы между годом в поле даты минус возраст, я сталкиваюсь с ошибками округления, из-за которых результаты выглядят неверно, хотя они на самом деле верны.

Я пытался использовать некоторую «умную» комбинацию int () или sprintf () для округления, но безрезультатно. Я посмотрел на Date :: Calc, но не вижу что-то, что я могу использовать.

p.s. Поскольку многие даты относятся к 1970 году, я не могу, к сожалению, использовать для этого эпоху UNIX.

Ответы [ 4 ]

6 голосов
/ 16 июня 2010

Вы пробовали DateTime ?Он будет обрабатывать как разбор, так и вычитание.

4 голосов
/ 16 июня 2010

Функции Perl gmtime и localtime не имеют проблем с обработкой отрицательного ввода и дат до 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 дня.

2 голосов
/ 17 июня 2010

Используйте DateTime и DateTime :: Duration.

Когда вы вычитаете DateTime :: Duration из DateTime, вы получаете другой 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
1 голос
/ 16 июня 2010

Я повторю рекомендацию Оесора (второй раз сегодня) и повторю напоминание Мобруле, что 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 датируются 14 декабря 1902 года (фактически 13-м, после полудня и до 18:00), до которого mktime начинает возвращать undef.Так что для 99% людей, живущих сегодня, это, вероятно, подойдет.

Бессмысленные пустяки: scalar localtime( 0x80000000 ) : 'Fri Dec 13 15:45:52 1901' <- это отсечение (<code>0x80000000 - минимальное целое число с дополнением в 2 с)

...