import csv
import os
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. 空结束时间表示设备仍在运行")
aW1wb3J0IGNzdgppbXBvcnQgb3MKZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUKCmRlZiBjYWxjdWxhdGVfZGV2aWNlX3VzYWdlKGlucHV0X2ZpbGUsIG91dHB1dF9maWxlKToKICAgICIiIgogICAg57uf6K6h5pm66IO96K6+5aSH5L2/55So5pe26Ze0CiAgICA6cGFyYW0gaW5wdXRfZmlsZTog6L6T5YWlQ1NW5paH5Lu26Lev5b6ECiAgICA6cGFyYW0gb3V0cHV0X2ZpbGU6IOi+k+WHukNTVuaWh+S7tui3r+W+hAogICAgIiIiCiAgICAjIOajgOafpei+k+WFpeaWh+S7tuaYr+WQpuWtmOWcqAogICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKGlucHV0X2ZpbGUpOgogICAgICAgIHJhaXNlIEZpbGVOb3RGb3VuZEVycm9yKGYi6L6T5YWl5paH5Lu25LiN5a2Y5ZyoOiB7aW5wdXRfZmlsZX0iKQogICAgCiAgICAjIOivu+WPluW5tuWkhOeQhuaVsOaNrgogICAgZGV2aWNlX2RhdGEgPSB7fQogICAgd2l0aCBvcGVuKGlucHV0X2ZpbGUsICdyJywgZW5jb2Rpbmc9J3V0Zi04JykgYXMgZjoKICAgICAgICByZWFkZXIgPSBjc3YuRGljdFJlYWRlcihmKQogICAgICAgIGZvciByb3dfbnVtLCByb3cgaW4gZW51bWVyYXRlKHJlYWRlciwgMSk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIGRldmljZV9pZCA9IHJvd1snZGV2aWNlX2lkJ10KICAgICAgICAgICAgICAgIHN0YXJ0ID0gZGF0ZXRpbWUuZnJvbWlzb2Zvcm1hdChyb3dbJ3N0YXJ0X3RpbWUnXSkKICAgICAgICAgICAgICAgIGVuZCA9IGRhdGV0aW1lLmZyb21pc29mb3JtYXQocm93WydlbmRfdGltZSddKSBpZiByb3dbJ2VuZF90aW1lJ10gZWxzZSBkYXRldGltZS5ub3coKQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICBpZiBkZXZpY2VfaWQgbm90IGluIGRldmljZV9kYXRhOgogICAgICAgICAgICAgICAgICAgIGRldmljZV9kYXRhW2RldmljZV9pZF0gPSBbXQogICAgICAgICAgICAgICAgZGV2aWNlX2RhdGFbZGV2aWNlX2lkXS5hcHBlbmQoKHN0YXJ0LCBlbmQpKQogICAgICAgICAgICBleGNlcHQgKEtleUVycm9yLCBWYWx1ZUVycm9yKSBhcyBlOgogICAgICAgICAgICAgICAgcHJpbnQoZiLorablkYo6IOesrCB7cm93X251bX0g6KGM5pWw5o2u5qC85byP6ZSZ6K+vIC0ge3N0cihlKX0iKQogICAgICAgICAgICAgICAgY29udGludWUKICAgIAogICAgIyDlpoLmnpzmsqHmnInmib7liLDmnInmlYjmlbDmja4KICAgIGlmIG5vdCBkZXZpY2VfZGF0YToKICAgICAgICBwcmludCgi6ZSZ6K+vOiDovpPlhaXmlofku7bkuK3msqHmnInmib7liLDmnInmlYjmlbDmja4iKQogICAgICAgIHJldHVybgogICAgCiAgICAjIOiuoeeul+avj+S4quiuvuWkh+eahOaAu+S9v+eUqOaXtumXtO+8iOenku+8iQogICAgcmVzdWx0cyA9IFtdCiAgICBmb3IgZGV2aWNlX2lkLCBwZXJpb2RzIGluIGRldmljZV9kYXRhLml0ZW1zKCk6CiAgICAgICAgIyDmjInlvIDlp4vml7bpl7TmjpLluo8KICAgICAgICBwZXJpb2RzLnNvcnQoKQogICAgICAgIAogICAgICAgICMg5ZCI5bm26YeN5Y+g5pe26Ze05q61CiAgICAgICAgbWVyZ2VkID0gW10KICAgICAgICBjdXJyZW50X3N0YXJ0LCBjdXJyZW50X2VuZCA9IHBlcmlvZHNbMF0KICAgICAgICBmb3Igc3RhcnQsIGVuZCBpbiBwZXJpb2RzWzE6XToKICAgICAgICAgICAgaWYgc3RhcnQgPD0gY3VycmVudF9lbmQ6CiAgICAgICAgICAgICAgICBjdXJyZW50X2VuZCA9IG1heChjdXJyZW50X2VuZCwgZW5kKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgbWVyZ2VkLmFwcGVuZCgoY3VycmVudF9zdGFydCwgY3VycmVudF9lbmQpKQogICAgICAgICAgICAgICAgY3VycmVudF9zdGFydCwgY3VycmVudF9lbmQgPSBzdGFydCwgZW5kCiAgICAgICAgbWVyZ2VkLmFwcGVuZCgoY3VycmVudF9zdGFydCwgY3VycmVudF9lbmQpKQogICAgICAgIAogICAgICAgICMg6K6h566X5oC75pe26Ze077yI56eS77yJCiAgICAgICAgdG90YWxfc2Vjb25kcyA9IHN1bSgoZW5kIC0gc3RhcnQpLnRvdGFsX3NlY29uZHMoKSBmb3Igc3RhcnQsIGVuZCBpbiBtZXJnZWQpCiAgICAgICAgCiAgICAgICAgIyDovazmjaLkuLrlj6/or7vmoLzlvI8KICAgICAgICBob3VycywgcmVtYWluZGVyID0gZGl2bW9kKHRvdGFsX3NlY29uZHMsIDM2MDApCiAgICAgICAgbWludXRlcywgc2Vjb25kcyA9IGRpdm1vZChyZW1haW5kZXIsIDYwKQogICAgICAgIHJlYWRhYmxlID0gZiJ7aW50KGhvdXJzKX3lsI/ml7Z7aW50KG1pbnV0ZXMpfeWIhntpbnQoc2Vjb25kcyl956eSIgogICAgICAgIAogICAgICAgIHJlc3VsdHMuYXBwZW5kKHsKICAgICAgICAgICAgJ2RldmljZV9pZCc6IGRldmljZV9pZCwKICAgICAgICAgICAgJ3RvdGFsX3NlY29uZHMnOiB0b3RhbF9zZWNvbmRzLAogICAgICAgICAgICAncmVhZGFibGVfdGltZSc6IHJlYWRhYmxlCiAgICAgICAgfSkKICAgIAogICAgIyDlhpnlhaXnu5PmnpwKICAgIHdpdGggb3BlbihvdXRwdXRfZmlsZSwgJ3cnLCBlbmNvZGluZz0ndXRmLTgnLCBuZXdsaW5lPScnKSBhcyBmOgogICAgICAgIHdyaXRlciA9IGNzdi5EaWN0V3JpdGVyKGYsIGZpZWxkbmFtZXM9WydkZXZpY2VfaWQnLCAndG90YWxfc2Vjb25kcycsICdyZWFkYWJsZV90aW1lJ10pCiAgICAgICAgd3JpdGVyLndyaXRlaGVhZGVyKCkKICAgICAgICB3cml0ZXIud3JpdGVyb3dzKHJlc3VsdHMpCiAgICAKICAgIHByaW50KGYi57uf6K6h5a6M5oiQ77yB57uT5p6c5bey5L+d5a2Y6IezOiB7b3V0cHV0X2ZpbGV9IikKICAgIHByaW50KGYi5aSE55CG5LqGIHtsZW4ocmVzdWx0cyl9IOWPsOiuvuWkh+eahOS9v+eUqOiusOW9lSIpCgojIOS9v+eUqOekuuS+iwppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgdHJ5OgogICAgICAgICMg5pu/5o2i5Li65a6e6ZmF5paH5Lu26Lev5b6ECiAgICAgICAgaW5wdXRfZmlsZSA9ICJkZXZpY2VfbG9ncy5jc3YiICAjIOi+k+WFpeaWh+S7tuWQjQogICAgICAgIG91dHB1dF9maWxlID0gInVzYWdlX3JlcG9ydC5jc3YiICAjIOi+k+WHuuaWh+S7tuWQjQogICAgICAgIAogICAgICAgICMg5qOA5p+l6L6T5YWl5paH5Lu25piv5ZCm5a2Y5ZyoCiAgICAgICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKGlucHV0X2ZpbGUpOgogICAgICAgICAgICAjIOWIm+W7uuekuuS+i+aWh+S7tu+8iOS7heeUqOS6jua1i+ivle+8iQogICAgICAgICAgICBwcmludChmIuWIm+W7uuekuuS+i+aWh+S7tjoge2lucHV0X2ZpbGV9IikKICAgICAgICAgICAgc2FtcGxlX2RhdGEgPSBbCiAgICAgICAgICAgICAgICBbJ2RldmljZV9pZCcsICdzdGFydF90aW1lJywgJ2VuZF90aW1lJ10sCiAgICAgICAgICAgICAgICBbJ0QwMDEnLCAnMjAyMy0wNi0wMSAwODozMDowMCcsICcyMDIzLTA2LTAxIDEwOjE1OjAwJ10sCiAgICAgICAgICAgICAgICBbJ0QwMDEnLCAnMjAyMy0wNi0wMSAxNDowMDowMCcsICcyMDIzLTA2LTAxIDE2OjQ1OjAwJ10sCiAgICAgICAgICAgICAgICBbJ0QwMDInLCAnMjAyMy0wNi0wMSAwOTowMDowMCcsICcyMDIzLTA2LTAxIDExOjMwOjAwJ10sCiAgICAgICAgICAgICAgICBbJ0QwMDEnLCAnMjAyMy0wNi0wMiAxMzozMDowMCcsICcyMDIzLTA2LTAyIDE1OjAwOjAwJ10sCiAgICAgICAgICAgICAgICBbJ0QwMDMnLCAnMjAyMy0wNi0wMiAxMDowMDowMCcsICcnXSwgICMg5pyq57uT5p2f55qE5L2/55So6K6w5b2VCiAgICAgICAgICAgIF0KICAgICAgICAgICAgCiAgICAgICAgICAgIHdpdGggb3BlbihpbnB1dF9maWxlLCAndycsIGVuY29kaW5nPSd1dGYtOCcsIG5ld2xpbmU9JycpIGFzIGY6CiAgICAgICAgICAgICAgICB3cml0ZXIgPSBjc3Yud3JpdGVyKGYpCiAgICAgICAgICAgICAgICB3cml0ZXIud3JpdGVyb3dzKHNhbXBsZV9kYXRhKQogICAgICAgICAgICBwcmludCgi5bey5Yib5bu656S65L6L5pWw5o2u5paH5Lu277yM56iL5bqP5bCG5L2/55So5q2k5paH5Lu26L+Q6KGMIikKICAgICAgICAKICAgICAgICAjIOaJp+ihjOe7n+iuoQogICAgICAgIGNhbGN1bGF0ZV9kZXZpY2VfdXNhZ2UoaW5wdXRfZmlsZSwgb3V0cHV0X2ZpbGUpCiAgICAKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICBwcmludChmIueoi+W6j+i/kOihjOaXtuWHuumUmToge3N0cihlKX0iKQogICAgICAgIHByaW50KCLor7fmo4Dmn6XovpPlhaXmlofku7bmoLzlvI/mmK/lkKbmraPnoa4iKQogICAgICAgIHByaW50KCLmlofku7bmoLzlvI/opoHmsYI6IikKICAgICAgICBwcmludCgiMS4gQ1NW5qC85byP77yM5YyF5ZCr6KGo5aS0OiBkZXZpY2VfaWQsc3RhcnRfdGltZSxlbmRfdGltZSIpCiAgICAgICAgcHJpbnQoIjIuIOaXtumXtOagvOW8jzogWVlZWS1NTS1ERCBISDpNTTpTUyIpCiAgICAgICAgcHJpbnQoIjMuIOepuue7k+adn+aXtumXtOihqOekuuiuvuWkh+S7jeWcqOi/kOihjCIp