【问题标题】:How can I calculate the age of a person in year, month, days?如何计算一个人的年、月、日年龄?
【发布时间】:2010-10-01 23:29:29
【问题描述】:

我想根据出生日期和当前日期相对于当前日期的年、月和日来计算一个人的年龄。

例如:

>>> calculate_age(2008, 01, 01)
1 years, 0 months, 16 days

任何指向执行此操作的算法的指针都将受到赞赏。

【问题讨论】:

  • 许多语言都可以在它们的标准库中做到这一点。任何首选语言还是您想要“纯”算法?
  • python-list 上有一个关于为什么计算年龄不是so straightforward a task 的讨论。从那次讨论中我找到了this implementation(我不会在这里复制和粘贴,因为它不是我的)。不过,我还没有发现任何图书馆下降。
  • 我认为您必须更好地指定要求。假设出生日期是 2008-02-01,日期是 2009-01-31,结果是什么? - 0年11个月31天? - 0 年 10 个月 28+31=59 天? - 0 年 10 个月 29+31=60 天?

标签: algorithm datetime math


【解决方案1】:

您可以使用 PHP date_diff http://php.net/manual/en/function.date-diff.phphttp://www.php.net/manual/en/datetime.diff.php 来实现您想要的,并且您可以使用 PHP 日期格式以您想要的任何格式输出它

$interval = date_diff(date_create(), date_create('2008-01-01 10:30:00')); echo $interval->format("You are %Y Year, %M Months, %d Days, %H Hours, %i Minutes, %s Seconds Old");

结果: You are 04 Year, 04 Months, 1 Days, 00 Hours, 56 Minutes, 36 Seconds Old

【讨论】:

    【解决方案2】:

    此算法打印下一种格式 -> 24 年 8 个月 2 天

    from dateutil.relativedelta import *
    from datetime import date
    
    def calc_age(dob):
      today = date.today()
      age = relativedelta(today, dob)
      print str(age.years)+" Years, "+str(age.months)+" Months,"+str(age.days)+" Days"
    
    #test it
    calc_age(date(1993,10,10))
    

    【讨论】:

      【解决方案3】:

      我知道它有点过时了,但这是我使用的一个简单代码,它具有 Python 库的强大功能(用于注释的 Python 3.5,但没有它也可以工作)。

      from datetime import date, timedelta
      
      def age(birth: date) -> (int, int, int):
              """
                  Get a 3-int tuple telling the exact age based on birth date.
              """
              today_age = date(1, 1, 1) + (date.today() - birth)
              # -1 because we start from year 1 and not 0.
              return (today_age.year - 1, today_age.month - 1, today_age.day - 1)
      

      【讨论】:

        【解决方案4】:

        我认为这些问题比仅一种编程语言更广泛。 如果您使用的是 C#,则可以使用 DateTimeOffset 来计算。

        var offset = DateTimeOffset.MinValue;
        var lastDate = DateTime.Today;
        var firstDate = new DateTime(2006,11,19);
        
        
        var diff = (lastDate- firstDate);
        var resultWithOffset = DateTimeOffset.MinValue.AddDays(diff.TotalDays);
        var result = resultWithOffset.AddDays(-offset.Day).AddMonths(-offset.Month).AddYears(-offset.Year);
        

        result.Dayresult.Monthresult.Year 将保存您需要的信息。

        【讨论】:

        • 我已经对此进行了测试,但它不起作用。它有一个偏移量
        【解决方案5】:

        dreeves 答案的 Swift 实现。

        ps。我使用两个 NSDate 而不是 (y,m,d) (ynow,mnow,dnow) 作为输入,这在实际使用中可能更方便。

        extension NSDate {
        
            convenience init(ageDateString:String) {
                let dateStringFormatter = NSDateFormatter()
                dateStringFormatter.dateFormat = "yyyy-MM-dd"
                dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
                let d = dateStringFormatter.dateFromString(ageDateString)!
                self.init(timeInterval:0, sinceDate:d)
            }
        
            func ageFrom(date: NSDate) -> (Int, Int, Int) {
                let cal = NSCalendar.currentCalendar()
        
                let y = cal.component(NSCalendarUnit.Year, fromDate: date)
                let m = cal.component(NSCalendarUnit.Month, fromDate: date)
                let d = cal.component(NSCalendarUnit.Day, fromDate: date)
                let ynow = cal.component(NSCalendarUnit.Year, fromDate: self)
                let mnow = cal.component(NSCalendarUnit.Month, fromDate: self)
                let dnow = cal.component(NSCalendarUnit.Day, fromDate: self)
        
                let t0 = y * 12 + m - 1       // total months for birthdate.
                var t = ynow * 12 + mnow - 1;   // total months for Now.
                var dm = t - t0;              // delta months.
                if(dnow >= d) {
                    return (Int(floor(Double(dm)/12)), dm % 12, dnow - d)
                }
                dm--
                t--
                return (Int(floor(Double(dm)/12)), dm % 12, Int((self.timeIntervalSince1970 - NSDate(ageDateString: "\(Int(floor(Double(t)/12)))-\(t%12 + 1)-\(d)").timeIntervalSince1970)/60/60/24))
            }
        
        }
        
        // sample usage
        let birthday = NSDate(ageDateString: "2012-7-8")
        print(NSDate().ageFrom(birthday))
        

        【讨论】:

          【解决方案6】:

          这是使用数学的借用规则。

                  DateTime dateOfBirth = new DateTime(2000, 4, 18);
                  DateTime currentDate = DateTime.Now;
          
                  int ageInYears = 0;
                  int ageInMonths = 0;
                  int ageInDays = 0;
          
                  ageInDays = currentDate.Day - dateOfBirth.Day;
                  ageInMonths = currentDate.Month - dateOfBirth.Month;
                  ageInYears = currentDate.Year - dateOfBirth.Year;
          
                  if (ageInDays < 0)
                  {
                      ageInDays += DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
                      ageInMonths = ageInMonths--;
          
                      if (ageInMonths < 0)
                      {
                          ageInMonths += 12;
                          ageInYears--;
                      }
                  }
                  if (ageInMonths < 0)
                  {
                      ageInMonths += 12;
                      ageInYears--;
                  }
          
                  Console.WriteLine("{0}, {1}, {2}", ageInYears, ageInMonths, ageInDays);
          

          【讨论】:

            【解决方案7】:

            首先,请注意假设,例如,如果您出生于 2 月 1 日,那么在接下来的 3 月 1 日,您正好是 1 个月大,即使您的天龄仅为 28 岁 - 小于平均一个月。年份也有可变长度(由于闰年),这意味着您生日时的年龄通常不是精确的整数年数。如果您想以年/月/日的形式表达您活着的确切时间,请参阅我的其他答案。但更有可能你想把它捏得恰到好处,所以在 2 月 1 日出生意味着每年 2 月 1 日你是 X 年 0 个月零 0 天,而在任何一个月的 1 日你都是 X 年 Y 个月,和 0 天。

            在这种情况下,请继续阅读。 (注意:以下仅适用于过去的日期。)

            给定一个出生日期(y,m,d),当前日期(ynow,mnow,dnow),以及一个给出unix/epoch time for a given date的函数tm(),下面将输出一个三元素列表,给出年龄为{年、月、天}:

            t0 = y*12 + m - 1;        # total months for birthdate.
            t = ynow*12 + mnow - 1;   # total months for Now.
            dm = t - t0;              # delta months.    
            if(dnow >= d) return [floor(dm/12), mod(dm,12), dnow-d];
            dm--; t--;
            return [floor(dm/12), mod(dm,12), 
                    (tm({ynow,mnow,dnow}) - tm({floor(t/12), mod(t,12)+1, d}))/60/60/24];
            

            如果您不喜欢所有楼层和模组,则以下是等效算法。但我认为上面的更好。一方面,它避免在不需要时调用tm()

            {yl, ml} = {ynow, mnow};
            if(mnow < m || mnow == m && dnow < d) yl--;
            if(dnow < d) ml--;
            years = yl - y;
            months = ml + 12*(ynow - yl) - m;
            yl = ynow;
            if(ml == 0) { ml = 12; yl--; }
            days = (tm({ynow, mnow, dnow}) - tm({yl, ml, d}))/60/60/24;
            return [years, months, days];
            

            【讨论】:

            • 顺便说一句,我相信这对 2 月 29 日出生的人来说是正确的。也就是说,他们比 2 月 28 日的整数年少一天,比 2 月 28 日的整数年多一天年 3 月 1 日,无论是否有 2 月 29 日。
            • 我实际上在我编写的 WP7 应用程序中使用了这段代码(针对 C# 进行了修改)。今天一定是我测试的幸运日,因为当我将 2012 年 1 月 31 日与 2011 年 10 月 20 日进行比较时,最后一部分,你调用 tm() 并以 d 作为最后一个参数,它窒息了,因为它是试图构造一个日期为 2011 年 9 月 31 日,这显然是不存在的。如果日期大于该月的天数,我通过将 d 设置为该年和该月的该月的天数 (yl, ml) 来解决它。如果语言不支持,编写代码并不难。 C# 中的 DateTime.DaysInMonth(年、月)。
            • 感谢您的错误报告!我想我用更智能的 tm() 版本对此进行了测试,其中 tm(2011,9,31) 与 tm(2011,10,1) 相同。在您的版本中,8 月 31 日之后的一个月是 9 月 30 日;在我的版本中,它是 10 月 1 日。这可能取决于更有意义的应用程序(并且可能实际上并不重要)。
            【解决方案8】:

            这不是一个简单的问题,因为上述日子(如果我们不考虑 leap-seconds)没有简单的公式。

            月份可以由 28、29、30 或 31 天组成;年可以是 365 或 366 天。因此,当您尝试计算月份和年份的完整时间单位时,就会出现问题。

            这是一个interesting article,它考虑了所有复杂的方面来解决您在 Java 中的问题。

            【讨论】:

            • 这不仅仅是“超过几个小时”。几天和几周仍然是明确的。真正的诀窍是定义一年的长度(一个月,一年的 12 日,也取决于该定义)。但其他一切都以秒为单位明确定义。请参阅下面的答案。
            • 我将它从“小时”更新为“天”,你是对的。我没有提到周,因为它通常不是衡量周期的理想方式(在示例中 ghoseb 也没有专门指周)
            【解决方案9】:

            这里的其他人有正确的想法 - 您只需要能够将其扩展到其他语言。许多计算机系统从特定点(“纪元”或“参考日期”)开始以秒为单位计算时间,并从那里计算出日期,并提供从秒转换为日期的方法。您应该能够以秒为单位获取当前时间,将出生日期转换为纪元的秒数​​,减去这两个值,然后将其除以一天中的秒数:

            int timenow = time(0);
            struct tm birthday = { tm_mday = 16 , .tm_mon = 1 , .tm_year = 1963 };
            int timebirth = mktime( &birthday_tm );
            int diff_sec = timenow - timebirth;
            int days = diff_sec / ( 24 * 60 * 60);
            printf("%d - %d = %d seconds, or %d days\n", timenow, timebirth, diff_sec, days);
            

            此特定代码中的错误是 mktime() 无法处理纪元之前的日期,即 1970 年 1 月 1 日在基于 Unix 的系统中。其他系统上也会存在类似问题,具体取决于它们如何计算时间。通用算法应该可以工作,你只需要知道你对特定系统的限制。

            【讨论】:

            • 顺便说一句,我对此投了赞成票,但您可能应该使用 time_t 而不是 int。 mktime() 是一个很棒的函数。
            • 是的,time_t 可能会更好。因此,一个 C 的乐趣的例子,你可以用类型名称做各种有趣的技巧。至少我在发布之前编译并运行了代码:-)。
            • ...除了一天没有固定的秒数。在夏令时边界上,天比一个小时更长或更短。世界各地的情况都不同。
            【解决方案10】:

            由于年、月甚至日的长度可能不均匀,因此您不能先将两个日期相减以获得简单的持续时间。如果您希望结果与人类对待日期的方式相匹配,则必须使用实际日期,使用您的语言的内置函数,这些函数比以往任何时候都更能理解日期。

            该算法的编写方式取决于您使用的语言,因为每种语言都有一组不同的函数。我自己在 Java 中的实现,使用了笨拙但技术上正确的 GregorianCalendar:

            Term difference (Date from, Date to) {
                GregorianCalendar cal1 = new GregorianCalendar();
                cal1.setTimeInMillis(from.getTime());
            
                GregorianCalendar cal2 = new GregorianCalendar();
                cal2.setTimeInMillis(to.getTime());
            
                int years = cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR);
                int months = cal2.get(Calendar.MONTH) - cal1.get(Calendar.MONTH);
                int days = cal2.get(Calendar.DAY_OF_MONTH) - cal1.get(Calendar.DAY_OF_MONTH);
                if (days < 0) {
                    months--;
                    days += cal1.getActualMaximum(Calendar.DAY_OF_MONTH);
                }
                if (months < 0) {
                    years--;
                    months += 12;
                }
                return new Term(years, months, days);
            }
            

            这可能并不完美,但它提供了人类可读的结果。 Term 是我自己的一个类,用于存储人类风格的句点,因为 Java 的日期库没有。

            对于未来的项目,我计划转移到更好的 Joda 日期/时间库,其中确实有一个 Period 类,并且可能必须重写这个函数。

            【讨论】:

              【解决方案11】:

              如果您愿意违反“我的生日应该是整数岁”的原则,这里有一个方法。请注意,由于闰年,该原则并非严格正确,因此以下内容实际上更准确,尽管可能不太直观。如果您想知道某人的实际确切年龄,我会这样做。

              首先将出生日期和当前时间都转换为纪元时间(自时间黎明以来的秒数,即 unix 领域中的 1970 年)并减去它们。例如,请参阅此问题以了解如何做到这一点:How do I convert a date/time to epoch time (aka unix time -- seconds since 1970) in Perl?。您现在有了该人的确切年龄(以秒为单位),需要将其转换为“36 岁 3 个月 8 天”之类的字符串。

              这是执行此操作的伪代码。删除几周或几小时或任何您不想要的部分应该很简单......

              daysInYear = 365.2425;  # Average lengh of a year in the gregorian calendar.
                                      # Another candidate for len of a year is a sidereal year,
                                      # 365.2564 days.  cf. http://en.wikipedia.org/wiki/Year
              weeksInYear = daysInYear / 7;
              daysInMonth = daysInYear / 12;
              weeksInMonth = daysInMonth / 7;
              sS = 1;
              mS = 60*sS;
              hS = 60*mS;
              dS = 24*hS;
              wS = 7*dS;
              oS = daysInMonth*dS;
              yS = daysInYear*dS;
              
              # Convert a number of seconds to years,months,weeks,days,hrs,mins,secs.
              seconds2str[secs] 
              {
                local variables:
                  y, yQ= False, o, oQ= False, w, wQ= False,
                  d, dQ= False, h, hQ= False, m, mQ= False, s= secs;
              
                if(secs<0) return "-" + seconds2str(-secs);  # "+" is string concatenation.
              
                y = floor(s/yS);
                if(y>0) yQ = oQ = wQ = dQ = hQ = mQ = True;
                s -= y * yS;
              
                o = floor(s/oS);
                if(o>0) oQ = wQ = dQ = hQ = mQ = True;
                s -= o * oS;
              
                w = floor(s/wS);
                if(w>0) wQ = dQ = hQ = mQ = True;
                s -= w * wS;
              
                d = floor(s/dS);
                if(d>0) dQ = hQ = mQ = True;
                s -= d * dS;
              
                h = floor(s/hS);
                if(h>0) hQ = mQ = True;
                s -= h * hS;
              
                m = floor(s/mS);
                if(m>0) mQ = True;
                s -= m * mS;
              
                return
                  (yQ ? y + " year"  + maybeS(y) + " " : "") + 
                  (oQ ? o + " month" + maybeS(o) + " " : "") + 
                  (wQ ? w + " week"  + maybeS(w) + " " : "") + 
                  (dQ ? d + " day"   + maybeS(d) + " " : "") + 
                  (hQ ? dd(h) + ":" : "") + 
                  (mQ ? dd(m) + ":" + dd(round(s)) + "s" : s + "s");
              }
              
              
              # Returns an "s" if n!=1 and "" otherwise.
              maybeS(n) { return (n==1 ? "" : "s"); }
              
              # Double-digit: takes a number from 0-99 and returns it as a 2-character string.
              dd(n) { return (n<10 ? "0" + tostring(n) : tostring(n)); }
              

              【讨论】:

              • 不幸的是,如果没有真正考虑到闰年和夏令时,结果通常会是休息日。
              • 谢谢迈克尔!我在答案前对此提出了警告。您可能会争辩说,在这个人的生日上给出整数年的答案是经常休息一天!所以我认为这个答案也很有价值,具体取决于具体的应用。
              【解决方案12】:

              由于您显然在使用 python:创建日期时间对象,一个带有“生日”,一个带有“现在”,减去它们并从生成的 timedelta 对象中读取年龄。

              为什么要为闰年之类的事情烦恼?

              【讨论】:

              • 我使用 Python,但我真的在寻找一种可以用我使用的任何语言实现的通用算法。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-09-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-09-08
              相关资源
              最近更新 更多