Functions for calculating the timing of solar positions, given geographical coordinates and dates. They can be also used to find the time for an arbitrary solar elevation between 90 and -90 degrees by supplying "twilight" angle(s) as argument.
Usage
day_night(
  date = lubridate::now(tzone = "UTC"),
  tz = ifelse(lubridate::is.Date(date), "UTC", lubridate::tz(date)),
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "none",
  unit.out = "hours"
)
day_night_fast(date, tz, geocode, twilight, unit.out)
is_daytime(
  date = lubridate::now(tzone = "UTC"),
  tz = ifelse(lubridate::is.Date(date), "UTC", lubridate::tz(date)),
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "none",
  unit.out = "hours"
)
noon_time(
  date = lubridate::now(tzone = "UTC"),
  tz = lubridate::tz(date),
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "none",
  unit.out = "datetime"
)
sunrise_time(
  date = lubridate::now(tzone = "UTC"),
  tz = lubridate::tz(date),
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "sunlight",
  unit.out = "datetime"
)
sunset_time(
  date = lubridate::now(tzone = "UTC"),
  tz = lubridate::tz(date),
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "sunlight",
  unit.out = "datetime"
)
day_length(
  date = lubridate::now(tzone = "UTC"),
  tz = "UTC",
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "sunlight",
  unit.out = "hours"
)
night_length(
  date = lubridate::now(tzone = "UTC"),
  tz = "UTC",
  geocode = tibble::tibble(lon = 0, lat = 51.5, address = "Greenwich"),
  twilight = "sunlight",
  unit.out = "hours"
)Arguments
- date
- "vector" of - POSIXcttimes or- Dateobjects, any valid TZ is allowed, default is current date at Greenwich matching the default for- geocode.
- tz
- character vector indicating time zone to be used in output and to interpret - Datevalues passed as argument to- date.
- geocode
- data frame with one or more rows and variables lon and lat as numeric values (degrees). If present, address will be copied to the output. 
- twilight
- character string, one of "none", "rim", "refraction", "sunlight", "civil", "nautical", "astronomical", or a - numericvector of length one, or two, giving solar elevation angle(s) in degrees (negative if below the horizon).
- unit.out
- character string, One of "datetime", "day", "hour", "minute", or "second". 
Value
A tibble with variables day, tz, twilight.rise, twilight.set, longitude, latitude, address, sunrise, noon, sunset, daylength, nightlength or the corresponding individual vectors.
The value returned represents an instant in time or a duration. The
class of the object returned varies depending on the argument passed to
parameter unit.out. If unit.out = "datetime", the returned
value is a "POSIXct" vector, otherwise it is a "numeric" vector.
is_daytime() returns a logical vector, with TRUE for
  day time and FALSE for night time.
noon_time, sunrise_time and sunset_time return a
  vector of POSIXct times
day_length and night_length return numeric a vector
  giving the length in hours
Details
Twilight names are interpreted as follows. "none": solar elevation =
  0 degrees. "rim": upper rim of solar disk at the horizon or solar elevation
  = -0.53 / 2. "refraction": solar elevation = 0 degrees + refraction
  correction. "sunlight": upper rim of solar disk corrected for refraction,
  which is close to the value used by the online NOAA Solar Calculator.
  "civil": -6 degrees, "nautical": -12 degrees, and "astronomical": -18 degrees.
  Unit names for output are as follows: "day", "hours", "minutes" and
  "seconds" times for sunrise and sunset are returned as times-of-day since
  midnight expressed in the chosen unit. "date" or "datetime" return the same
  times as datetime objects with TZ set (this is much slower than "hours").
  Day length and night length are returned as numeric values expressed in
  hours when `"datetime"' is passed as argument to unit.out. If
  twilight is a numeric vector of length two, the element with index 1 is
  used for sunrise and that with index 2 for sunset.
is_daytime() supports twilight specifications by name, a test
  like sun_elevation() > 0 may be used directly for a numeric angle.
Note
Function day_night() is an implementation of Meeus equations as
  used in NOAAs on-line web calculator, which are very precise and valid for
  a very broad range of dates. For sunrise and sunset the times are affected
  by refraction in the atmosphere, which does in turn depend on weather
  conditions. The effect of refraction on the apparent position of the sun is
  only an estimate based on "typical" conditions. The more tangential to the
  horizon is the path of the sun, the larger the effect of refraction is on
  the times of visual occlusion of the sun behind the horizon—i.e. the
  largest timing errors occur at high latitudes. The computation is not
  defined for latitudes 90 and -90 degrees, i.e. at the poles.
There exists a different R implementation of the same algorithms called
  "AstroCalcPureR" available as function astrocalc4r in package
  'fishmethods'. Although the equations used are almost all the same, the
  function signatures and which values are returned differ. In particular,
  the implementation in 'photobiology' splits the calculation into two
  separate functions, one returning angles at given instants in time, and a
  separate one returning the timing of events for given dates. In
  'fishmethods' (= 1.11-0) there is a bug in function astrocalc4r() that
  affects sunrise and sunset times. The times returned by the functions in
  package 'photobiology' have been validated against the NOAA base
  implementation.
In the current implementation functions sunrise_time,
  noon_time, sunset_time, day_length,
  night_length and is_daytime are all wrappers
  on day_night, so if more than one quantity is needed it is
  preferable to directly call day_night and extract the different
  components from the returned list.
night_length returns the length of night-time conditions in one
  day (00:00:00 to 23:59:59), rather than the length of the night between two
  consecutive days.
Warning
Be aware that R's Date class does not save time zone
  metadata. This can lead to ambiguities in the current implementation
  based on time instants. The argument passed to date should be
  of class POSIXct, in other words an instant in time, from which
  the correct date will be computed based on the tz argument.
The time zone in which times passed to date as argument are
  expressed does not need to be the local one or match the geocode, however,
  the returned values will be in the same time zone as the input.
References
The primary source for the algorithm used is the book: Meeus, J. (1998) Astronomical Algorithms, 2 ed., Willmann-Bell, Richmond, VA, USA. ISBN 978-0943396613.
A different implementation is available at https://github.com/NEFSC/READ-PDB-AstroCalc4R/ and in R paclage 'fishmethods'. In 'fishmethods' (= 1.11-0) there is a bug in function astrocalc4r() that affects sunrise and sunset times.
An interactive web page using the same algorithms is available at https://gml.noaa.gov/grad/solcalc/. There are small differences in the returned times compared to our function that seem to be related to the estimation of atmospheric refraction (about 0.1 degrees).
See also
Other astronomy related functions:
format.solar_time(),
sun_angles()
Examples
library(lubridate)
my.geocode <- data.frame(lon = 24.93838,
                         lat = 60.16986,
                         address = "Helsinki, Finland")
day_night(ymd("2015-05-30", tz = "EET"),
          geocode = my.geocode)
#> # A tibble: 1 × 12
#>   day                 tz    twilight.rise twilight.set longitude latitude
#>   <dttm>              <chr>         <dbl>        <dbl>     <dbl>    <dbl>
#> 1 2015-05-29 00:00:00 EET               0            0      24.9     60.2
#> # ℹ 6 more variables: address <chr>, sunrise <dbl>, noon <dbl>, sunset <dbl>,
#> #   daylength <dbl>, nightlength <dbl>
day_night(ymd("2015-05-30", tz = "EET") + days(1:10),
          geocode = my.geocode,
          twilight = "civil")
#> # A tibble: 10 × 12
#>    day                 tz    twilight.rise twilight.set longitude latitude
#>    <dttm>              <chr>         <dbl>        <dbl>     <dbl>    <dbl>
#>  1 2015-05-30 00:00:00 EET              -6           -6      24.9     60.2
#>  2 2015-05-31 00:00:00 EET              -6           -6      24.9     60.2
#>  3 2015-06-01 00:00:00 EET              -6           -6      24.9     60.2
#>  4 2015-06-02 00:00:00 EET              -6           -6      24.9     60.2
#>  5 2015-06-03 00:00:00 EET              -6           -6      24.9     60.2
#>  6 2015-06-04 00:00:00 EET              -6           -6      24.9     60.2
#>  7 2015-06-05 00:00:00 EET              -6           -6      24.9     60.2
#>  8 2015-06-06 00:00:00 EET              -6           -6      24.9     60.2
#>  9 2015-06-07 00:00:00 EET              -6           -6      24.9     60.2
#> 10 2015-06-08 00:00:00 EET              -6           -6      24.9     60.2
#> # ℹ 6 more variables: address <chr>, sunrise <dbl>, noon <dbl>, sunset <dbl>,
#> #   daylength <dbl>, nightlength <dbl>
sunrise_time(ymd("2015-05-30", tz = "EET"),
             geocode = my.geocode)
#> [1] "2015-05-29 04:12:39 EEST"
noon_time(ymd("2015-05-30", tz = "EET"),
          geocode = my.geocode)
#> [1] "2015-05-29 13:17:37 EEST"
sunset_time(ymd("2015-05-30", tz = "EET"),
            geocode = my.geocode)
#> [1] "2015-05-29 22:22:34 EEST"
day_length(ymd("2015-05-30", tz = "EET"),
           geocode = my.geocode)
#> [1] 18.16531
day_length(ymd("2015-05-30", tz = "EET"),
           geocode = my.geocode,
           unit.out = "day")
#> [1] 0.756888
is_daytime(ymd("2015-05-30", tz = "EET") + hours(c(0, 6, 12, 18, 24)),
           geocode = my.geocode)
#> [1] FALSE  TRUE  TRUE  TRUE FALSE
is_daytime(ymd_hms("2015-05-30 03:00:00", tz = "EET"),
           geocode = my.geocode)
#> [1] FALSE
is_daytime(ymd_hms("2015-05-30 00:00:00", tz = "UTC"),
           geocode = my.geocode)
#> [1] FALSE
is_daytime(ymd_hms("2015-05-30 03:00:00", tz = "EET"),
           geocode = my.geocode,
           twilight = "civil")
#> [1] TRUE
is_daytime(ymd_hms("2015-05-30 00:00:00", tz = "UTC"),
           geocode = my.geocode,
           twilight = "civil")
#> [1] TRUE
