Android中计算两个Epoch时间戳间仅日间时段的时间差
计算两个时间戳之间日出到日落时段的总时长(Android)
要解决这个问题,核心思路是拆分每一天的有效时长再累加总和——因为每天的日出日落时间都不一样,得逐个日期处理,判断当天的有效时间段是日出到日落的哪一部分,再计算时长。
步骤拆解
- 时间戳与日期映射:把起始、结束时间戳转换成具体日期,确定需要处理的日期范围(从
startTime所在日期到endTime所在日期)。 - 日期与日出日落数据匹配:根据你提供的
sunRise和sunSet映射(key是第N天),将每个日期对应到对应的日出日落时间。 - 计算单天有效时长:对每个日期,计算当天实际处于日出到日落且在
[startTime, endTime]范围内的时间段,再转成毫秒级时长。 - 累加总时长:把所有天数的有效时长相加,得到最终结果。
代码实现
注意事项
- 若开发的是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
相关产品推荐
相关产品推荐

