Scrape Daily Daylight (R)

gist
An R script for retrieving sunrise-sunset times and calculating daily daylight duration from the Thai Astronomical Society

This is R code used to retrieve sunrise and sunset times from the Thai Astronomical Society website (thaiastro.nectec.or.th), then use that data to calculate the length of Daylight Hours per day for a given province.

This dataset is very useful for research related to the environmental impact of light on plant growth, or for studies of animal behavior.

📦 Required Libraries

Before using this code, install and load the following libraries:

library(rvest)     # For web scraping, reading HTML structure, and extracting data
library(dplyr)     # For manipulating and cleaning data frames
library(stringr)   # For string manipulation
library(lubridate) # For handling and calculating dates and times
library(purrr)     # For looping through each month's table (functional programming)

💻 The get_daily_daylight Function

Full code for the data retrieval function

get_daily_daylight <- function(year, province_name) {
  
  # 1. Handle the year number
  year_ce <- ifelse(year > 2500, year - 543, year)
  index_url <- paste0("https://thaiastro.nectec.or.th/skyevnt/sunmoon/", year_ce, "/")

  # 2. Find the link for the target province
  index_page <- tryCatch(
    read_html(index_url, encoding = "UTF-8"),
    error = function(e) { stop(paste("Unable to access the index data for year", year_ce)) }
  )
  
  links <- index_page %>% html_nodes("a")
  prov_df <- data.frame(
    province = html_text(links) %>% str_trim(),
    href = html_attr(links, "href"),
    stringsAsFactors = FALSE
  )
  
  matched_row <- prov_df %>% filter(str_detect(province, province_name))
  if (nrow(matched_row) == 0) stop(paste("No data found for province:", province_name))

  # 3. Retrieve data from that province's page
  target_url <- paste0("https://thaiastro.nectec.or.th/skyevnt/sunmoon/", year_ce, "/", matched_row$href[1])
  target_page <- read_html(target_url, encoding = "UTF-8")

  # Extract "all" tables on that page (this site has 1 table per month)
  tables <- target_page %>% html_table(fill = TRUE)

  # Keep only tables with 11 columns (the standard structure of this site's schedule tables)
  valid_tables <- keep(tables, ~ ncol(.x) == 11)

  if (length(valid_tables) == 0) stop("No table with the correct structure (11 columns) found on this page")

  # 4. Bind all 12 months' tables together (bind rows)
  raw_df <- map_df(valid_tables, function(tbl) {
    # Temporarily rename columns so they can be bound together
    colnames(tbl) <- paste0("V", 1:11)
    # Convert every column to character to prevent data type mismatch errors
    mutate_all(tbl, as.character)
  })

  # Rename columns according to the actual structure
  colnames(raw_df) <- c("DateStr", "Weekday",
                        "Sunrise", "Sunrise_Azimuth", 
                        "Sunset", "Sunset_Azimuth",
                        "Moon_Illum", 
                        "Moonrise", "Moonrise_Azimuth", 
                        "Moonset", "Moonset_Azimuth")
  
  # 5. Clean the data and calculate the daylight duration
  final_data <- raw_df %>%
    # Keep only rows that are actual time data (identified by the ":" character)
    filter(str_detect(Sunrise, ":") & str_detect(Sunset, ":")) %>%
    mutate(
      Year = year_ce,
      Province = province_name,

      # Split the date and month apart (e.g., "1 ม.ค." -> Day=1, Month_Th="ม.ค.")
      Day = as.integer(str_extract(DateStr, "^\\d+")),
      Month_Th = str_replace(DateStr, "^\\d+\\s*", ""),

      # Convert the "HH:MM" text into a Period
      rise_time = hm(Sunrise),
      set_time = hm(Sunset),

      # Calculate daylight duration in hours (decimal)
      Daylight_Hours = round(time_length(set_time - rise_time, unit = "hour"), 2)
    ) %>%
    # Arrange the columns needed for further use
    select(Year, Province, Month_Th, Day, Weekday, Sunrise, Sunset, Daylight_Hours)

  return(final_data)
}

💡 Real-World Usage Example

See an example of using the function to retrieve daily daylight duration in 2024 for various locations

# Retrieve data for Phatthalung province in 2024
phatthalung_daylight <- get_daily_daylight(2024, "พัทลุง")

# Check the first 5 rows of the data table
head(phatthalung_daylight)

# Retrieve data for Bangkok in 2025
bkk_daylight <- get_daily_daylight(2025, "กรุงเทพมหานคร")
head(bkk_daylight)