两个日期之间的月数

Posted

技术标签:

【中文标题】两个日期之间的月数【英文标题】:Number of months between two dates 【发布时间】:2011-01-01 01:01:06 【问题描述】:

是否有标准/通用方法/公式来计算 R 中?

我正在寻找类似于MathWorks months function的东西

【问题讨论】:

另见:***.com/a/19687995/59087 【参考方案1】:

我正要说这很简单,但difftime() 停在几周后。多么奇怪。

所以一个可能的答案是破解一些东西:

# turn a date into a 'monthnumber' relative to an origin
R> monnb <- function(d)  lt <- as.POSIXlt(as.Date(d, origin="1900-01-01")); \
                          lt$year*12 + lt$mon  
# compute a month difference as a difference between two monnb's
R> mondf <- function(d1, d2)  monnb(d2) - monnb(d1) 
# take it for a spin
R> mondf(as.Date("2008-01-01"), Sys.Date())
[1] 24
R> 

似乎是对的。可以将其包装成一些简单的类结构。或将其保留为 hack :)

编辑: 似乎也适用于 Mathworks 中的示例:

R> mondf("2000-05-31", "2000-06-30")
[1] 1
R> mondf(c("2002-03-31", "2002-04-30", "2002-05-31"), "2002-06-30")
[1] 3 2 1
R> 

添加EndOfMonth 标志留给读者作为练习:)

编辑 2:也许 difftime 将其省略,因为没有可靠的方法来表示与其他单位的 difftime 行为一致的分数差异。

【讨论】:

谢谢,德克。这是一个聪明的把戏。说到单位,我发现这也有效.. >as.numeric(as.Date('2009-10-1') - as.Date('2009-8-01'), units = 'weeks') > as.numeric(as.Date('2009-10-1') - as.Date('2009-8-01'), units = 'days') 但它也停在“weeks”。 我认为这只是巧妙地重载 - 运算符来调用相同的 difftime() 函数。俗话说“没有免费的午餐”:) 我发现这对于大型数据集来说非常慢。下面 Manoel Galdino 的答案要快得多。 @anotherfred 并且可能是错误的。快速示例是日期 2017-01-31 和 2018-03-01。在他的第一个答案中给出“13”(一旦我们添加了缺少的as.numeric(),在第二个答案中添加了“12”。我的答案是“14”。 @DirkEddelbuettel 抱歉,应该澄清一下 - 我的意思是他的第一种方法对于像我这样的情况很有用,我有一个大数据集但所有日期都是本月的 1 日。【参考方案2】:

我认为这是对与 MathWorks 函数奇偶校验问题的更接近的答案

MathWorks 月份函数

MyMonths = months(StartDate, EndDate, EndMonthFlag)

我的 R 代码

library(lubridate)
interval(mdy(10012015), today()) %/% months(1)

输出(与 2016 年 4 月运行代码时一样)

[1] 6

Lubridate [package] 提供的工具可以更轻松地解析和操作日期。这些工具按共同目的分组如下。有关每个函数的更多信息,请参见其帮助文档。

interval lubridate 创建具有指定开始和结束日期的 Interval 类对象。如果开始日期早于结束日期,则间隔将为正数。否则为负数

今天 lubridate 当前日期

months Base 提取月份 这些是通用函数:内部日期时间类的方法记录在这里。

%/% base 表示整数除法 AKA ( x %/% y )(最多舍入误差)

【讨论】:

这对我有用,而其他一些没有。 这对我也有效,而其他人则没有。我不得不将我的结果与 SPSS 中的 datediff 函数进行比较。这与这些结果相符。【参考方案3】:

一个简单的函数...

elapsed_months <- function(end_date, start_date) 
    ed <- as.POSIXlt(end_date)
    sd <- as.POSIXlt(start_date)
    12 * (ed$year - sd$year) + (ed$mon - sd$mon)

示例...

>Sys.time()
[1] "2014-10-29 15:45:44 CDT"
>elapsed_months(Sys.time(), as.Date("2012-07-15"))
[1] 27
>elapsed_months("2002-06-30", c("2002-03-31", "2002-04-30", "2002-05-31"))
[1] 3 2 1

对我来说,将这个问题简单地考虑为两个日期相减是有意义的,并且由于minuend − subtrahend = difference (wikipedia),我将较晚的日期放在参数列表的首位。

请注意,尽管这些日期的内部表示年份为负数,但由于减去负数的规则,它对于 1900 年之前的日期工作正常...

> elapsed_months("1791-01-10", "1776-07-01")
[1] 174

【讨论】:

【参考方案4】:

可能有更简单的方法。它不是一个函数,但它只有一行。

length(seq(from=date1, to=date2, by='month')) - 1

例如

> length(seq(from=Sys.Date(), to=as.Date("2020-12-31"), by='month')) - 1

生产:

[1] 69

这会计算两个日期之间的整月数。如果您想包含当前月份/不是整月的剩余部分,请删除 -1。

【讨论】:

这并不总是给出预期的结果。 length(seq(from=as.Date("2015-01-31"), to=as.Date("2015-03-31"), by='month')) = 3 还有,length(seq(from=as.Date("2015-01-31"), to=as.Date("2015-04-30"), by='month')) = 3 上面的代码现在返回一个错误,因为 Sys.Date()>"2020-12-31" 现在。如果您更改 'from' 和 'to' 参数,它仍然有效。【参考方案5】:

在 R-Help 邮件列表中有一条和你一样的消息(之前我提到了一个 CRAN 列表)。

这里the link。有两种建议的解决方案:

每月平均有 365.25/12 天,因此以下表达式给出了 d1 和 d2 之间的月数:
#test data 
d1 <- as.Date("01 March 1950", "%d %B %Y")    
d2 <- as.Date(c("01 April 1955", "01 July 1980"), "%d %B %Y")
# calculation 
round((d2 - d1)/(365.25/12))
另一种可能性是像这样获取seq.Dates 的长度:
as.Date.numeric <- function(x) structure(floor(x+.001), class = "Date")
sapply(d2, function(d2) length(seq(d1, as.Date(d2), by = "month")))-1

【讨论】:

假设所有日期之间都有整数个月,但很有用。【参考方案6】:
library(lubridate)

case1:朴素函数

mos<-function (begin, end) 
      mos1<-as.period(interval(ymd(begin),ymd(end)))
      mos<-mos1@year*12+mos1@month
      mos

案例2:如果您只需要考虑“月”而不考虑“日”

mob<-function (begin, end) 
      begin<-paste(substr(begin,1,6),"01",sep="")
      end<-paste(substr(end,1,6),"01",sep="")
      mob1<-as.period(interval(ymd(begin),ymd(end)))
      mob<-mob1@year*12+mob1@month
      mob

例子:

mos(20150101,20150228) # 1
mos(20150131,20150228) # 0
# you can use "20150101" instead of 20150101

mob(20150131,20150228) # 1
mob(20150131,20150228) # 1
# you can use a format of "20150101", 20150101, 201501

【讨论】:

interval(mdy(20150101), mdy(20150228)) %/% months(1) 使用lubridate的原生函数interval即可。 谢谢你。但是,在我的业务中,我需要经常计算 2 天的“月数”。例如,20150131 和 20150201 之间的月份必须为 1。所以,我曾经制作“mob”用户定义函数。你知道怎么用 lubridate 来做这个吗? 我认为您可以使用月份(ymd)-月份(ymd)。或者您可以将年、月和日设为三列。 可以,但是如果是不同的年份,就不行了。例如,月(ymd(20170101)) - 月(ymd(20161231)) = -11 不是 1【参考方案7】:
library(lubridate)
date1 = "1 April 1977"
date2 = "7 July 2017"

date1 = dmy(date1)
date2 = dmy(date2)
number_of_months = (year(date1) - year(date2)) * 12 + month(date1) - month(date2)

月差 = 12 * 年差 + 月差。

以下可能需要使用ifelse 条件进行更正 月份减法

【讨论】:

可能值得注意的是,这会导致负数 - 所以要么更改 date1/date2 要么使用 abs(etc) 。我认为这是获得几个月的guestimate 的一个非常简单直接的解决方案【参考方案8】:

对我来说这是有效的:

library(lubridate)

Pagos$Datediff <- (interval((Pagos$Inicio_FechaAlta), (Pagos$Inicio_CobFecha)) %/% months(1))

输出是两个日期之间的月数,并存储在 Pagos 数据框的列中。

【讨论】:

感谢您的贡献,但这并没有给出任何小数位。有什么建议吗? 恐怕这只给你几个月的数量。但不能用逗号。我的观点是,应该以天数计算,而不是那样。对于您的情况,我可能会更准确。【参考方案9】:

您可以使用 zoo 包中的 as.yearmon() 函数。此函数将日期类型变量转换为年月类型变量。您可以减去两个年月类型变量,然后乘以 12 以获得月份的差异,如下所示:

12 * (as.yearmon(Date1) - as.yearmon(Date2))

【讨论】:

通常不鼓励仅使用代码回答。您能否解释一下为什么它有效或为什么它比其他解决方案更好? @Jason 刚刚做了! 这可行,但as.yearmon()zoo 包的一部分,而不是R 基础的一部分。如果你安装了 zoo:12 * (zoo::as.yearmon(Date1) - zoo::as.yearmon(Date2))【参考方案10】:

以月为单位的日期差异

$date1 = '2017-01-20';
$date2 = '2019-01-20';

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $joining_months = (($year2 - $year1) * 12) + ($month2 - $month1);

【讨论】:

【参考方案11】:

另一种简短而方便的方法是:

day1 <- as.Date('1991/04/12')
day2 <- as.Date('2019/06/10')
round(as.numeric(day2 - day1)/30.42)

[1] 338

【讨论】:

以上是关于两个日期之间的月数的主要内容,如果未能解决你的问题,请参考以下文章

r 中每年两个日期之间的月数

计算两个日期之间的月数

计算SQL中两个日期之间的月数[重复]

如何计算定义自定义财务日历的两个日期之间的月数?

如何计算两个日期之间的月数和天数,而不在 SQL Server 中返回负值

查询两个日期相差的月数和剩下的天数