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

Android中计算两个Epoch时间戳间仅日间时段的时间差

计算两个时间戳之间日出到日落时段的总时长(Android)

要解决这个问题,核心思路是拆分每一天的有效时长再累加总和——因为每天的日出日落时间都不一样,得逐个日期处理,判断当天的有效时间段是日出到日落的哪一部分,再计算时长。

步骤拆解

  1. 时间戳与日期映射:把起始、结束时间戳转换成具体日期,确定需要处理的日期范围(从startTime所在日期到endTime所在日期)。
  2. 日期与日出日落数据匹配:根据你提供的sunRise和sunSet映射(key是第N天),将每个日期对应到对应的日出日落时间。
  3. 计算单天有效时长:对每个日期,计算当天实际处于日出到日落且在[startTime, endTime]范围内的时间段,再转成毫秒级时长。
  4. 累加总时长:把所有天数的有效时长相加,得到最终结果。

代码实现

注意事项

  • 若开发的是Android 8.0(API 26)及以上,可直接使用java.time包的API(推荐,语法更简洁);
  • 低版本Android需引入ThreeTenABP兼容库,在build.gradle中添加依赖:
    implementation 'com.jakewharton.threetenabp:threetenabp:1.4.6'
    

实现代码(以java.time为例,低版本替换为ThreeTenABP对应类即可)

import java.time.Instant
import java.time.LocalDate
import java.time.LocalTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit

fun calculateDaytimeDuration(
    startTime: Long,
    endTime: Long,
    sunRiseMap: Map<Int, String>,
    sunSetMap: Map<Int, String>
): Long {
    // 定义时区(根据业务需求调整,示例用东八区)
    val zoneId = ZoneId.of("Asia/Shanghai")
    
    // 将时间戳转换为带时区的日期时间对象
    val startZdt = Instant.ofEpochMilli(startTime).atZone(zoneId)
    val endZdt = Instant.ofEpochMilli(endTime).atZone(zoneId)
    
    // 计算需要处理的总天数(包含起始日和结束日)
    val dayCount = ChronoUnit.DAYS.between(startZdt.toLocalDate(), endZdt.toLocalDate()) + 1
    
    var totalDuration = 0L
    
    for (dayIndex in 0 until dayCount.toInt()) {
        // 获取当前处理的日期
        val currentDate = startZdt.toLocalDate().plusDays(dayIndex.toLong())
        // 对应sunRise/sunSet的key(第1天对应key=1,以此类推)
        val mapKey = dayIndex + 1
        
        // 跳过无日出日落数据的日期
        val sunriseTimeStr = sunRiseMap[mapKey] ?: continue
        val sunsetTimeStr = sunSetMap[mapKey] ?: continue
        
        // 将"6.00"这类字符串转换为LocalTime对象
        val sunriseLocalTime = parseTimeStr(sunriseTimeStr)
        val sunsetLocalTime = parseTimeStr(sunsetTimeStr)
        
        // 组合成当天的日出、日落时间戳(毫秒)
        val sunriseZdt = ZonedDateTime.of(currentDate, sunriseLocalTime, zoneId)
        val sunsetZdt = ZonedDateTime.of(currentDate, sunsetLocalTime, zoneId)
        val sunriseTimestamp = sunriseZdt.toInstant().toEpochMilli()
        val sunsetTimestamp = sunsetZdt.toInstant().toEpochMilli()
        
        // 确定当天的有效开始/结束时间
        val dayStart = if (dayIndex == 0) {
            // 起始日:取startTime和日出时间的较大值
            maxOf(startTime, sunriseTimestamp)
        } else {
            sunriseTimestamp
        }
        
        val dayEnd = if (dayIndex == dayCount.toInt() - 1) {
            // 结束日:取endTime和日落时间的较小值
            minOf(endTime, sunsetTimestamp)
        } else {
            sunsetTimestamp
        }
        
        // 仅当有效开始时间<=结束时间时,累加时长
        if (dayStart <= dayEnd) {
            totalDuration += dayEnd - dayStart
        }
    }
    
    return totalDuration
}

// 辅助方法:将"6.00"或"18.30"格式的字符串转为LocalTime
private fun parseTimeStr(timeStr: String): LocalTime {
    val parts = timeStr.split(".")
    val hour = parts[0].toInt()
    val minute = parts[1].toInt()
    return LocalTime.of(hour, minute)
}

调用示例

val sunRise = mapOf<Int,String>(1 to "6.00", 2 to "6.05",3 to "6.01", 4 to "6.06")
val sunSet = mapOf<Int,String>(1 to "18.30", 2 to "18.25",3 to "18.20", 4 to "18.23")
val startTime = 1579919400000 // 对应东八区2020-01-24 09:30:00
val endTime = 1580203800000 // 对应东八区2020-01-27 10:30:00

val totalDaytime = calculateDaytimeDuration(startTime, endTime, sunRise, sunSet)
// 总时长为毫秒,可转换为分钟:totalDaytime / (1000 * 60)

关键细节说明

  • 时区处理:必须指定正确的时区(如东八区Asia/Shanghai),否则时间转换会出错,导致日出日落时间与实际日期不匹配。
  • 边界情况兼容:
    • 若startTime在日出前,当天有效开始时间从日出算起;
    • 若endTime在日落后,当天有效结束时间到日落为止;
    • 若某天日出时间晚于日落时间(异常情况),当天有效时长为0;
    • 若sunRise/sunSet无对应日期数据,直接跳过该天计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 13:17:29