每月日期的顺序,确保它是在同一天,或在无效的情况下的最后一天

时间:2016-04-07 14:24:00

标签: r seq

鉴于初始日期,我希望生成一系列具有月间隔的日期,确保每个元素与初始日期或月份的最后一天具有相同的日期,以防同一天产生无效日期。

听起来非常标准,对吧?

无法使用difftime。以下是difftime的帮助文件所说的内容:

  

"月"等单位是不可能的,因为它们不是恒定的   长度。要创建月,季度或年的间隔,请使用seq.Date   或seq.POSIXt。

然后查看seq.POSIXt的帮助文件,我发现:

  

使用"月"首先提前一个月而不改变一天:如果   这导致一个月的无效日期,它向前计数   进入下个月:见例子。

这是帮助文件中的示例。

seq(ISOdate(2000,1,31), by = "month", length.out = 4)
> seq(ISOdate(2000,1,31), by = "month", length.out = 4)
[1] "2000-01-31 12:00:00 GMT" "2000-03-02 12:00:00 GMT" 
"2000-03-31 12:00:00 GMT" "2000-05-01 12:00:00 GMT"

因此,鉴于初始日期是在第31天,这将在2月,4月等产生无效日期。因此,序列最终实际上跳过这些月份,因为它"向前计数"结束于3月02日,而不是2月29日。

如果我从2000-01-31开始,我希望序列如下:

  • 2000-01-31
  • 2000-02- 29
  • 2000年3月31日
  • 2000-04-30
  • ...

它应该正确处理闰年,所以如果初始日期是2015-01-31,那么序列应该是:

  • 2015年1月31日
  • 2015-02- 28
  • 2015年3月31日
  • 2015年4月30日
  • ...

这些只是用来说明问题的例子,我不提前知道初始日期,也不能假设它。初始日期可能在月中(2015-01-15),在这种情况下seq可以正常工作。但是,正如在示例中那样,在月末使用seq单独使用会产生问题(第29,30和31天)。我不能假设初始日期是该月的最后一天。

我环顾四周试图寻找解决方案。在SO中的一些问题(例如这里)中有一个"技巧"通过获取下个月的第一天并简单地减去1来获得一个月的最后一天。并且找到第一天是“#34; easy"因为它只是第1天。

所以到目前为止我的解决方案是:

# Given an initial date for my sequence
initial_date <- as.Date("2015-01-31")

# Find the first day of the month
library(magrittr) # to use pipes and make the code more readable
firs_day_of_month <- initial_date %>% 
    format("%Y-%m") %>% 
    paste0("-01") %>% 
    as.Date()

# Generate a sequence from initial date, using seq  
# This is the sequence that will have incorrect values in months that would
# have invalid dates
given_dat_seq <- seq(initial_date, by = "month", length.out = 4)

# And then generate an auxiliary sequence for the last day of the month
# I do this generating a sequence that starts the first day of the 
# same month as initial date and it goes one month further 
# (lenght 5 instead of 4) and substract 1 to all the elements
last_day_seq <- seq(firs_day_of_month, by = "month", length.out = 5)-1

# And finally, for each pair of elements, I take the min date of both
pmin(given_dat_seq, last_day_seq[2:5])

它有效,但同时又有点愚蠢,愚蠢和错综复杂。所以我不喜欢它。最重要的是,我无法相信在R中没有更简单的方法可以做到这一点。

有人可以指点我一个更简单的解决方案吗? (我想它应该像seq(initial_date, "month", 4)一样简单,但显然不是这样)。我搜索了它并在SO和R邮件列表中查看了这里,但除了我上面提到的技巧之外,我无法找到解决方案。

2 个答案:

答案 0 :(得分:6)

最简单的解决方案是来自lubridate的%m+%,它解决了这个问题。所以:

seq_monthly <- function(from,length.out) {
  return(from %m+% months(c(0:(length.out-1))))
}

输出:

> seq_monthly(as.Date("2015-01-31"),length.out=4)
[1] "2015-01-31" "2015-02-28" "2015-03-31" "2015-04-30"

答案 1 :(得分:3)

类似于lubridate答案,这里有一个使用RcppBDT(它包装来自C ++的Boost Date.Time库)

R> dt <- new(bdtDt, 2010, 1, 31); for (i in 1:5) { dt$addMonths(i); print(dt) }
[1] "2010-02-28"
[1] "2010-04-30"
[1] "2010-07-31"
[1] "2010-11-30"
[1] "2011-04-30"
R> dt <- new(bdtDt, 2000, 1, 31); for (i in 1:5) { dt$addMonths(i); print(dt) }
[1] "2000-02-29"
[1] "2000-04-30"
[1] "2000-07-31"
[1] "2000-11-30"
[1] "2001-04-30"
R>