import csv
import os
import json
import sys
from datetime import datetime
def calculate_device_usage(input_file, output_file):
"""
统计智能设备使用时间
:param input_file: 输入CSV文件路径
:param output_file: 输出CSV文件路径
"""
# 检查输入文件是否存在
if not os.path.exists(input_file):
raise FileNotFoundError
(f
"输入文件不存在: {input_file}")
# 读取并处理数据
device_data = {}
with open(input_file, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row_num, row in enumerate(reader, 1):
try:
device_id = row['device_id']
start = datetime.fromisoformat(row['start_time'])
end = datetime.fromisoformat(row['end_time']) if row['end_time'] else datetime.now()
if device_id not in device_data:
device_data[device_id] = []
device_data[device_id].append((start, end))
except (KeyError, ValueError) as e:
print(f"警告: 第 {row_num} 行数据格式错误 - {str(e)}")
continue
# 如果没有找到有效数据
if not device_data:
print("错误: 输入文件中没有找到有效数据")
return
# 计算每个设备的总使用时间(秒)
results = []
for device_id, periods in device_data.items():
# 按开始时间排序
periods.sort()
# 合并重叠时间段
merged = []
current_start, current_end = periods[0]
for start, end in periods[1:]:
if start <= current_end:
current_end = max(current_end, end)
else:
merged.append((current_start, current_end))
current_start, current_end = start, end
merged.append((current_start, current_end))
# 计算总时间(秒)
total_seconds = sum((end - start).total_seconds() for start, end in merged)
# 转换为可读格式
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
readable = f"{int(hours)}小时{int(minutes)}分{int(seconds)}秒"
results.append({
'device_id': device_id,
'total_seconds': total_seconds,
'readable_time': readable
})
# 写入结果
with open(output_file, 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['device_id', 'total_seconds', 'readable_time'])
writer.writeheader()
writer.writerows(results)
print(f"统计完成!结果已保存至: {output_file}")
print(f"处理了 {len(results)} 台设备的使用记录")
# 使用示例
if __name__ == "__main__":
try:
# 替换为实际文件路径
input_file = "device_logs.csv" # 输入文件名
output_file = "usage_report.csv" # 输出文件名
# 检查输入文件是否存在
if not os.path.exists(input_file):
# 创建示例文件(仅用于测试)
print(f"创建示例文件: {input_file}")
sample_data = [
['device_id', 'start_time', 'end_time'],
['D001', '2023-06-01 08:30:00', '2023-06-01 10:15:00'],
['D001', '2023-06-01 14:00:00', '2023-06-01 16:45:00'],
['D002', '2023-06-01 09:00:00', '2023-06-01 11:30:00'],
['D001', '2023-06-02 13:30:00', '2023-06-02 15:00:00'],
['D003', '2023-06-02 10:00:00', ''], # 未结束的使用记录
]
with open(input_file, 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerows(sample_data)
print("已创建示例数据文件,程序将使用此文件运行")
# 执行统计
calculate_device_usage(input_file, output_file)
except Exception as e:
print(f"程序运行时出错: {str(e)}")
print("请检查输入文件格式是否正确")
print("文件格式要求:")
print("1. CSV格式,包含表头: device_id,start_time,end_time")
print("2. 时间格式: YYYY-MM-DD HH:MM:SS")
print("3. 空结束时间表示设备仍在运行")
aW1wb3J0IGNzdgppbXBvcnQgb3MKaW1wb3J0IGpzb24KaW1wb3J0IHN5cwpmcm9tIGRhdGV0aW1lIGltcG9ydCBkYXRldGltZQoKZGVmIGNhbGN1bGF0ZV9kZXZpY2VfdXNhZ2UoaW5wdXRfZmlsZSwgb3V0cHV0X2ZpbGUpOgogICAgIiIiCiAgICDnu5/orqHmmbrog73orr7lpIfkvb/nlKjml7bpl7QKICAgIDpwYXJhbSBpbnB1dF9maWxlOiDovpPlhaVDU1bmlofku7bot6/lvoQKICAgIDpwYXJhbSBvdXRwdXRfZmlsZTog6L6T5Ye6Q1NW5paH5Lu26Lev5b6ECiAgICAiIiIKICAgICMg5qOA5p+l6L6T5YWl5paH5Lu25piv5ZCm5a2Y5ZyoCiAgICBpZiBub3Qgb3MucGF0aC5leGlzdHMoaW5wdXRfZmlsZSk6CiAgICAgICAgcmFpc2UgRmlsZU5vdEZvdW5kRXJyb3IoZiLovpPlhaXmlofku7bkuI3lrZjlnKg6IHtpbnB1dF9maWxlfSIpCiAgICAKICAgICMg6K+75Y+W5bm25aSE55CG5pWw5o2uCiAgICBkZXZpY2VfZGF0YSA9IHt9CiAgICB3aXRoIG9wZW4oaW5wdXRfZmlsZSwgJ3InLCBlbmNvZGluZz0ndXRmLTgnKSBhcyBmOgogICAgICAgIHJlYWRlciA9IGNzdi5EaWN0UmVhZGVyKGYpCiAgICAgICAgZm9yIHJvd19udW0sIHJvdyBpbiBlbnVtZXJhdGUocmVhZGVyLCAxKToKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgZGV2aWNlX2lkID0gcm93WydkZXZpY2VfaWQnXQogICAgICAgICAgICAgICAgc3RhcnQgPSBkYXRldGltZS5mcm9taXNvZm9ybWF0KHJvd1snc3RhcnRfdGltZSddKQogICAgICAgICAgICAgICAgZW5kID0gZGF0ZXRpbWUuZnJvbWlzb2Zvcm1hdChyb3dbJ2VuZF90aW1lJ10pIGlmIHJvd1snZW5kX3RpbWUnXSBlbHNlIGRhdGV0aW1lLm5vdygpCiAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIGlmIGRldmljZV9pZCBub3QgaW4gZGV2aWNlX2RhdGE6CiAgICAgICAgICAgICAgICAgICAgZGV2aWNlX2RhdGFbZGV2aWNlX2lkXSA9IFtdCiAgICAgICAgICAgICAgICBkZXZpY2VfZGF0YVtkZXZpY2VfaWRdLmFwcGVuZCgoc3RhcnQsIGVuZCkpCiAgICAgICAgICAgIGV4Y2VwdCAoS2V5RXJyb3IsIFZhbHVlRXJyb3IpIGFzIGU6CiAgICAgICAgICAgICAgICBwcmludChmIuitpuWRijog56ysIHtyb3dfbnVtfSDooYzmlbDmja7moLzlvI/plJnor68gLSB7c3RyKGUpfSIpCiAgICAgICAgICAgICAgICBjb250aW51ZQogICAgCiAgICAjIOWmguaenOayoeacieaJvuWIsOacieaViOaVsOaNrgogICAgaWYgbm90IGRldmljZV9kYXRhOgogICAgICAgIHByaW50KCLplJnor686IOi+k+WFpeaWh+S7tuS4reayoeacieaJvuWIsOacieaViOaVsOaNriIpCiAgICAgICAgcmV0dXJuCiAgICAKICAgICMg6K6h566X5q+P5Liq6K6+5aSH55qE5oC75L2/55So5pe26Ze077yI56eS77yJCiAgICByZXN1bHRzID0gW10KICAgIGZvciBkZXZpY2VfaWQsIHBlcmlvZHMgaW4gZGV2aWNlX2RhdGEuaXRlbXMoKToKICAgICAgICAjIOaMieW8gOWni+aXtumXtOaOkuW6jwogICAgICAgIHBlcmlvZHMuc29ydCgpCiAgICAgICAgCiAgICAgICAgIyDlkIjlubbph43lj6Dml7bpl7TmrrUKICAgICAgICBtZXJnZWQgPSBbXQogICAgICAgIGN1cnJlbnRfc3RhcnQsIGN1cnJlbnRfZW5kID0gcGVyaW9kc1swXQogICAgICAgIGZvciBzdGFydCwgZW5kIGluIHBlcmlvZHNbMTpdOgogICAgICAgICAgICBpZiBzdGFydCA8PSBjdXJyZW50X2VuZDoKICAgICAgICAgICAgICAgIGN1cnJlbnRfZW5kID0gbWF4KGN1cnJlbnRfZW5kLCBlbmQpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBtZXJnZWQuYXBwZW5kKChjdXJyZW50X3N0YXJ0LCBjdXJyZW50X2VuZCkpCiAgICAgICAgICAgICAgICBjdXJyZW50X3N0YXJ0LCBjdXJyZW50X2VuZCA9IHN0YXJ0LCBlbmQKICAgICAgICBtZXJnZWQuYXBwZW5kKChjdXJyZW50X3N0YXJ0LCBjdXJyZW50X2VuZCkpCiAgICAgICAgCiAgICAgICAgIyDorqHnrpfmgLvml7bpl7TvvIjnp5LvvIkKICAgICAgICB0b3RhbF9zZWNvbmRzID0gc3VtKChlbmQgLSBzdGFydCkudG90YWxfc2Vjb25kcygpIGZvciBzdGFydCwgZW5kIGluIG1lcmdlZCkKICAgICAgICAKICAgICAgICAjIOi9rOaNouS4uuWPr+ivu+agvOW8jwogICAgICAgIGhvdXJzLCByZW1haW5kZXIgPSBkaXZtb2QodG90YWxfc2Vjb25kcywgMzYwMCkKICAgICAgICBtaW51dGVzLCBzZWNvbmRzID0gZGl2bW9kKHJlbWFpbmRlciwgNjApCiAgICAgICAgcmVhZGFibGUgPSBmIntpbnQoaG91cnMpfeWwj+aXtntpbnQobWludXRlcyl95YiGe2ludChzZWNvbmRzKX3np5IiCiAgICAgICAgCiAgICAgICAgcmVzdWx0cy5hcHBlbmQoewogICAgICAgICAgICAnZGV2aWNlX2lkJzogZGV2aWNlX2lkLAogICAgICAgICAgICAndG90YWxfc2Vjb25kcyc6IHRvdGFsX3NlY29uZHMsCiAgICAgICAgICAgICdyZWFkYWJsZV90aW1lJzogcmVhZGFibGUKICAgICAgICB9KQogICAgCiAgICAjIOWGmeWFpee7k+aenAogICAgd2l0aCBvcGVuKG91dHB1dF9maWxlLCAndycsIGVuY29kaW5nPSd1dGYtOCcsIG5ld2xpbmU9JycpIGFzIGY6CiAgICAgICAgd3JpdGVyID0gY3N2LkRpY3RXcml0ZXIoZiwgZmllbGRuYW1lcz1bJ2RldmljZV9pZCcsICd0b3RhbF9zZWNvbmRzJywgJ3JlYWRhYmxlX3RpbWUnXSkKICAgICAgICB3cml0ZXIud3JpdGVoZWFkZXIoKQogICAgICAgIHdyaXRlci53cml0ZXJvd3MocmVzdWx0cykKICAgIAogICAgcHJpbnQoZiLnu5/orqHlrozmiJDvvIHnu5Pmnpzlt7Lkv53lrZjoh7M6IHtvdXRwdXRfZmlsZX0iKQogICAgcHJpbnQoZiLlpITnkIbkuoYge2xlbihyZXN1bHRzKX0g5Y+w6K6+5aSH55qE5L2/55So6K6w5b2VIikKCiMg5L2/55So56S65L6LCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICB0cnk6CiAgICAgICAgIyDmm7/mjaLkuLrlrp7pmYXmlofku7bot6/lvoQKICAgICAgICBpbnB1dF9maWxlID0gImRldmljZV9sb2dzLmNzdiIgICMg6L6T5YWl5paH5Lu25ZCNCiAgICAgICAgb3V0cHV0X2ZpbGUgPSAidXNhZ2VfcmVwb3J0LmNzdiIgICMg6L6T5Ye65paH5Lu25ZCNCiAgICAgICAgCiAgICAgICAgIyDmo4Dmn6XovpPlhaXmlofku7bmmK/lkKblrZjlnKgKICAgICAgICBpZiBub3Qgb3MucGF0aC5leGlzdHMoaW5wdXRfZmlsZSk6CiAgICAgICAgICAgICMg5Yib5bu656S65L6L5paH5Lu277yI5LuF55So5LqO5rWL6K+V77yJCiAgICAgICAgICAgIHByaW50KGYi5Yib5bu656S65L6L5paH5Lu2OiB7aW5wdXRfZmlsZX0iKQogICAgICAgICAgICBzYW1wbGVfZGF0YSA9IFsKICAgICAgICAgICAgICAgIFsnZGV2aWNlX2lkJywgJ3N0YXJ0X3RpbWUnLCAnZW5kX3RpbWUnXSwKICAgICAgICAgICAgICAgIFsnRDAwMScsICcyMDIzLTA2LTAxIDA4OjMwOjAwJywgJzIwMjMtMDYtMDEgMTA6MTU6MDAnXSwKICAgICAgICAgICAgICAgIFsnRDAwMScsICcyMDIzLTA2LTAxIDE0OjAwOjAwJywgJzIwMjMtMDYtMDEgMTY6NDU6MDAnXSwKICAgICAgICAgICAgICAgIFsnRDAwMicsICcyMDIzLTA2LTAxIDA5OjAwOjAwJywgJzIwMjMtMDYtMDEgMTE6MzA6MDAnXSwKICAgICAgICAgICAgICAgIFsnRDAwMScsICcyMDIzLTA2LTAyIDEzOjMwOjAwJywgJzIwMjMtMDYtMDIgMTU6MDA6MDAnXSwKICAgICAgICAgICAgICAgIFsnRDAwMycsICcyMDIzLTA2LTAyIDEwOjAwOjAwJywgJyddLCAgIyDmnKrnu5PmnZ/nmoTkvb/nlKjorrDlvZUKICAgICAgICAgICAgXQogICAgICAgICAgICAKICAgICAgICAgICAgd2l0aCBvcGVuKGlucHV0X2ZpbGUsICd3JywgZW5jb2Rpbmc9J3V0Zi04JywgbmV3bGluZT0nJykgYXMgZjoKICAgICAgICAgICAgICAgIHdyaXRlciA9IGNzdi53cml0ZXIoZikKICAgICAgICAgICAgICAgIHdyaXRlci53cml0ZXJvd3Moc2FtcGxlX2RhdGEpCiAgICAgICAgICAgIHByaW50KCLlt7LliJvlu7rnpLrkvovmlbDmja7mlofku7bvvIznqIvluo/lsIbkvb/nlKjmraTmlofku7bov5DooYwiKQogICAgICAgIAogICAgICAgICMg5omn6KGM57uf6K6hCiAgICAgICAgY2FsY3VsYXRlX2RldmljZV91c2FnZShpbnB1dF9maWxlLCBvdXRwdXRfZmlsZSkKICAgIAogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHByaW50KGYi56iL5bqP6L+Q6KGM5pe25Ye66ZSZOiB7c3RyKGUpfSIpCiAgICAgICAgcHJpbnQoIuivt+ajgOafpei+k+WFpeaWh+S7tuagvOW8j+aYr+WQpuato+ehriIpCiAgICAgICAgcHJpbnQoIuaWh+S7tuagvOW8j+imgeaxgjoiKQogICAgICAgIHByaW50KCIxLiBDU1bmoLzlvI/vvIzljIXlkKvooajlpLQ6IGRldmljZV9pZCxzdGFydF90aW1lLGVuZF90aW1lIikKICAgICAgICBwcmludCgiMi4g5pe26Ze05qC85byPOiBZWVlZLU1NLUREIEhIOk1NOlNTIikKICAgICAgICBwcmludCgiMy4g56m657uT5p2f5pe26Ze06KGo56S66K6+5aSH5LuN5Zyo6L+Q6KGMIik=