脚本如何计算连续打卡天数

wen 实用脚本 31

本文目录导读:

脚本如何计算连续打卡天数

  1. 纯前端 JavaScript 实现(本地存储)
  2. 后端 Python 实现
  3. 数据库查询(SQL)
  4. 考虑特殊情况的增强版本
  5. 完整的存储 + 计算方案
  6. 关键考虑因素

纯前端 JavaScript 实现(本地存储)

function calculateStreak(records) {
    if (records.length === 0) return 0;
    // 按日期降序排序
    records.sort((a, b) => new Date(b.date) - new Date(a.date));
    let streak = 1;
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    // 检查今天是否打卡
    const lastRecord = new Date(records[0].date);
    lastRecord.setHours(0, 0, 0, 0);
    const diffDays = Math.floor((today - lastRecord) / (1000 * 60 * 60 * 24));
    if (diffDays > 1) return 0; // 最后打卡超过1天,连续中断
    // 计算连续天数
    for (let i = 1; i < records.length; i++) {
        const currentDate = new Date(records[i].date);
        const prevDate = new Date(records[i - 1].date);
        const diff = Math.floor((prevDate - currentDate) / (1000 * 60 * 60 * 24));
        if (diff === 1) {
            streak++;
        } else {
            break;
        }
    }
    return streak;
}
// 使用示例
const records = [
    { date: '2024-01-10' },
    { date: '2024-01-09' },
    { date: '2024-01-08' },
    { date: '2024-01-06' }  // 中断
];
console.log(calculateStreak(records)); // 输出: 3

后端 Python 实现

from datetime import datetime, timedelta
from typing import List, Dict
def calculate_streak(records: List[Dict]) -> int:
    if not records:
        return 0
    # 提取日期并排序
    dates = [datetime.strptime(r['date'], '%Y-%m-%d').date() for r in records]
    dates.sort(reverse=True)
    # 检查今天是否打卡
    today = datetime.now().date()
    if dates[0] == today:
        streak = 1
    elif dates[0] == today - timedelta(days=1):
        streak = 1
    else:
        return 0  # 最后打卡不是今天或昨天
    # 计算连续天数
    for i in range(1, len(dates)):
        diff = (dates[i-1] - dates[i]).days
        if diff == 1:
            streak += 1
        else:
            break
    return streak
# 使用示例
records = [
    {'date': '2024-01-10'},
    {'date': '2024-01-09'},
    {'date': '2024-01-08'},
    {'date': '2024-01-07'}
]
print(calculate_streak(records))  # 输出: 4

数据库查询(SQL)

-- 假设打卡记录表: check_ins(user_id, check_date)
-- 计算用户连续打卡天数
WITH daily_check AS (
    SELECT 
        user_id,
        check_date,
        LAG(check_date) OVER (PARTITION BY user_id ORDER BY check_date DESC) as prev_date
    FROM check_ins
    WHERE user_id = 123
),
streak_calc AS (
    SELECT 
        *,
        CASE 
            WHEN prev_date IS NULL THEN 1
            WHEN DATEDIFF(prev_date, check_date) = 1 THEN 0
            ELSE 1
        END as is_break
    FROM daily_check
    ORDER BY check_date DESC
)
SELECT COUNT(*) as streak_days
FROM streak_calc
WHERE is_break = 0
LIMIT 1;

考虑特殊情况的增强版本

function calculateEnhancedStreak(records, options = {}) {
    const {
        allowTodayGap = true,    // 今天未打卡是否中断
        includeWeekends = true,   // 是否包含周末
        maxGap = 0               // 允许的最大间隔天数
    } = options;
    if (records.length === 0) return { streak: 0, isActive: false };
    // 排序并去重
    const dates = [...new Set(records.map(r => r.date))].sort();
    let streak = 1;
    let gaps = 0;
    for (let i = 1; i < dates.length; i++) {
        const curr = new Date(dates[i]);
        const prev = new Date(dates[i - 1]);
        const diff = Math.floor((curr - prev) / (1000 * 60 * 60 * 24));
        if (diff === 1) {
            streak++;
            gaps = 0;
        } else if (diff <= maxGap + 1) {
            // 允许有间隔
            streak++;
            gaps += diff - 1;
        } else {
            // 中断
            return { 
                streak: streak, 
                isActive: false,
                breakDate: dates[i]
            };
        }
    }
    return { streak, isActive: true, gaps };
}
// 使用示例
const records = [
    { date: '2024-01-10' },
    { date: '2024-01-09' },
    { date: '2024-01-07' },  // 跳过一天
    { date: '2024-01-06' }
];
console.log(calculateEnhancedStreak(records, { maxGap: 1 }));
// 输出: { streak: 4, isActive: true, gaps: 1 }

完整的存储 + 计算方案

class StreakTracker {
    constructor(storageKey = 'streak_data') {
        this.storageKey = storageKey;
        this.data = this.load();
    }
    // 打卡
    checkIn() {
        const today = new Date().toISOString().split('T')[0];
        if (!this.data.dates.includes(today)) {
            this.data.dates.push(today);
            this.save();
        }
        return this.calculateStreak();
    }
    // 计算连续天数
    calculateStreak() {
        const dates = [...this.data.dates].sort((a, b) => 
            new Date(b) - new Date(a)
        );
        if (dates.length === 0) return 0;
        let streak = 1;
        const today = new Date().toISOString().split('T')[0];
        // 检查最后打卡日期
        const lastDate = dates[0];
        const diff = Math.floor(
            (new Date(today) - new Date(lastDate)) / (1000 * 60 * 60 * 24)
        );
        if (diff > 1) return 0; // 中断
        // 计算连续
        for (let i = 1; i < dates.length; i++) {
            const prevDate = dates[i - 1];
            const currDate = dates[i];
            const diff = Math.floor(
                (new Date(prevDate) - new Date(currDate)) / (1000 * 60 * 60 * 24)
            );
            if (diff === 1) {
                streak++;
            } else {
                break;
            }
        }
        return streak;
    }
    load() {
        const data = localStorage.getItem(this.storageKey);
        return data ? JSON.parse(data) : { dates: [] };
    }
    save() {
        localStorage.setItem(this.storageKey, JSON.stringify(this.data));
    }
    // 获取打卡统计
    getStats() {
        return {
            streak: this.calculateStreak(),
            totalCheckins: this.data.dates.length,
            lastCheckin: this.data.dates[this.data.dates.length - 1] || null,
            dates: this.data.dates
        };
    }
}
// 使用示例
const tracker = new StreakTracker();
tracker.checkIn();           // 打卡
console.log(tracker.getStats()); // 获取统计

关键考虑因素

  1. 时间处理:注意时区问题,使用 UTC 或统一时区
  2. 边界情况:今天未打卡、昨天未打卡等情况
  3. 数据去重:同一天多次打卡应只算一次
  4. 性能优化:大量数据时考虑使用缓存

选择哪种实现取决于你的具体场景:前端、后端、还是数据库层面。

抱歉,评论功能暂时关闭!