Android 基于指定时区判断营业时间是否超时 日期解析报错解决
问题解决方法
报错直接原因
你遇到的java.text.ParseException: Unparseable date: "10:00AM"错误核心是时间格式串定义错误:
你代码中写的格式串是"hh:mm\\aa",多余添加了转义反斜杠,导致解析器会强制匹配输入串中的\a字符,而你传入的时间里不存在该字符,直接解析失败。
另外还有两个隐藏的解析失败风险:
- 用了
Locale.getDefault(),如果设备本地语言不是英语,AM/PM的本地化标记和你传入的英文AM/PM不匹配,也会解析失败,需要固定使用Locale.US。 - 第一版代码中
tzs变量未初始化就调用sdf.setTimeZone(tzs),会直接抛出空指针异常。
其他隐藏逻辑问题
你当前的实现还有跨天营业时间判断错误的问题:比如调用示例中timeElapse("10:00AM", "01:00AM", "Asia/Kuala_Lumpur")属于次日凌晨关门的跨天营业场景,直接比较两个时分的时间戳会判定01:00AM早于10:00AM,逻辑完全错误。
修复实现
方案1:兼容Android 所有版本(基于SimpleDateFormat)
public void timeElapse(String businessStart, String businessEnd, String timeZoneId){ // 修正格式串,固定Locale匹配英文AM/PM SimpleDateFormat sdf = new SimpleDateFormat("hh:mm a", Locale.US); try { TimeZone targetTz = TimeZone.getTimeZone(timeZoneId); sdf.setTimeZone(targetTz); // 解析营业时间为1970年基准的时间戳 long startTs = sdf.parse(businessStart).getTime(); long endTs = sdf.parse(businessEnd).getTime(); // 获取当前指定时区的时分,转换为同基准的时间戳 Calendar nowCal = Calendar.getInstance(targetTz); Calendar currentCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); currentCal.set(1970, Calendar.JANUARY, 1, nowCal.get(Calendar.HOUR_OF_DAY), nowCal.get(Calendar.MINUTE), 0); currentCal.set(Calendar.MILLISECOND, 0); long currentTs = currentCal.getTimeInMillis(); // 处理跨天营业逻辑 if (endTs < startTs) { endTs += 24 * 60 * 60 * 1000; if (currentTs < startTs) { currentTs += 24 * 60 * 60 * 1000; } } // 输出结果 if(currentTs > endTs){ Log.e("timeElapse", "已过营业时间"); } else if (currentTs < startTs) { Log.e("timeElapse", "尚未营业"); } else { Log.e("timeElapse", "营业中"); } } catch (ParseException e) { Log.e("timeElapse", "时间格式解析错误", e); } }
方案2:推荐方案(Android 8.0及以上,基于java.time)
java.time是Android官方推荐的时间处理库,避免了SimpleDateFormat的线程安全、格式解析等各种历史坑,代码更简洁:
public void timeElapse(String businessStart, String businessEnd, String timeZoneId){ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a", Locale.US); ZoneId targetZone = ZoneId.of(timeZoneId); // 解析营业时间 LocalTime startTime = LocalTime.parse(businessStart, formatter); LocalTime endTime = LocalTime.parse(businessEnd, formatter); // 获取当前指定时区的时间 LocalTime now = LocalTime.now(targetZone); if (endTime.isAfter(startTime)) { // 非跨天营业 if (now.isAfter(endTime)) { Log.e("timeElapse", "已过营业时间"); } else if (now.isBefore(startTime)) { Log.e("timeElapse", "尚未营业"); } else { Log.e("timeElapse", "营业中"); } } else { // 跨天营业 if (now.isAfter(startTime) || now.isBefore(endTime)) { Log.e("timeElapse", "营业中"); } else { Log.e("timeElapse", "已过营业时间"); } } }
如果需要兼容Android 8.0以下版本,引入ThreeTenABP适配库即可使用java.time相关API。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

