Преобразование эпох с использованием негригорианских календарей

Я работаю с набором выходных данных климатической модели (в частности, модели CMIP5). Это netcdfs температуры, ветра и т. д. с отметками времени.

Все они используют соглашение days since YYYY-mm-dd 00:00:00 в UTC. Я преобразовывал в более простые объекты даты (не даты и времени), используя lubridate:

library(tidyverse)
input$date.utc =
  ymd_hms('0001-01-01 00:00:00', tz = 'UTC') +
  days(floor(input$time))

Я столкнулся с двумя проблемами. Во-первых, у каждой модели своя эпоха. Это довольно легко исправить. Другая, более сложная проблема заключается в том, что не все модели используют григорианский календарь. Некоторые используют вариацию на 365 дней, где нет високосных лет.

Я не вижу способа указать негригорианский календарь в функциях lubridate. Это возможно?


person jimjamslam    schedule 26.05.2017    source источник


Ответы (1)


Я не смог найти эту функцию в lubridate, поэтому я написал функцию, по крайней мере, для вычисления количества високосных дней между каждым элементом вектора дат и заданной эпохой:

# count_leap_days: returns an integer for the number of leap days between a
# supplied vector of dates and a fixed epoch
count_leap_days <- function(dates, epoch = ymd('1850-01-01'), proleptic = FALSE)
{
  require(lubridate)

  # check input
  if (!is(epoch, 'Date') | !is(dates, 'Date'))
  {
    stop('count_leap_days: both arguments must be Date objects.')
  }
  if (any(dates <= epoch))
  {
    stop('count_leap_days: dates should all be later than epoch.')
  }
  if (proleptic = FALSE & epoch < ymd('1582-10-15'))
  {
    message('count_leap_days: ',
      'no leap days before 1582-10-15 unless proleptic = TRUE.')
    epoch = ymd('1582-10-15')
  }

  # get the year range
  # exclude start (end) years if they begin after (start before) feb 29
  y.epoch = year(epoch) +
    ifelse(epoch >= ymd(paste0(year(epoch), '-03-01')), 1, 0)
  y.dates = year(dates) -
    ifelse(dates <= ymd(paste0(year(dates), '-02-28')), 1, 0)
  span = y.dates - y.epoch + 1

  # a year is a leap year if it's:
  #   - divisble by 4. but
  #   - NOT if it's also divisible by 100, unless
  #   - it's also divisible by 400.
  # all years div. by 4 are also div. by 100, and
  # all years div. by 100 are also div. by 400.
  # hence, total days = (div. by 4) - (div. by 100) + (div. by 400)
  div4 = span %/% 4 +
    ifelse(
      (y.epoch %% 4) %in% c(0, (4 - (span %% 4) - 1):(4 - 1)) &
      (y.dates %% 4) %in% 0:(span %% 4 - 1),
    1, 0)
  div100 = span %/% 100 +
    ifelse(
      (y.epoch %% 100) %in% c(0, (100 - (span %% 100) - 1):(100 - 1)) &
      (y.dates %% 100) %in% 0:(span %% 100 - 1),
    1, 0)
  div400 = span %/% 400 +
    ifelse(
      (y.epoch %% 400) %in% c(0, (400 - (span %% 400) - 1):(400 - 1)) &
      (y.dates %% 400) %in% 0:(span %% 400 - 1),
    1, 0)
  return(div4 - div100 + div400) 
}

При этом я могу преобразовать 365-дневный календарь в григорианский, просто добавив количество пропущенных високосных дней:

input$date.utc = input$date.utc +
  count_leap_days(input$date.utc, epoch = epoch)
person jimjamslam    schedule 30.05.2017