【问题标题】:calculating the difference in months between two dates计算两个日期之间的月差
【发布时间】:2010-12-04 07:15:57
【问题描述】:

在 C#/.NET 中,TimeSpanTotalDaysTotalMinutes 等,但我无法计算出总月差的公式。每月变化的天数和闰年不断让我失望。如何获得 TotalMonths

编辑抱歉没有更清楚:我知道我实际上无法从TimeSpan 得到这个,但我认为使用TotalDaysTotalMinutes 将是一个很好的例子来表达什么我一直在寻找......除了我试图获得 Total Months。

示例:2009 年 12 月 25 日 - 2009 年 10 月 6 日 = 2 个总月。 10 月 6 日至 11 月 5 日等于 0 个月。 11月6日,1个月。 12月6日,2个月

【问题讨论】:

  • 您对 2009 年 12 月 25 日至 2009 年 10 月 6 日有何期待?
  • 如何定义 TimeSpan 以月为单位?
  • @Aliostad - 如果没有日期,您可以将一个月定义为 30 天并且非常准确。
  • 由于某种原因,它被一个mod与这个问题合并了。
  • 实际上,你需要在这里阅读我的帖子,它回答了这个问题并提供了一个编码解决方案,stackoverflow.com/questions/1916358/… 忽略巨魔(brianary)并注意我通过 cmets 与 supercat 的对话。在我们称为“孤立月”的时间跨度开始和结束时的月份,问题归结为如何用天数来定义这些孤立的月份——一旦你确定了(以及你想如何定义它) ),其余的只是代码(包括在内)。我的定义。基于我认为用户的期望

标签: c# .net datetime timespan


【解决方案1】:

接受的答案非常不正确:

对于这些日期:ldate = 2020-08-30 和 rdate = 2020-08-01,我们有一个月,但接受的答案返回 0。

对于这些日期:ldate = 2020-08-30 和 rdate = 2020-10-01,我们有三个月的时间,但接受的答案返回 -2。

这是计算两个日期之间月数的正确方法(可能不是唯一方法,但正确):

  • 您不必检查哪个日期比其他日期晚。

  • 从一个月的第一天到最后一天的间隔算作一个月。

      public static int GetMontsBetween(DateTime date1, DateTime date2)
      {
          int monthCount = 0;
          int direction = date1 < date2 ? 1 : -1;
          date2 = date2.AddDays(direction);
    
          while (date1.Year != date2.Year || date1.Month != date2.Month)
          {
              date1 = date1.AddMonths(direction);
              monthCount++;
          }
    
          return monthCount;
      } 
    

【讨论】:

    【解决方案2】:

    我们是这样做的:

    int DifferenceInMonth(DateTime startDate, DateTime endDate) {
        long ToTicks(DateTime date) => new DateTimeOffset(date).Ticks;
        var daysPerMonth = 30.4;
        return (int)Math.Round((ToTicks(endDate) - ToTicks(startDate)) / TimeSpan.TicksPerDay / daysPerMonth);
    }
    

    【讨论】:

      【解决方案3】:

      结合上面两个答案,另一种扩展方法是:

      public static int ElapsedMonths(this DateTime date1, DateTime date2)
      {
          DateTime earlierDate = (date1 > date2) ? date2 : date1;
          DateTime laterDate = (date1 > date2) ? date1 : date2;
          var eMonths = (laterDate.Month - earlierDate.Month) + 12 * (laterDate.Year - earlierDate.Year) - 
                                                  ((earlierDate.Day > laterDate.Day) ? 1 : 0);
          return eMonths;
      }
      
      

      感谢@AdamRobinson 和@MarkWhittaker

      【讨论】:

        【解决方案4】:

        我对@9​​87654321@ 的看法也使用extension method,但它可以返回正面或负面的结果。

        public static int MonthsBefore(this DateTime dt1, DateTime dt2)
        {
            (DateTime early, DateTime late, bool dt2After) = dt2 > dt1 ? (dt1,dt2,true) : (dt2,dt1,false);
            DateTime tmp; // Save the result so we don't repeat work
            int months = 1;
            while ((tmp = early.AddMonths(1)) <= late)
            {
                early = tmp;
                months++;
            }
            return (months-1)*(dt2After ? 1 : -1);
        }
        

        几个测试:

        // Just under 1 month's diff
        Assert.AreEqual(0, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 1, 31)));
        // Just over 1 month's diff
        Assert.AreEqual(1, new DateTime(2014, 1, 1).MonthsBefore(new DateTime(2014, 2, 2)));    
        // Past date returns NEGATIVE
        Assert.AreEqual(-6, new DateTime(2012, 1, 1).MonthsBefore(new DateTime(2011, 6, 10)));
        

        【讨论】:

          【解决方案5】:
          case IntervalType.Month:
              returnValue = start.AddMonths(-end.Month).Month.ToString();
              break;
          case IntervalType.Year:
              returnValue = (start.Year - end.Year).ToString();
              break;
          

          【讨论】:

          • 附上代码的描述也会对其他读者有益。
          • 是的,请添加一些评论。
          【解决方案6】:

          当您想要整整几个月时,接受的答案非常有效。

          我需要半个月。这是我几个月来想出的解决方案:

              /// <summary>
              /// Calculate the difference in months.
              /// This will round up to count partial months.
              /// </summary>
              /// <param name="lValue"></param>
              /// <param name="rValue"></param>
              /// <returns></returns>
              public static int MonthDifference(DateTime lValue, DateTime rValue)
              {
                  var yearDifferenceInMonths = (lValue.Year - rValue.Year) * 12;
                  var monthDifference = lValue.Month - rValue.Month;
          
                  return yearDifferenceInMonths + monthDifference + 
                      (lValue.Day > rValue.Day
                          ? 1 : 0); // If end day is greater than start day, add 1 to round up the partial month
              }
          

          我还需要与部分年份相同的年份差异。这是我想出的解决方案:

              /// <summary>
              /// Calculate the differences in years.
              /// This will round up to catch partial months.
              /// </summary>
              /// <param name="lValue"></param>
              /// <param name="rValue"></param>
              /// <returns></returns>
              public static int YearDifference(DateTime lValue, DateTime rValue)
              {
                  return lValue.Year - rValue.Year +
                         (lValue.Month > rValue.Month // Partial month, same year
                             ? 1
                             : ((lValue.Month = rValue.Month) 
                               && (lValue.Day > rValue.Day)) // Partial month, same year and month
                             ? 1 : 0);
              }
          

          【讨论】:

          • lValue.Month &lt; rValue.Month 时,您的YearDifference 函数中存在逻辑错误 - 我现在已经修复了,您可能需要查看...
          【解决方案7】:

          我知道的老问题,但可能会对某人有所帮助。我在上面使用了@Adam 接受的答案,但随后检查了差异是 1 还是 -1,然后检查它是否是一个完整日历月的差异。所以 21/07/55 和 20/08/55 不会是一整月,但 21/07/55 和 21/07/55 会是。

          /// <summary>
          /// Amended date of birth cannot be greater than or equal to one month either side of original date of birth.
          /// </summary>
          /// <param name="dateOfBirth">Date of birth user could have amended.</param>
          /// <param name="originalDateOfBirth">Original date of birth to compare against.</param>
          /// <returns></returns>
          public JsonResult ValidateDateOfBirth(string dateOfBirth, string originalDateOfBirth)
          {
              DateTime dob, originalDob;
              bool isValid = false;
          
              if (DateTime.TryParse(dateOfBirth, out dob) && DateTime.TryParse(originalDateOfBirth, out originalDob))
              {
                  int diff = ((dob.Month - originalDob.Month) + 12 * (dob.Year - originalDob.Year));
          
                  switch (diff)
                  {
                      case 0:
                          // We're on the same month, so ok.
                          isValid = true;
                          break;
                      case -1:
                          // The month is the previous month, so check if the date makes it a calendar month out.
                          isValid = (dob.Day > originalDob.Day);
                          break;
                      case 1:
                          // The month is the next month, so check if the date makes it a calendar month out.
                          isValid = (dob.Day < originalDob.Day);
                          break;
                      default:
                          // Either zero or greater than 1 month difference, so not ok.
                          isValid = false;
                          break;
                  }
                  if (!isValid)
                      return Json("Date of Birth cannot be greater than one month either side of the date we hold.", JsonRequestBehavior.AllowGet);
              }
              else
              {
                  return Json("Date of Birth is invalid.", JsonRequestBehavior.AllowGet);
              }
              return Json(true, JsonRequestBehavior.AllowGet);
          }
          

          【讨论】:

            【解决方案8】:

            这方面没有很多明确的答案,因为你总是在假设事情。

            此解决方案计算两个日期之间的月份,假设您要保存月份中的某天以进行比较,(意味着在计算中考虑了月份中的某天)

            例如,如果您的日期是 2012 年 1 月 30 日,则 2012 年 2 月 29 日不是一个月,而是 2013 年 3 月 1 日。

            它已经过非常彻底的测试,可能会在我们使用它后清理它,并且使用两个日期而不是时间跨度,这可能更好。希望这对其他人有帮助。

            private static int TotalMonthDifference(DateTime dtThis, DateTime dtOther)
            {
                int intReturn = 0;
                bool sameMonth = false;
            
                if (dtOther.Date < dtThis.Date) //used for an error catch in program, returns -1
                    intReturn--;
            
                int dayOfMonth = dtThis.Day; //captures the month of day for when it adds a month and doesn't have that many days
                int daysinMonth = 0; //used to caputre how many days are in the month
            
                while (dtOther.Date > dtThis.Date) //while Other date is still under the other
                {
                    dtThis = dtThis.AddMonths(1); //as we loop, we just keep adding a month for testing
                    daysinMonth = DateTime.DaysInMonth(dtThis.Year, dtThis.Month); //grabs the days in the current tested month
            
                    if (dtThis.Day != dayOfMonth) //Example 30 Jan 2013 will go to 28 Feb when a month is added, so when it goes to march it will be 28th and not 30th
                    {
                        if (daysinMonth < dayOfMonth) // uses day in month max if can't set back to day of month
                            dtThis.AddDays(daysinMonth - dtThis.Day);
                        else
                            dtThis.AddDays(dayOfMonth - dtThis.Day);
                    }
                    if (((dtOther.Year == dtThis.Year) && (dtOther.Month == dtThis.Month))) //If the loop puts it in the same month and year
                    {
                        if (dtOther.Day >= dayOfMonth) //check to see if it is the same day or later to add one to month
                            intReturn++;
                        sameMonth = true; //sets this to cancel out of the normal counting of month
                    }
                    if ((!sameMonth)&&(dtOther.Date > dtThis.Date))//so as long as it didn't reach the same month (or if i started in the same month, one month ahead, add a month)
                        intReturn++;
                }
                return intReturn; //return month
            }
            

            【讨论】:

              【解决方案9】:

              游戏迟到了,但我想这可能对某人有所帮助。大多数人倾向于逐月测量,不包括月份有不同变化的事实。使用这种思想框架,我创建了一个班轮来为我们比较日期。使用以下流程。

              1. 在比较年份时,任何超过 1 年的年份都将乘以 12,这不会小于 1 整年。
              2. 如果结束年份更大,我们需要评估当前日期是否大于或等于前一天 2A。如果结束日期大于或等于我们取当前月份,然后加上 12 个月减去开始月份的月份 2B。如果结束日期小于开始日期,我们执行与上述相同的操作,只是在减去之前将开始月份加 1
              3. 如果年末没有更大,我们的表现与 2A/2B 相同,但不添加 12 个月,因为我们不需要在全年进行评估。

                    DateTime date = new DateTime(2003, 11, 25);
                    DateTime today = new DateTime(2004, 12, 26);
                    var time = (today.Year - date.Year > 1 ? (today.Year - date.Year - 1) * 12 : 0) +  (today.Year > date.Year ? (today.Day >= date.Day ? today.Month + 12 - date.Month : today.Month + 12 - (date.Month + 1)) : (today.Day >= date.Day ? today.Month - date.Month : today.Month - (date.Month + 1)));
                

              【讨论】:

              • 三元死亡?
              【解决方案10】:

              我在DateTimeDateTimeOffset 上写了一个非常简单的扩展方法来做到这一点。我希望它像 TimeSpan 上的 TotalMonths 属性一样工作:即返回两个日期之间完整月份的计数,忽略任何部分月份。因为它基于DateTime.AddMonths(),所以它尊重不同的月份长度,并返回人类可以理解的月份。

              (不幸的是,您不能将它作为 TimeSpan 上的扩展方法来实现,因为它不会保留所使用的实际日期的知识,而且它们在几个月内很重要。)

              代码和测试都是available on GitHub。代码很简单:

              public static int GetTotalMonthsFrom(this DateTime dt1, DateTime dt2)
              {
                  DateTime earlyDate = (dt1 > dt2) ? dt2.Date : dt1.Date;
                  DateTime lateDate = (dt1 > dt2) ? dt1.Date : dt2.Date;
              
                  // Start with 1 month's difference and keep incrementing
                  // until we overshoot the late date
                  int monthsDiff = 1;
                  while (earlyDate.AddMonths(monthsDiff) <= lateDate)
                  {
                      monthsDiff++;
                  }
              
                  return monthsDiff - 1;
              }
              

              它通过了所有这些单元测试用例:

              // Simple comparison
              Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 1)));
              // Just under 1 month's diff
              Assert.AreEqual(0, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 1, 31)));
              // Just over 1 month's diff
              Assert.AreEqual(1, new DateTime(2014, 1, 1).GetTotalMonthsFrom(new DateTime(2014, 2, 2)));
              // 31 Jan to 28 Feb
              Assert.AreEqual(1, new DateTime(2014, 1, 31).GetTotalMonthsFrom(new DateTime(2014, 2, 28)));
              // Leap year 29 Feb to 29 Mar
              Assert.AreEqual(1, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2012, 3, 29)));
              // Whole year minus a day
              Assert.AreEqual(11, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2012, 12, 31)));
              // Whole year
              Assert.AreEqual(12, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2013, 1, 1)));
              // 29 Feb (leap) to 28 Feb (non-leap)
              Assert.AreEqual(12, new DateTime(2012, 2, 29).GetTotalMonthsFrom(new DateTime(2013, 2, 28)));
              // 100 years
              Assert.AreEqual(1200, new DateTime(2000, 1, 1).GetTotalMonthsFrom(new DateTime(2100, 1, 1)));
              // Same date
              Assert.AreEqual(0, new DateTime(2014, 8, 5).GetTotalMonthsFrom(new DateTime(2014, 8, 5)));
              // Past date
              Assert.AreEqual(6, new DateTime(2012, 1, 1).GetTotalMonthsFrom(new DateTime(2011, 6, 10)));
              

              【讨论】:

              • 质朴,但最好的解决方案。复制并粘贴。谢谢
              • 谢谢你,很好的解决这个工作的魅力并满足我的要求。
              【解决方案11】:

              这是我发现准确的月份差异的贡献:

              namespace System
              {
                   public static class DateTimeExtensions
                   {
                       public static Int32 DiffMonths( this DateTime start, DateTime end )
                       {
                           Int32 months = 0;
                           DateTime tmp = start;
              
                           while ( tmp < end )
                           {
                               months++;
                               tmp = tmp.AddMonths( 1 );
                           }
              
                           return months;
                      }
                  }
              }
              

              用法:

              Int32 months = DateTime.Now.DiffMonths( DateTime.Now.AddYears( 5 ) );
              

              您可以创建另一个名为 DiffYears 的方法,并在 while 循环中应用与上述完全相同的逻辑和 AddYears 而不是 AddMonths。

              【讨论】:

                【解决方案12】:

                (我知道这是一个老问题,但是...)

                这在纯 .NET 中相对很痛苦。我会推荐我自己的 Noda Time 库,它是专门为这样的事情设计的:

                LocalDate start = new LocalDate(2009, 10, 6);
                LocalDate end = new LocalDate(2009, 12, 25);
                Period period = Period.Between(start, end);
                int months = period.Months;
                

                (还有其他选项,例如,如果您只想要跨年数月,您可以使用 Period period = Period.Between(start, end, PeriodUnits.Months);

                【讨论】:

                • 我下载了你的库并复制了你上面写的代码,但我收到编译时错误。错误 1 ​​运算符“-”不能应用于“NodaTime.LocalDate”和“NodaTime.LocalDate”类型的操作数。我从 5 年就知道这篇文章了,从那时起有什么改变,导致这段代码不起作用吗?
                • @HakamFostok: 抱歉 - 当 2.0 发布时它会起作用,但在那之前你需要使用Period.Between。已编辑代码,使其适用于 NodaTime 1.3.1。
                • 非常感谢 NodaTime 库做了我想做的事。我不仅想计算两个日期之间的月份,还想计算剩余的天数,这正是 NodaTime 所做的,再次感谢。
                • @JonSkeet 你的图书馆真的是黑魔法。日期总是咬我。那段代码 sn-p 为我节省了大量时间。
                【解决方案13】:

                该方法返回一个包含 3 个元素的列表,第一个是年,第二个是月,最后一个元素是天:

                public static List<int> GetDurationInEnglish(DateTime from, DateTime to)
                    {
                        try
                        {
                            if (from > to)
                                return null;
                
                            var fY = from.Year;
                            var fM = from.Month;
                            var fD = DateTime.DaysInMonth(fY, fM);
                
                            var tY = to.Year;
                            var tM = to.Month;
                            var tD = DateTime.DaysInMonth(tY, tM);
                
                            int dY = 0;
                            int dM = 0;
                            int dD = 0;
                
                            if (fD > tD)
                            {
                                tM--;
                
                                if (tM <= 0)
                                {
                                    tY--;
                                    tM = 12;
                                    tD += DateTime.DaysInMonth(tY, tM);
                                }
                                else
                                {
                                    tD += DateTime.DaysInMonth(tY, tM);
                                }
                            }
                            dD = tD - fD;
                
                            if (fM > tM)
                            {
                                tY--;
                
                                tM += 12;
                            }
                            dM = tM - fM;
                
                            dY = tY - fY;
                
                            return new List<int>() { dY, dM, dD };
                        }
                        catch (Exception exception)
                        {
                            //todo: log exception with parameters in db
                
                            return null;
                        }
                    }
                

                【讨论】:

                  【解决方案14】:
                  DateTime start = new DateTime(2003, 12, 25);
                  DateTime end = new DateTime(2009, 10, 6);
                  int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
                  double daysInEndMonth = (end - end.AddMonths(1)).Days;
                  double months = compMonth + (start.Day - end.Day) / daysInEndMonth;
                  

                  【讨论】:

                    【解决方案15】:

                    以下实际上是您可以做到的最准确的方法,因为“1 个月”的定义会根据月份的不同而变化,而其他答案都没有考虑到这一点!如果您想了解更多关于框架中未构建的问题的信息,您可以阅读这篇文章:A Real Timespan Object With .Years & .Months (但是,阅读该文章不是理解和使用以下功能所必需的,它可以 100% 工作,没有其他人喜欢使用的近似值的固有不准确性 - 并随意将 .ReverseIt 函数替换为您的框架上可能具有的内置 .Reverse 函数(这里只是为了完整性)。

                    请注意,您可以获得任意数量的日期/时间精度、秒和分钟,或秒、分钟和天,最长可达年(其中包含 6 个部分/段)。如果您指定前两个并且它已超过一年,它将返回“1 年零 3 个月前”并且不会返回其余部分,因为您请求了两个片段。如果它只有几个小时,那么它只会返回“2 小时 1 分钟前”。当然,如果您指定 1、2、3、4、5 或 6 个段(最大为 6,因为秒、分钟、小时、天、月、年仅产生 6 种类型),则同样的规则适用。它还将纠正语法问题,例如“分钟”与“分钟”,具体取决于它是否为 1 分钟或更长,所有类型都相同,并且生成的“字符串”将始终在语法上正确。

                    以下是一些使用示例: bAllowSegments 标识要显示多少段...即:如果为 3,则返回字符串(例如)..."3 years, 2 months and 13 days"(不包括小时、分钟和秒,因为返回前 3 个时间类别) ,但是,如果日期是较新的日期,例如几天前,指定相同的段 (3) 将返回 "4 days, 1 hour and 13 minutes ago",因此它会考虑所有因素!

                    如果 bAllowSegments 为 2,它将返回 "3 years and 2 months",如果 6(最大值)将返回 "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds",但请注意,它将 NEVER RETURN 类似 "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago",因为它知道没有日期数据在前 3 个段中并忽略它们,即使您指定 6 个段,所以不要担心 :)。当然,如果有一个段为 0,它会在形成字符串时考虑到这一点,并显示为"3 days and 4 seconds ago" 并忽略“0 hours”部分!喜欢的话请尽情欣赏。

                     Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
                      ' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
                      ' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
                      ' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
                      Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
                      Dim dtNow = DateTime.Now
                      Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)
                    
                      rYears = dtNow.Year - dt.Year
                      rMonths = dtNow.Month - dt.Month
                      If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
                      rDays = dtNow.Day - dt.Day
                      If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
                      rHours = dtNow.Hour - dt.Hour
                      If rHours < 0 Then rHours += 24 : rDays -= 1
                      rMinutes = dtNow.Minute - dt.Minute
                      If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
                      rSeconds = dtNow.Second - dt.Second
                      If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1
                    
                      ' this is the display functionality
                      Dim sb As StringBuilder = New StringBuilder()
                      Dim iSegmentsAdded As Int16 = 0
                    
                      If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
                      If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
                    
                      If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
                      If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
                    
                      If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
                      If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
                    
                      If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
                      If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
                    
                      If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
                      If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
                    
                      If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1
                    
                    parseAndReturn:
                    
                      ' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
                      ' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...
                    
                      If sb.ToString = "" Then sb.Append("less than 1 second")
                    
                      Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")
                    
                     End Function
                    

                    当然,您将需要一个“ReplaceLast”函数,它接受一个源字符串,一个指定需要替换的参数,以及另一个指定要替换它的参数,它只替换最后一次出现那个字符串......如果你没有一个或不想实现它,我已经包括了我的一个,所以在这里,它可以“按原样”工作,无需修改。我知道不再需要 reverseit 函数(存在于 .net 中),但是 ReplaceLast 和 ReverseIt 函数是从 pre-.net 时代遗留下来的,所以请原谅它看起来多么过时(仍然 100% 有效,一直在使用em 使用了十多年,可以保证它们没有错误)... :)。干杯。

                    <Extension()> _ 
                    Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String 
                        ' let empty string arguments run, incase we dont know if we are sending and empty string or not. 
                        sReplacable = sReplacable.ReverseIt 
                        sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version! 
                        Return sReplacable.ReverseIt.ToString 
                    End Function 
                    
                    <Extension()> _ 
                    Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String 
                        Dim strTempX As String = "", intI As Integer 
                    
                        If n > strS.Length Or n = -1 Then n = strS.Length 
                    
                        For intI = n To 1 Step -1 
                            strTempX = strTempX + Mid(strS, intI, 1) 
                        Next intI 
                    
                        ReverseIt = strTempX + Right(strS, Len(strS) - n) 
                    
                    End Function 
                    

                    【讨论】:

                      【解决方案16】:

                      如果您要处理月份和年份,您需要知道每个月有多少天以及哪些年份是闰年。

                      输入Gregorian Calendar(以及其他特定于文化的Calendar 实现)。

                      虽然 Calendar 没有提供直接计算两个时间点之间差异的方法,但它确实有诸如

                      之类的方法
                      DateTime AddWeeks(DateTime time, int weeks)
                      DateTime AddMonths(DateTime time, int months)
                      DateTime AddYears(DateTime time, int years)
                      

                      【讨论】:

                        【解决方案17】:

                        This库计算月份的差异,考虑到DateTime的所有部分:

                        // ----------------------------------------------------------------------
                        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 );
                        
                          // differences
                          Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
                          // > DateDiff.Years: 1
                          Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
                          // > DateDiff.Quarters: 5
                          Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
                          // > DateDiff.Months: 16
                          Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
                          // > DateDiff.Weeks: 70
                          Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
                          // > DateDiff.Days: 497
                          Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
                          // > DateDiff.Weekdays: 71
                          Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
                          // > DateDiff.Hours: 11940
                          Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
                          // > DateDiff.Minutes: 716441
                          Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
                          // > DateDiff.Seconds: 42986489
                        
                          // 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
                          Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
                          // > DateDiff.ElapsedHours: 12
                          Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
                          // > DateDiff.ElapsedMinutes: 41
                          Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
                          // > DateDiff.ElapsedSeconds: 29
                        } // DateDiffSample
                        

                        【讨论】:

                          【解决方案18】:

                          idiomatic-c# 中没有内置的方法可以准确地做到这一点。有一些解决方法,例如人们已经编码的this CodeProject example

                          【讨论】:

                            【解决方案19】:

                            如果你想在28th Feb1st March 之间得到一个结果1

                            DateTime date1, date2;
                            int monthSpan = (date2.Year - date1.Year) * 12 + date2.Month - date1.Month
                            

                            【讨论】:

                            • 这似乎与 Sql Server DateDiff(month, ...) 函数使用的逻辑相同。它还具有极其简洁和易于解释和理解的优点。我将解释如下......从一个日期到另一个日期,您需要翻几页日历?
                            【解决方案20】:

                            几个月的问题在于,它并不是一个真正的简单衡量标准——它们的大小不是恒定的。您需要为要包含的内容定义规则,然后从那里开始工作。例如 1 月 1 日至 2 月 1 日 - 您可以说其中涉及 2 个月,或者您可以说是 1 个月。那么“1 Jan 20:00”到“1 Feb 00:00”呢?那还不是整整一个月。那是0吗? 1?反过来呢(1 月 1 日 00:00 至 2 月 1 日 20:00)... 1? 2?

                            先定义规则,然后你就得自己写代码了,恐怕……

                            【讨论】:

                              【解决方案21】:

                              http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html

                              如果您可以将时间从公历日期转换为Julian day number,您可以创建一个运算符来比较zulian天数,可以输入double来获取月、日、秒等。检查从上面的链接中获取从格里高利转换为儒略的算法。

                              【讨论】:

                                【解决方案22】:

                                也许您不想了解月份分数;这段代码呢?

                                public static class DateTimeExtensions { public static int TotalMonths(this DateTime start, DateTime end) { return (start.Year * 12 + start.Month) - (end.Year * 12 + end.Month); } } // Console.WriteLine( // DateTime.Now.TotalMonths( // DateTime.Now.AddMonths(-1))); // prints "1"

                                【讨论】:

                                • 我看不懂*100。应该是*12吗?
                                【解决方案23】:

                                您无法从TimeSpan 中获得该信息,因为“月”是可变的计量单位。您必须自己计算,并且必须弄清楚您希望它如何工作。

                                例如,July 5, 2009August 4, 2009 这样的日期应该产生一个月还是零个月的差异?如果你说它应该产生一个,那么July 31, 2009August 1, 2009 呢? 是一个月吗?它只是日期的Month 值的差异,还是与实际时间跨度更相关?确定所有这些规则的逻辑非常重要,因此您必须确定自己的规则并实施适当的算法。

                                如果您想要的只是月份的差异——完全忽略日期值——那么你可以使用这个:

                                public static int MonthDifference(this DateTime lValue, DateTime rValue)
                                {
                                    return (lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year);
                                }
                                

                                请注意,这会返回一个相对差异,这意味着如果rValue 大于lValue,则返回值将为负数。如果你想要一个绝对的差异,你可以使用这个:

                                public static int MonthDifference(this DateTime lValue, DateTime rValue)
                                {
                                    return Math.Abs((lValue.Month - rValue.Month) + 12 * (lValue.Year - rValue.Year));
                                }
                                

                                【讨论】:

                                • @Dinah 这只是一个近似值,如果你想知道真正的 .Month 和 .Years - 我刚刚发布了一个你可以阅读的答案。尽管就近似值而言,这是一个很好的近似值(Adam Robinson 的道具),但是您应该记住,如果您使用这些近似值中的任何一个,您只是在无意中对用户撒谎。
                                • @Erx_VB.NExT.Coder:感谢您的支持,但是虽然您的回答表明没有一个答案考虑到一个月是一个可变计量单位这一事实,但似乎大多数他们这样做;他们只是不使用您的特定近似值。举个例子,我回答中的第一句话表明它是可变的。任何答案,包括你的,是一个近似值,只是因为它不是一个精确的答案。您的“2 个月”结果对于不同的输入可能意味着不同的东西,所以它是一个近似值。
                                • 我的不是一个近似值,如果今天是 3 月 14 日,那么前两个月是根据 1 月有 31 天,2 月有 29 天这一事实计算的。现在,您是正确的,因为我的方法不是“一般”月份的定义,而您的方法是!但是,我的仅适用于您报告“此评论已发布 x 个月和 y 天 AGO”之类的内容,“AGO”部分有所不同,因为它指的是前 x 个月,需要计算前 x 个月根据这 x 个月里有多少天!链接....
                                • 这有意义吗?因此,如果您指的是特定的已知月份,那么我的方法是 100% 准确的,并且您将是一个近似值,但是,如果您指的是一般的一个月,那么近似值会是一个更好的主意,并且我的只是一个坏主意(它不是为此而设计的,使用它也没有意义)。这是我描述问题并提供解决方案的文章的链接:stackoverflow.com/questions/1916358/…
                                • 这似乎与 Sql Server DateDiff(month, ...) 函数使用的逻辑相同。它还具有极其简洁和易于解释和理解的优点。我将解释如下...从一个日期到另一个日期,您需要翻几页日历?
                                【解决方案24】:

                                您需要根据日期时间自行解决。你如何处理最后的存根日将取决于你想用它做什么。

                                一种方法是计算月份,然后在最后修正天数。比如:

                                   DateTime start = new DateTime(2003, 12, 25);
                                   DateTime end = new DateTime(2009, 10, 6);
                                   int compMonth = (end.Month + end.Year * 12) - (start.Month + start.Year * 12);
                                   double daysInEndMonth = (end - end.AddMonths(1)).Days;
                                   double months = compMonth + (start.Day - end.Day) / daysInEndMonth;
                                

                                【讨论】:

                                • 不错的代码,但有 1 个错误:改为:(如 2 月 28 日 + 1 个月 == 3 月 28 日):-) //decimal daysInEndMonth = (end - end.AddMonths(1)).Days ;我建议:十进制 daysInEndMonth = DateTime.DaysInMonth(end.Year, end.Month) * -1;
                                【解决方案25】:

                                我会这样做:

                                static int TotelMonthDifference(this DateTime dtThis, DateTime dtOther)
                                {
                                    int intReturn = 0;
                                
                                    dtThis = dtThis.Date.AddDays(-(dtThis.Day-1));
                                    dtOther = dtOther.Date.AddDays(-(dtOther.Day-1));
                                
                                    while (dtOther.Date > dtThis.Date)
                                    {
                                        intReturn++;     
                                        dtThis = dtThis.AddMonths(1);
                                    }
                                
                                    return intReturn;
                                }
                                

                                【讨论】:

                                • 这当然是一种算法,但可以大大简化为return (dtOther.Month - dtThis.Month) + 12 * (dtOther.Year - dtThis.Year);
                                • 两个问题:您从 2 个日期开始,而不是 TimeSpan。其次,您在两个月的第一天之间进行计算,这是一个非常值得怀疑的定义。虽然有时可能是对的。
                                • @Henk:是的,当然这并不总是正确的,这就是为什么我说我会这样做,而不是任何人都应该这样做。 OP没有指定如何计算结果。 @Adam:哇,我又觉得太复杂了……这对我来说太频繁了。感谢您的评论,您显然是对的,您的版本要好得多。从现在开始我将使用它。
                                • @Adam:您为什么不将其作为实际答案提交?!这是迄今为止最紧凑的。非常光滑。
                                • @Dinah:我不想假设那是你真正想要的。如果是,我已经编辑了我之前的答案以包含这种方法。
                                【解决方案26】:

                                您必须首先定义 TotalMonths 的含义。
                                一个简单的定义是一个月为 30.4 天 (365.25 / 12)。

                                除此之外,任何包含分数的定义似乎都没有用,更常见的整数值(日期之间的整月)也取决于非标准的业务规则。

                                【讨论】:

                                  【解决方案27】:

                                  如果您想要确切的数字,您不能只从时间跨度中获取,因为您需要知道您正在处理的月份,以及您是否正在处理闰年,就像您说的那样。

                                  要么选择一个大概的数字,要么对原始的 DateTimes 做一些调整

                                  【讨论】:

                                    猜你喜欢
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2011-06-06
                                    • 2011-08-03
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多