You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R语言时间逻辑判断失效求助:基于日出日落时间分类时段

问题分析与解决:时区不匹配导致时段分类失效

核心问题

你使用lubridate、dplyr、suncalc处理数据时,捕获时间(Time列,UTC时区)与日出日落时间(dawn/sunriseEnd等,MST时区)直接对比,因时区不统一导致逻辑判断几乎不生效,仅少数行得到结果。


原数据与代码

数据样例

library(lubridate)
library(dplyr)
library(suncalc)

# 数据前6行
md <- structure(list(Date.of.Capture = structure(c(18383, 18393, 18395, 
18395, 18402, 18815), class = "Date"), Month = c(5L, 5L, 5L, 
5L, 5L, 7L), Day = c(1L, 11L, 13L, 13L, 20L, 7L), Year = c(2020L, 
2020L, 2020L, 2020L, 2020L, 2021L), Time.of.Capture = c("6:24", 
"6:27", "8:55", "8:55", "20:22", "6:26"), Time = structure(c(1588314240, 
1589178420, 1589360100, 1589360100, 1590006120, 1625639160), class = c("POSIXct", 
"POSIXt"), tzone = "UTC"), ToD = c(NA, NA, NA, NA, "Daytime", 
NA), lat = c(40.75336, 40.75336, 40.75336, 40.75336, 40.75336, 
40.75336), lon = c(-111.624088, -111.624088, -111.624088, -111.624088, 
-111.624088, -111.624088), sunriseEnd = structure(c(1588336111, 
1589199430, 1589372111, 1589372111, 1589976545, 1625659614), class = c("POSIXct", 
"POSIXt"), tzone = "MST"), sunsetStart = structure(c(1588386056, 
1589250673, 1589423593, 1589423593, 1590028795, 1625713092), class = c("POSIXct", 
"POSIXt"), tzone = "MST"), dawn = structure(c(1588334143, 1589197398, 
1589370065, 1589370065, 1589974454, 1625657442), class = c("POSIXct", 
"POSIXt"), tzone = "MST"), dusk = structure(c(1588388024, 1589252706, 
1589425639, 1589425639, 1590030886, 1625715264), class = c("POSIXct", 
"POSIXt"), tzone = "MST")), class = "data.frame", row.names = c(NA, 
6L))

原数据格式化代码

md$Time <- paste0(md$Year, "-", md$Month, "-", md$Day,  " ", md$Time.of.Capture,":00")
md$Time <- ymd_hms(md$Time)

md$Date.of.Capture <- paste0(md$Year, "-", md$Month, "-", md$Day)
md$Date.of.Capture <- as.Date(md$Date.of.Capture, format = "%Y-%m-%d", tz = "MST")

timesofday <- getSunlightTimes(date = md$Date.of.Capture, 
                               lat = 40.753360, lon = -111.624088, 
                               tz="MST", keep=c("sunriseEnd", "sunsetStart", "dawn", "dusk"))

md <- left_join(md, timesofday, by = c("Date.of.Capture" = "date"))

原时段判断代码

md$ToD<-NA

for(i in 1: nrow(md)){
  if (md$Time[i] > md$dawn[i] & md$Time[i] < md$sunriseEnd[i]){
    md$ToD[i] <- "Dawn"
  } else if (md$Time[i] > md$sunsetStart[i] & md$Time[i] < md$dusk[i]){
    md$ToD[i] <- "Dusk"
  } else if (md$Time[i] > md$dusk[i] & md$Time[i] < md$dawn[i]){
    md$ToD[i] <- "Nighttime"
  } else if (md$Time[i] > md$sunriseEnd[i] & md$Time[i] < md$sunsetStart[i]){
    md$ToD[i] <- "Daytime"
  }
}

unique(md$ToD)

问题根源

  1. 时区不统一:Time列是UTC时区,日出日落列是MST时区(比UTC晚7小时)。直接对比时,底层时间戳数值差7小时,导致大部分判断条件不成立。比如第一行UTC时间2020-05-01 06:24转换为MST是2020-04-30 23:24,和当天MST的dawn(2020-05-01 05:55)对比完全不在区间内。
  2. Nighttime逻辑错误:同一天的dusk时间戳必然大于dawn,Time > dusk & Time < dawn的条件永远无法满足,忽略了跨天的夜晚时段。

修正方案

步骤1:统一时区

将Time列转换为MST时区,与日出日落时间保持一致:

md$Time <- with_tz(md$Time, tzone = "MST")

步骤2:修正时段判断逻辑

改用dplyr::case_when简化代码,同时修正Nighttime的跨天判断:

md <- md %>%
  mutate(
    ToD = case_when(
      between(Time, dawn, sunriseEnd) ~ "Dawn",
      between(Time, sunriseEnd, sunsetStart) ~ "Daytime",
      between(Time, sunsetStart, dusk) ~ "Dusk",
      # 夜晚包含:当天黄昏到午夜、午夜到次日黎明
      Time > dusk | Time < dawn ~ "Nighttime",
      TRUE ~ NA_character_
    )
  )

完整修正代码

library(lubridate)
library(dplyr)
library(suncalc)

# 优化数据格式化
md$Time <- ymd_hms(paste(md$Year, md$Month, md$Day, md$Time.of.Capture, sep = " "), tz = "UTC")
md$Date.of.Capture <- ymd(paste(md$Year, md$Month, md$Day, sep = "-"))

timesofday <- getSunlightTimes(date = md$Date.of.Capture, 
                               lat = 40.753360, lon = -111.624088, 
                               tz="MST", keep=c("sunriseEnd", "sunsetStart", "dawn", "dusk"))

md <- left_join(md, timesofday, by = c("Date.of.Capture" = "date"))

# 统一时区为MST
md$Time <- with_tz(md$Time, tzone = "MST")

# 修正时段判断
md <- md %>%
  mutate(
    ToD = case_when(
      between(Time, dawn, sunriseEnd) ~ "Dawn",
      between(Time, sunriseEnd, sunsetStart) ~ "Daytime",
      between(Time, sunsetStart, dusk) ~ "Dusk",
      Time > dusk | Time < dawn ~ "Nighttime",
      TRUE ~ NA_character_
    )
  )

# 查看结果
md %>% select(Time, dawn, sunriseEnd, sunsetStart, dusk, ToD)

验证结果

修正后6行数据的ToD列将得到正确分类:

  • 第1行:Dawn(06:24在05:55-06:28区间内)
  • 第2行:Dawn(06:27在05:53-06:28区间内)
  • 第3-4行:Daytime(08:55在06:25-20:13区间内)
  • 第5行:Dusk(20:22在20:19-20:48区间内)
  • 第6行:Dawn(06:26等于sunriseEnd,若需包含边界可将between改为Time >= dawn & Time <= sunriseEnd)

内容的提问来源于stack exchange,提问作者BlakeLedbet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 22:24:52