【问题标题】:birthday age calculation, but also for number of months and days生日年龄计算,还有月数和天数
【发布时间】:2012-03-24 13:46:21
【问题描述】:

您好,所以我已经实现了这个解决方案,以从生日日期输入中获取用户的生日: Calculate age in C#

这很好用,但是我确实需要解释不到一岁的生日(婴儿,婴儿)。如果 bdate 和当前日期之间的时间少于 365 天,它将当前只给我一个“0”的年龄。

我的想法是这样的:

public string calculateAge(DateTime birthDate, DateTime now)
        {
            //BDay is in different year (age > 1)
            int age = now.Year - birthDate.Year;
            if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day)) age--;

            if (age == 0)
            {
                //Bday is in same year
                age = now.Month - birthDate.Month;
                if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day)) age--;

                return age.ToString() + " months";
            }
            if (age == 0)
            {
                //Bday is in the same month
                age = now.Day - birthDate.Day;
                if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day)) age--;

                return age.ToString() + " days";
            }
            return age.ToString();
        }

但是我的一些测试Bdays给了我这个:

(Today's date: 3/6/2012)
Bday1 = 3/5/2012
Age result = -1
Expected result = 1 day

Bday2 = 3/1/2012
Age result = 0 months
Expected result = 5 days

Bday3 = 1/1/2012
Age result = 2 months
Expected result = 2 months (this is fine)

Bday4 = 3/7/2011
Age result = -1 months
Expected result = 11 months

Bday5 = 3/1/2011
Age result = 1
Expected result = 1 (this is fine) 

您可以看到,由于当前的设置方式,问题源于当 bday 月份小于当前月份时,可能会产生一些负数。

我还看到了无法进入“days”循环的错误,但我认为现在这是一个有争议的问题。如果您对我可以做些什么来获得预期的结果有任何见解,请告诉我。另外,如果您还需要更多信息,例如测试生日。谢谢!

【问题讨论】:

    标签: c# date


    【解决方案1】:
    static public string calculateAge(DateTime birthDate, DateTime now)
    {
      birthDate = birthDate.Date;
      now = now.Date;
    
      var days = now.Day - birthDate.Day;
      if (days < 0)
      {
        var newNow = now.AddMonths(-1);
        days += (int)(now - newNow).TotalDays;
        now = newNow;
      }
      var months = now.Month - birthDate.Month;
      if (months < 0)
      {
        months += 12;
        now = now.AddYears(-1);
      }
      var years = now.Year - birthDate.Year;
      if (years == 0)
      {
        if (months == 0)
          return days.ToString() + " days";
        else
          return months.ToString() + " months";
      }
      return years.ToString();
    } 
    

    结果(目前 - 2012 年 3 月 7 日):

      3/5/2012: 2 days
      3/1/2012: 6 days
      1/1/2012: 2 months
      3/8/2011: 11 months
      3/1/2011: 1
    

    【讨论】:

      【解决方案2】:

      你可以使用Time Period Library for .NET的DateDiff类:

      // ----------------------------------------------------------------------
      public void DateDiffSample()
      {
        DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
        Console.WriteLine( "Date1: {0}", date1 );
        // > Date1: 08.11.2009 07:13:59
        DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
        Console.WriteLine( "Date2: {0}", date2 );
        // > Date2: 20.03.2011 19:55:28
      
        DateDiff dateDiff = new DateDiff( date1, date2 );
      
        // elapsed
        Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
        // > DateDiff.ElapsedYears: 1
        Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
        // > DateDiff.ElapsedMonths: 4
        Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
        // > DateDiff.ElapsedDays: 12
      } // DateDiffSample
      

      【讨论】:

        【解决方案3】:

        我们实际上在我们的框架中有一个“DateSpan”结构来进行类似的计算......它与您所追求的相关的关键在于定义“开始”和“结束”DateTime 变量的位置它具有以下属性:

            public int WholeMonths
            {
                get
                {
                    var startInEndsYear = Start.AddYears(End.Year - Start.Year);
        
                    // Are within a month of each other if EITHER:
                    // 1. Month is the same
                    // 2. Month period is within 1 
                    //    AND
                    //    The difference between days of the year is less than the number of days in the start's month
                    var sameMonth = End.Month == startInEndsYear.Month || (End.Month - 1 == Start.Month && (End.DayOfYear - startInEndsYear.DayOfYear) / (double)DateTime.DaysInMonth(startInEndsYear.Year, startInEndsYear.Month) < 1.0d );
                    var sameMonthAndDay = sameMonth && End.Day == Start.Day;
        
                    var res = (End.Year - Start.Year) * 12;
                    if (sameMonth && !sameMonthAndDay)
                    {
                        res -= (startInEndsYear > End) ? 1 : 0;
                    }
                    else if (sameMonthAndDay)
                    {
                        res -= (End.TimeOfDay < Start.TimeOfDay ? 1 : 0);
                    }
                    else
                    {
                        res -= Start.Month;
                        res += End.Month;
                    }
                    return res;
                }
            }
        

        以及依赖于它的属性:

            public int WholeYears
            {
                get
                {
                    return (int) Math.Floor(WholeMonths/12d);
                }
            }
        
            public int PartMonths
            {
                get
                {
                    return WholeMonths % 12;
                }
            }
        

        我将把它留作进一步的练习,让您根据这些属性转换成单词。

        编辑:这是天数的计算:

            public TimeSpan PartDays
            {
                get
                {
                    var startInEndsMonth = Start.AddMonths(WholeMonths);
                    return End.Subtract(startInEndsMonth);
                }
            }
        

        【讨论】:

        • 以上没有考虑“天”。但无论如何都会把它留在这里留给后代......
        【解决方案4】:

        这是一种计算年龄的方法,就像西方文化中的普通人为 CE dates 计算的那样。不同的文化和日历以不同的方式计算年龄。例如,China and other countries in Asia 推算一个新生婴儿在他出生的那一天是 1 岁,他的年龄在农历的每个农历新年都会增加。因此,如果一个孩子在农历新年前一个月出生,那么他在那个月将是 1 岁,然后在他出生后的一个月内将滴答作响到 2 岁。

        此算法不适用于 BCE 日期或跨越从儒略历到公历过渡的日期。无论如何,这都是一个冒险的命题,因为不同的地点,即使是在同一个国家,也会在不同的时间切换:例如,俄罗斯直到布尔什维克革命之后才切换到公历。

        因此,除非您有开始日期的语言环境和结束日期的语言环境,否则您无法准确计算儒略/格里高利分界的时间跨度。

        算法是:

        • 查找参考日期,即当前日期或之前的最近一个月生日。

          • 如果当前日期早于实际出生日期,则使用前一个月。如果实际出生日期晚于该月的最后一天,则将其限制在该月的最后一天。

            例如,如果当前日期是 2012 年 3 月 7 日,而实际生日是“1990 年 3 月 31 日”,那么您的参考日期是 2012 年 2 月 29 日。

        • 计算您的参考日期与出生日期之间的差异(以年和月为单位)。这很容易,因为在西方历法中,年份的月数是一致的。您可以使用整数除法:

          int totalMonths = (12 * endYear + endMonth) - (12 * startYear + startMonth); int 年 = totalMonths / 12 ; int 月 = totalMonths % 12 ;

        或者你可以根据需要进行减法和进位。

        int years  = endYear  - startYear  ;
        int months = endMonth - startMonth ;
        
        if ( months < 0 )
        {
          months += 12 ;
          years  -=  1 ;
        }
        

        无论哪种情况,结果都应该相同。

        天数部分是从参考日期到当前日期的天数。

        使用此算法,一个人在出生当天是 0 天。

        这是我的代码:

        static class HumanAgeFactory
        {
        
          public static HumanAge ComputeAge( this DateTime dob )
          {
            return dob.ComputeAgeAsOf( DateTime.Now ) ;
          }
        
          public static HumanAge ComputeAgeAsOf( this DateTime dob , DateTime now )
          {
            dob = dob.Date ; // toss the time component
            now = now.Date ; // toss the time component
        
            if ( dob > now ) throw new ArgumentOutOfRangeException( "dob" , "'now' must be on or after 'dob'" ) ;
        
            DateTime mostRecentBirthDay = MostRecentNthDayOfTheMonthOnOrBefore( dob.Day , now ) ;
            int      years              = mostRecentBirthDay.Year  - dob.Year          ;
            int      months             = mostRecentBirthDay.Month - dob.Month         ;
            int      days               = (int) ( now - mostRecentBirthDay ).TotalDays ;
        
            if ( months < 0 )
            {
              months += 12 ;
              years  -=  1 ;
            }
        
            if ( days   < 0 ) throw new InvalidOperationException() ;
            if ( months < 0 ) throw new InvalidOperationException() ;
            if ( years  < 0 ) throw new InvalidOperationException() ;
        
            HumanAge instance = new HumanAge( years , months , days ) ;
            return instance ;
          }
        
          private static DateTime MostRecentNthDayOfTheMonthOnOrBefore( int nthDay , DateTime now )
          {
            if ( nthDay < 1 ) throw new ArgumentOutOfRangeException( "dayOfBirth" ) ;
        
            int year  = now.Year  ;
            int month = now.Month ;
        
            if ( nthDay > now.Day )
            {
              --month ;
              if ( month < 1 )
              {
                month += 12 ;
                year  -=  1 ;
              }
            }
        
            int daysInMonth = CultureInfo.CurrentCulture.Calendar.GetDaysInMonth( year , month ) ;
            int day         = ( nthDay > daysInMonth ? daysInMonth : nthDay ) ;
        
            DateTime instance = new DateTime( year , month , day ) ;
            return instance ;
          }
        
        }
        
        public class HumanAge
        {
          public int Years  { get ; private set ; }
          public int Months { get ; private set ; }
          public int Days   { get ; private set ; }
        
          public override string ToString()
          {
            string instance = string.Format( "{0} {1} , {2} {3} , {4} {5}" ,
              Years  , Years  == 1 ? "year"  : "years"  ,
              Months , Months == 1 ? "month" : "months" ,
              Days   , Days   == 1 ? "day"   : "days"
              ) ;
            return instance ;
          }
        
          public HumanAge( int years , int months , int days )
          {
            if ( years  < 0                ) throw new ArgumentOutOfRangeException( "years"  ) ;
            if ( months < 0 || months > 12 ) throw new ArgumentOutOfRangeException( "months" ) ;
            if ( days   < 0 || days   > 31 ) throw new ArgumentOutOfRangeException( "days"   ) ;
        
            this.Years  = years  ;
            this.Months = months ;
            this.Days   = days   ;
        
            return ;
          }
        
        }
        

        【讨论】:

          【解决方案5】:

          你可以使用我发现的这个函数有点接近:

          /// <summary>
          /// Converts a timespan value to a string representation.
          /// </summary>
          /// <param name="time_span">the amount of time to convert to words</param>
          /// <param name="whole_seconds">round up the seconds</param>
          /// <returns>4 minutes, 58 seconds, etc</returns>
          /// <remarks>If it can't convert to a string, it returns "Calculating time remaining..."</remarks>
          public string TimespanToWords(TimeSpan time_span, bool whole_seconds = true)
          {
              TimeSpan span;
              string str2 = "";
              if (time_span.Days > 0)
              {
                  str2 = str2 + ", " + time_span.Days.ToString() + " days";
                  span = new TimeSpan(time_span.Days, 0, 0, 0);
                  time_span = time_span.Subtract(span);
              }
              if (time_span.Hours > 0)
              {
                  str2 = str2 + ", " + time_span.Hours.ToString() + " hours";
                  span = new TimeSpan(0, time_span.Hours, 0, 0);
                  time_span = time_span.Subtract(span);
              }
              if (time_span.Minutes > 0)
              {
                  str2 = str2 + ", " + time_span.Minutes.ToString() + " minutes";
                  span = new TimeSpan(0, 0, time_span.Minutes, 0);
                  time_span = time_span.Subtract(span);
              }
              if (whole_seconds)
              {
                  if (time_span.Seconds > 0)
                  {
                      str2 = str2 + ", " + time_span.Seconds.ToString() + " seconds";
                  }
              }
              else
              {
                  str2 = str2 + ", " + time_span.TotalSeconds.ToString() + " seconds";
              }
              if (str2.Length > 0)
              {
                  str2 = str2.Substring(2);
              }
              if (string.IsNullOrEmpty(str2))
              {
                  return "Calculating time remaining...";
              }
              return str2;
          }
          

          使用方法如下:

              var date1 = System.DateTime.Parse("01/01/1999");
              var date2 = System.DateTime.Parse("03/07/2012");
          
          
              var ts = date2 - date1;
          
              var timeString = TimespanToWords(ts, true);
          

          【讨论】:

            【解决方案6】:
                int[] getAge(DateTime dt)
                {
                    DateTime today = DateTime.Now;
                    int years = 0;
                    int days = 0;
                    int months = 0;
                    int[] age = new int[3];
                    while (dt.Year != today.Year || dt.Month != today.Month || dt.Day != today.Day)
                    {
                        if (dt.AddYears(1).CompareTo(today) <= 0)
                        {
                            years++;
                            dt = dt.AddYears(1);
                        }
                        else
                        {
                            if (dt.AddMonths(1).CompareTo(today) <= 0)
                            {
                                months++;
                                dt = dt.AddMonths(1);
                            }
                            else
                            {
                                if (dt.AddDays(1).CompareTo(today) <= 0)
                                {
                                    days++;
                                    dt = dt.AddDays(1);
                                }
                                else
                                {
                                    dt = today;
                                }
                            }
            
                        }
                    }
                    age[0] = years;
                    age[1] = months;
                    age[2] = days;
                    return age;
                }
            

            【讨论】:

            • 您好,欢迎来到 Stack Overflow。您可以通过说明它的作用、它如何解决问题以及列出与此处发布的其他解决方案相比的好处来改进您的答案。要改进它,您可以单击答案下方的编辑按钮。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-09-25
            • 1970-01-01
            • 2011-03-02
            相关资源
            最近更新 更多