计算两个日期之间的月份差异
在 C#/.NET 中,TimeSpan
有 TotalDays
、TotalMinutes
等,但我无法找出总月差的公式。每月不同的日子和闰年总是让我感到困惑。如何获取TotalMonths
?
编辑抱歉没有说得更清楚:我知道我实际上无法从TimeSpan
得到这个,但我想使用TotalDays
和TotalMinutes
将是一个很好的例子来表达我正在寻找的内容......除了我试图获取总月数。
示例:2009 年 12 月 25 日 - 2009 年 10 月 6 日 = 总共 2 个月。 10 月 6 日至 11 月 5 日等于 0 个月。 11月6日,1个月。 2个月后12月6日
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(27)
以下是我对获取月份差异所做的贡献,我发现它是准确的:
用法:
您可以创建另一个名为 DiffYears 的方法,并应用与上面完全相同的逻辑,并在 while 循环中使用 AddYears 而不是 AddMonths。
Here is my contribution to get difference in Months that I've found to be accurate:
Usage:
You can create another method called DiffYears and apply exactly the same logic as above and AddYears instead of AddMonths in the while loop.
游戏已经晚了,但我想这可能对某人有帮助。大多数人倾向于按日期来衡量每月的情况,排除了月份有不同变化的事实。利用这一思想框架,我创建了一个单行表来为我们比较日期。使用以下过程。
2A。如果结束日期大于或等于当前月份,然后加上 12 个月减去起始月份的月份
2B。如果结束日期小于开始日期,我们执行与上面相同的操作,只是在减去之前在开始月份中加 1
如果结束年份不大于我们执行与 2A/2B 相同的操作,但不添加12 个月,因为我们不需要全年评估。
Way late to the game but I imagine this may be helpful to someone. The majority of people tend to measure month to month by date excluding the fact that months come in different variations. Using that frame of thought I created a one liner which compares the dates for us. Using the the following process.
2A. If the end day is greater or equal we take the current month and then add 12 months subtract the month of the start month
2B. If the end day is less than the start day we perform the the same as above except we add 1 to the start month before subtracting
If the end year is not greater we perform the same as 2A/2B, but without adding the 12 months because we do not need to evaluate around the year.
我对这个答案的看法也使用了扩展方法,但它可以返回正值或负值结果。
一些测试:
My take on this answer also uses an extension method, but it can return a positive or negative result.
A couple tests:
结合上面的两个答案,另一种扩展方法是:
感谢@AdamRobinson和@MarkWhittaker
Combing two of the answers above, another extension method is:
Thanks to @AdamRobinson and @MarkWhittaker
我们是这样做的:
Here's how we did it:
接受的答案非常不正确:
对于这些日期:ldate = 2020-08-30 和 rdate = 2020-08-01,我们有一个月,但接受的答案返回 0。
对于这些日期:ldate = 2020-08-30并且 rdate = 2020-10-01,我们有三个月,但接受的答案返回 -2。
这是计算两个日期之间的月份数的正确方法(也许不是唯一的方法,但正确):
您不必检查哪个日期比其他日期晚。
从每月的第一天到最后一天的间隔算作一个月。
The accepted answer is strongly incorrect:
For these dates: ldate = 2020-08-30 and rdate = 2020-08-01, we have one month, but the accepted answer returns 0.
For these dates: ldate = 2020-08-30 and rdate = 2020-10-01, we have three months, but the accepted answer returns -2.
Here is the correct method (maybe not the unique method, but correct) to calculate the quantity of months between two dates:
You don't have to check which date is lower than other.
An interval from the first day to the last day of month is counted as a month.
您无法从
TimeSpan
中获取该值,因为“月”是可变的计量单位。你必须自己计算它,并且必须弄清楚你希望它到底如何工作。例如,
2009 年 7 月 5 日
和2009 年 8 月 4 日
这样的日期应该产生一个月还是零个月的差异?如果您说它应该产生一个,那么2009 年 7 月 31 日
和2009 年 8 月 1 日
又如何呢? 那是一个月吗?这只是日期的Month
值的差异,还是与实际的时间跨度更相关?确定所有这些规则的逻辑并不简单,因此您必须确定自己的规则并实现适当的算法。如果您想要的只是月份的差异 - 完全忽略日期值 - 那么您可以使用这个:
请注意,这会返回相对差异,这意味着如果
rValue
大于lValue
,则返回值为负数。如果你想要绝对差异,你可以使用这个:You won't be able to get that from a
TimeSpan
, because a "month" is a variable unit of measure. You'll have to calculate it yourself, and you'll have to figure out how exactly you want it to work.For example, should dates like
July 5, 2009
andAugust 4, 2009
yield one month or zero months difference? If you say it should yield one, then what aboutJuly 31, 2009
andAugust 1, 2009
? Is that a month? Is it simply the difference of theMonth
values for the dates, or is it more related to an actual span of time? The logic for determining all of these rules is non-trivial, so you'll have to determine your own and implement the appropriate algorithm.If all you want is simply a difference in the months--completely disregarding the date values--then you can use this:
Note that this returns a relative difference, meaning that if
rValue
is greater thanlValue
, then the return value will be negative. If you want an absolute difference, you can use this:(我意识到这是一个老问题,但是...)
在纯 .NET 中执行此操作相对痛苦。我推荐我自己的 Noda Time 库,它是专门为这样的事情而设计的:(
还有其他选项,例如,如果您只想要一个计数月份甚至跨年,您可以使用
Period period = period.Between(start, end, periodUnits.Months);
)(I realize this is an old question, but...)
This is relatively painful to do in pure .NET. I'd recommend my own Noda Time library, which is particularly designed for things like this:
(There are other options, e.g. if you only want a count of months even across years, you'd use
Period period = Period.Between(start, end, PeriodUnits.Months);
)也许您不想了解月份分数;这段代码怎么样?
Maybe you don't want to know about month fractions; What about this code?
我在
DateTime
和DateTimeOffset
上编写了一个非常简单的扩展方法来执行此操作。我希望它的工作方式与 TotalMonths 属性完全相同>TimeSpan
可以工作:即返回两个日期之间完整月份的计数,忽略任何部分月份。因为它基于DateTime .AddMonths()
它尊重不同的月份长度并返回人类所理解的月份。(不幸的是,您无法将其实现为 TimeSpan 的扩展方法,因为这不会保留实际使用日期的知识,而且对于几个月来说它们很重要。)
代码和测试都是 可在 GitHub 上获取。代码非常简单:
并且它通过了所有这些单元测试用例:
I've written a very simple extension method on
DateTime
andDateTimeOffset
to do this. I wanted it to work exactly like aTotalMonths
property onTimeSpan
would work: i.e. return the count of complete months between two dates, ignoring any partial months. Because it's based onDateTime.AddMonths()
it respects different month lengths and returns what a human would understand as a period of months.(Unfortunately you can't implement it as an extension method on TimeSpan because that doesn't retain knowledge of the actual dates used, and for months they're important.)
The code and tests are both available on GitHub. The code is very simple:
And it passes all these unit test cases:
首先,您必须定义 TotalMonths 的含义。
一个简单的定义是一个月为 30.4 天 (365.25 / 12)。
除此之外,任何包括分数的定义似乎都没用,而且更常见的整数值(日期之间的整月)也取决于非标准业务规则。
You will have to define what you mean by TotalMonths to start with.
A simple definition puts a month at 30.4 days (365.25 / 12).
Beyond that, any definition including fractions seems useless, and the more common integer value (whole months between dates) also depends on non-standard business rules.
您需要根据日期时间自己计算出来。最后如何处理存根日将取决于您想用它做什么。
一种方法是计算月份,然后在最后修正天数。像这样的东西:
You need to work it out yourself off the datetimes. How you deal with the stub days at the end will depend on what you want to use it for.
One method would be to count month and then correct for days at the end. Something like:
对此没有太多明确的答案,因为你总是在假设事情。
此解决方案计算两个日期之间的月份,假设您要保存月份中的某一天进行比较(这意味着计算中会考虑该月中的某一天)
例如,如果您的日期为 2012 年 1 月 30 日和 2012 年 2 月 29 日不会是一个月,但 2013 年 3 月 1 日会。
它已经过非常彻底的测试,可能会在我们使用它之后清理它,并接受两个日期而不是时间跨度,这可能更好。希望这对其他人有帮助。
There are not a lot of clear answers on this because you are always assuming things.
This solution calculates between two dates the months between assuming you want to save the day of month for comparison, (meaning that the day of the month is considered in the calculation)
Example, if you have a date of 30 Jan 2012, 29 Feb 2012 will not be a month but 01 March 2013 will.
It's been tested pretty thoroughly, probably will clean it up later as we use it, and takes in two dates instead of a Timespan, which is probably better. Hope this helps out anyone else.
我会这样做:
I would do it like this:
当您想要完整的月份时,接受的答案非常有效。
我需要部分月份。这是我针对部分月份提出的解决方案:
我还需要一年的差异,部分年份的需求也相同。这是我想出的解决方案:
The accepted answer works perfectly when you want full months.
I needed partial months. This is the solution I came up with for partial months:
I also needed a year difference with the same need for partial years. Here is the solution I came up with:
我知道的老问题,但可能对某人有帮助。我在上面使用了 @Adam 接受的答案,但随后检查差异是否为 1 或 -1,然后检查是否是完整日历月的差异。因此 21/07/55 和 20/08/55 不会是一个完整的月份,但 21/07/55 和 21/07/55 会是。
Old question I know, but might help someone. I've used @Adam accepted answer above, but then checked if the difference is 1 or -1 then check to see if it is a full calendar month's difference. So 21/07/55 and 20/08/55 would not be a full month, but 21/07/55 and 21/07/55 would be.
月份的问题在于它并不是一个简单的衡量标准——它们的大小不是恒定的。您需要为要包含的内容定义规则,并从那里开始工作。例如,1 月 1 日到 2 月 1 日 - 您可以说涉及 2 个月,也可以说是 1 个月。那么“1 月 1 日 20:00”到“2 月 1 日 00:00”又如何——这并不是一个完整的月份。那是0吗? 1?反过来呢(1月1日00:00到2月1日20:00)...1? 2?
首先定义规则,然后你必须自己编写代码,恐怕......
The problem with months is that it isn't really a simple measure - they aren't constant size. You would need to define your rules for what you want to include, and work from there. For example 1 Jan to 1 Feb - you could argue 2 months are involved there, or you could say that is one month. Then what about "1 Jan 20:00" to "1 Feb 00:00" - that isn't quite an entire full month. Is that 0? 1? what about the other way around (1 Jan 00:00 to 1 Feb 20:00)... 1? 2?
First define the rules, then you'll have to code it yourself, I'm afraid...
如果您希望在
2 月 28 日
和3 月 1 日
之间获得结果1
:If you want to have a result
1
between28th Feb
and1st March
:此库考虑 DateTime 的所有部分来计算月份差异:
This library calculates the difference of months, considering all parts of DateTime:
下面实际上是您可以做到的最准确的方法,因为“1个月”的定义根据它是哪个月份而变化,并且其他答案都没有考虑到这一点!如果您想了解有关框架中未内置问题的更多信息,您可以阅读这篇文章:具有 .Years 和 .Years 的真实时间跨度对象.Months (但是,阅读该文章并不是理解和使用下面的函数所必需的,它 100% 有效,没有其他人喜欢使用的近似值固有的不准确性 - 并且可以随意替换 .ReverseIt 函数使用您的框架上可能有的内置 .Reverse 函数(这里只是为了完整性),
请注意,您可以获得任意数量的日期/时间精度、秒和分钟,或秒、分钟和天,无论何时。到年(其中包含 6 个部分/段)。如果您指定前两个并且它已存在一年以上,则它将返回“1 年零 3 个月前”,并且不会返回其余部分,因为您已经请求了两个段。如果它只有几个小时,那么它只会返回“2 小时 1 分钟前”当然,如果您指定 1、2、3、4、5 或 6 个段(最大为 6,因为秒),则适用相同的规则。 、分钟、小时、天、月、年只有 6 种类型)它还会纠正语法问题,例如“分钟”与“分钟”,具体取决于它是否为 1 分钟或更长,所有类型都相同,以及生成的“字符串”。语法上总是正确的。
以下是一些使用示例:
bAllowSegments 标识要显示的段数...即:如果 3,则返回字符串将是(作为示例)...
“3 年,2 个月和 13 天”
(不包括小时、分钟和秒作为返回的前 3 个时间类别),但是,如果日期是较新的日期,例如几天前,则指定相同的段 (3) 将返回“4 天,1小时 13 分钟前”
相反,所以它考虑了一切!如果 bAllowSegments 为 2,则返回“3 年零 2 个月”,如果为 6(最大值)则返回“3 年、2 个月、13 天、13 小时、29 分钟和 9”秒”,但是,请注意,它
永远不会返回
这样的内容“0年,0个月,0天,3小时,2分13秒前”< /code> 因为它知道前 3 个段中没有日期数据并忽略它们,即使您指定了 6 个段,所以不用担心:)。当然,如果有一个段中包含 0,则在形成字符串时会考虑到这一点,并显示为
“3 天 4 秒前”
并忽略“0 小时”部分!喜欢的话请尽情享受并发表评论。当然,您将需要一个“ReplaceLast”函数,它接受一个源字符串、一个指定需要替换的内容的参数,以及另一个指定您想要替换它的内容的参数,并且它仅替换该字符串的最后一次出现...如果您没有或不想实现它,我已经包含了我的一个,所以就在这里,它将“按原样”工作,无需修改。我知道不再需要reverseit函数(存在于.net中),但是ReplaceLast和ReverseIt函数是从.net之前的时代遗留下来的,所以请原谅它看起来有多过时(仍然可以100%工作,一直在使用他们已经使用了十多年,可以保证它们没有错误)...:)。干杯。
Below is actually the most accurate way you can do it, since the definition of "1 Month" changes depending on which month it is, and non of the other answers take this into account! If you want more information about the issue which is not built into the framework, you can read this post: A Real Timespan Object With .Years & .Months (however, reading that post isn't necessary to understand and use the function below, it works 100%, without the inherent inaccuracies of the approximation others love to use - and feel free to replace the .ReverseIt function with the built-in .Reverse function you may have on your framework (it's just here for completeness).
Please note that you can get any number of dates/times accuracy, seconds & minutes, or seconds, minutes and days, anywhere up to years (which would contain 6 parts/segments). If you specify top two and it's over a year old, it will return "1 year and 3 months ago" and won't return the rest because you've requested two segments. if it's only a few hours old, then it will only return "2 hours and 1 minute ago". Of course, same rules apply if you specify 1, 2, 3, 4, 5 or 6 segmets (maxes out at 6 because seconds, minutes, hours, days, months, years only make 6 types). It will also correct grammer issues like "minutes" vs "minute" depending on if it's 1 minute or more, same for all types, and the "string" generated will always be grammatically correct.
Here are some examples for use:
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"
(won't include hours, minutes and seconds as the top 3 time categories are returned), if however, the date was a newer date, such as something a few days ago, specifying the same segments (3) will return"4 days, 1 hour and 13 minutes ago"
instead, so it takes everything into account!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"
, but, be reminded that it willNEVER RETURN
something like this"0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
as it understands there is no date data in the top 3 segments and ignores them, even if you specify 6 segments, so don't worry :). Of course, if there is a segment with 0 in it, it will take that into account when forming the string, and will display as"3 days and 4 seconds ago"
and ignoring the "0 hours" part! Enjoy and please comment if you like.Of course, you will need a "ReplaceLast" function, which takes a source string, and an argument specifying what needs to be replaced, and another arg specifying what you want to replace it with, and it only replaces the last occurance of that string... i've included my one if you don't have one or dont want to implement it, so here it is, it will work "as is" with no modification needed. I know the reverseit function is no longer needed (exists in .net) but the ReplaceLast and the ReverseIt func are carried over from the pre-.net days, so please excuse how dated it may look (still works 100% tho, been using em for over ten years, can guarante they are bug free)... :). cheers.
如果您想要确切的数字,则不能仅从时间跨度中获得,因为您需要知道您正在处理哪些月份,以及是否正在处理闰年,就像您所说的那样。
要么获取一个大概的数字,要么对原始日期时间进行一些调整
If you want the exact number, you can't from just the Timespan, since you need to know which months you're dealing, and whether you're dealing with a leap year, like you said.
Either go for an approximate number, or do some fidgetting with the original DateTimes
http://www.astro.uu.nl/~ strous/AA/en/reken/juliaansedag.html
如果您可以将公历日期转换为儒略日数,您只需创建一个运算符来比较 zulian 日数,可以输入 double 来获取月、日、秒等。查看上面的链接以获取算法从格里高利转换为儒略。
http://www.astro.uu.nl/~strous/AA/en/reken/juliaansedag.html
If you can get the time converted from a Gregorian Date into Julian day number, you can just create an operator to do comparisons of the zulian day number, which can be type double to get months, days, seconds, etc. Check out the above link for an algorithm for converting from Gregorian to Julian.
在惯用的 C# 中没有内置方法可以准确地执行此操作。有一些解决方法,例如此 CodeProject 示例人们已经编码了。
There is no built in way to do this accurately in idiomatic-c#. There are some workarounds, such as this CodeProject example that people have coded though.
如果您处理的是月份和年份,您需要知道每个月有多少天以及哪些年份是闰年。
输入公历(以及其他特定于文化的< a href="http://msdn.microsoft.com/en-us/library/system.globalization.calendar.aspx" rel="nofollow">日历 实现)。
虽然 Calendar 没有提供直接计算两个时间点之间差异的方法,但它确实提供了诸如
If you're dealing with months and years you need something that knows how many days each month has and which years are leap years.
Enter the Gregorian Calendar (and other culture-specific Calendar implementations).
While Calendar doesn't provide methods to directly calculate the difference between two points in time, it does have methods such as
该方法返回一个包含 3 个元素的列表,第一个元素是年份,第二个元素是月份,最后一个元素是日期:
The method returns a list that contains 3 element first is year, second is month and end element is day: