JavaScript实现基于工作日和工作时间的精确计数器

javascript实现基于工作日和工作时间的精确计数器

本教程详细介绍了如何使用JavaScript创建一个高级计数器,该计数器能按分钟递增,并仅在指定的工作日(周一至周五)和工作时间(上午6点至晚上8点)内进行计数。文章将深入探讨如何准确计算初始值,包括过去工作日的分钟数和当前工作日已过的分钟数,同时确保在非工作时间或非工作日暂停计数但仍显示当前值,并在每月初自动重置。

引言:构建智能计数器的挑战

在许多应用场景中,我们需要实现一个能够根据特定规则进行计数的计时器。例如,一个项目进度跟踪器可能只需要在工作日的工作时间内累积时间。这要求计数器不仅能按设定的频率(如每分钟)递增,还需要智能地识别当前时间是否符合计数条件,并在不符合条件时暂停计数但仍显示当前值,同时在每月初自动重置。

实现这样一个计数器面临的主要挑战包括:

精确的时间判断: 准确识别当前是工作日还是周末,以及是否处于指定的工作时间段内。初始值计算: 在页面加载或功能启动时,如何根据当前日期和时间,以及历史已过的工作时间,计算出正确的初始计数器值。这包括计算当前月份中已过去的所有工作日的总分钟数,以及当前工作日中已过的分钟数。动态暂停与显示: 在非工作时间或非工作日,计数器应停止递增,但其当前值仍需持续显示。每月自动重置: 确保计数器在每个月的第一天(如果是工作日)从零开始重新计数。

核心概念与参数配置

为了实现上述功能,我们首先需要定义一些核心参数并获取当前的时间状态:

function startCounter() {    // 获取当前日期和时间    var currentDate = new Date();    var currentDay = currentDate.getDay(); // 0: 星期日, 1: 星期一, ..., 6: 星期六    var currentHour = currentDate.getHours();    var currentMinutes = currentDate.getMinutes();    var currentDayOfMonth = currentDate.getDate(); // 当前月份的日期 (1-31)    // 定义工作日和工作时间参数    var weekEndDays = [0, 6]; // 星期日和星期六为周末    var startHour = 6; // 工作开始时间 (上午6点)    var endHour = 20; // 工作结束时间 (晚上8点,不包含20点)    // 判断当前是否为周末或非工作时间    var isWeekend = weekEndDays.indexOf(currentDay) > -1;    var isOutsideWorkingHours = currentHour = endHour;

在这里:

立即学习“Java免费学习笔记(深入)”;

weekEndDays 数组可以灵活配置周末。startHour 和 endHour 定义了每日的工作时间范围。请注意,endHour 表示的时刻是工作时间结束的边界,例如 endHour = 20 意味着工作时间截止到19:59,20:00即为非工作时间。

精确计算计数器初始值

计数器的核心在于其初始值的准确性。当页面加载或功能启动时,我们需要计算出当前月份中,直到当前时刻为止,所有符合工作日和工作时间条件的累积分钟数。这分为两个主要部分:计算已过去的工作天数,以及计算当前工作日已过的分钟数。

1. 计算已过去的工作天数 (daysSinceStart)

我们需要遍历当前月份中从1号到当前日期前一天的所有日期,判断它们是否为工作日。

    // 计算从本月1号到当前日期前一天,已过去的工作天数    var daysSinceStart = 0;    for (let i = 1; i < currentDayOfMonth; i++) {        let checkDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), i);        if (weekEndDays.indexOf(checkDate.getDay()) === -1) { // 如果不是周末            daysSinceStart++;        }    }

这段代码确保了我们准确地统计了当前月份中,截至当前日期的前一天,所有符合工作日条件的完整天数。

2. 处理当前日期的情况

当前日期的情况相对复杂:

如果当前是工作日,并且已经过了工作时间(例如,现在是周二晚上9点),那么当前这一天也应该被视为一个完整的已过去的工作日。如果当前是工作日,并且处于工作时间内,我们需要计算从工作时间开始到当前分钟已过去的时间。如果当前是周末或非工作时间,那么 minutesSinceStartOfDay 应为0。

    var minutesSinceStartOfDay = 0;    // 如果今天是工作日,且当前时间已超过工作结束时间,则将今天计为一个完整的工作日    if (!isWeekend && isOutsideWorkingHours && currentHour >= endHour) {        daysSinceStart++;    }    // 如果当前是工作日且处于工作时间内,计算当天已过的分钟数    else if (!isWeekend && !isOutsideWorkingHours) {        minutesSinceStartOfDay = (currentHour - startHour) * 60 + currentMinutes;        console.log('Counter starting...');    } else {        console.log('Counter paused.');    }

3. 整合计算初始值

一旦 daysSinceStart 和 minutesSinceStartOfDay 计算完毕,我们就可以确定计数器的初始值:

    // 计算每个工作日总分钟数    var minutesPerWorkingDay = (endHour - startHour) * 60;    // 计算计数器初始值    var counter = daysSinceStart * minutesPerWorkingDay + minutesSinceStartOfDay;

这里的 minutesPerWorkingDay 是一个关键常量,代表一个完整工作日(6 AM 到 8 PM 之间,即14小时)的分钟数,即 (20 – 6) * 60 = 840 分钟。

实时计数与暂停逻辑

计数器的实时递增是通过 setInterval 实现的。关键在于在每次递增前,都需要重新判断当前时间是否符合计数条件。

    // 启动计数器,每分钟递增    var intervalId = setInterval(function() {        // 在每次递增前重新获取当前时间状态        currentDate = new Date();        currentDay = currentDate.getDay();        currentHour = currentDate.getHours();        currentMinutes = currentDate.getMinutes();        isWeekend = weekEndDays.indexOf(currentDay) > -1;        isOutsideWorkingHours = currentHour = endHour;        if (isWeekend || isOutsideWorkingHours) {            // 如果是周末或非工作时间,不递增,但仍显示当前值            console.log('Counter:', counter, '(Paused)');            return; // 暂停递增        }        counter++; // 符合条件时递增        console.log('Counter:', counter);    }, 60000); // 60000毫秒 = 1分钟    // 页面加载时显示初始值    console.log('Counter:', counter);}// 启动计数器startCounter();

这里的核心改进是:

在 setInterval 的回调函数内部,每次执行时都重新获取当前的日期和时间,并重新判断 isWeekend 和 isOutsideWorkingHours。这确保了在跨越工作时间边界时,计数器能即时响应。当 isWeekend 或 isOutsideWorkingHours 为真时,不再使用 return 语句直接退出 startCounter 函数,而是仅退出 setInterval 的当前回调,这样计数器的值仍然可以被 console.log 打印出来,实现了“暂停计数但仍显示值”的需求。

每月重置机制

本方案通过将计数器的初始值计算基于当前月份的1号,天然地实现了每月重置功能。当进入一个新的月份时,currentDate.getMonth() 会发生变化,从而 daysSinceStart 的计算会从新月份的1号重新开始,达到自动重置的效果。

完整代码示例

将以上所有部分整合,形成一个完整的、健壮的JavaScript计数器实现:

function startCounter() {    // 获取当前日期和时间    var currentDate = new Date();    var currentDay = currentDate.getDay(); // 0: 星期日, 1: 星期一, ..., 6: 星期六    var currentHour = currentDate.getHours();    var currentMinutes = currentDate.getMinutes();    var currentDayOfMonth = currentDate.getDate(); // 当前月份的日期 (1-31)    // 定义工作日和工作时间参数    var weekEndDays = [0, 6]; // 星期日和星期六为周末    var startHour = 6; // 工作开始时间 (上午6点)    var endHour = 20; // 工作结束时间 (晚上8点,不包含20点)    // 判断当前是否为周末或非工作时间    var isWeekend = weekEndDays.indexOf(currentDay) > -1;    var isOutsideWorkingHours = currentHour = endHour;    // 计算从本月1号到当前日期前一天,已过去的工作天数    var daysSinceStart = 0;    for (let i = 1; i = endHour) {        daysSinceStart++;    }    // 如果当前是工作日且处于工作时间内,计算当天已过的分钟数    else if (!isWeekend && !isOutsideWorkingHours) {        minutesSinceStartOfDay = (currentHour - startHour) * 60 + currentMinutes;        console.log('Counter starting...');    } else {        console.log('Counter paused.');    }    // 计算每个工作日总分钟数    var minutesPerWorkingDay = (endHour - startHour) * 60; // 14小时 * 60分钟 = 840分钟    // 计算计数器初始值    var counter = daysSinceStart * minutesPerWorkingDay + minutesSinceStartOfDay;    // 启动计数器,每分钟递增    var intervalId = setInterval(function() {        // 在每次递增前重新获取当前时间状态        currentDate = new Date();        currentDay = currentDate.getDay();        currentHour = currentDate.getHours();        currentMinutes = currentDate.getMinutes(); // 重新获取分钟数,确保跨分钟时判断准确        isWeekend = weekEndDays.indexOf(currentDay) > -1;        isOutsideWorkingHours = currentHour = endHour;        if (isWeekend || isOutsideWorkingHours) {            // 如果是周末或非工作时间,不递增,但仍显示当前值            console.log('Counter:', counter, '(Paused)');            return; // 暂停递增        }        counter++; // 符合条件时递增        console.log('Counter:', counter);    }, 60000); // 60000毫秒 = 1分钟    // 页面加载时显示初始值    console.log('Counter:', counter);}// 启动计数器startCounter();

注意事项与优化建议

setInterval 的精度: setInterval 并非绝对精确,它只能保证在指定的时间间隔后将任务放入执行队列,实际执行时间可能会因浏览器负载、CPU调度等因素而略有延迟。对于需要极高精度的计时场景,应考虑使用服务器端时间戳或更复杂的同步机制页面刷新与持久化: 当前方案在页面刷新时会根据当前的日期和时间重新计算计数器的初始值,实现了“断点续计”的效果。如果需要跨浏览器会话或设备持久化计数器状态,可以考虑将 counter 的值存储到 localStorage 中,并在 startCounter 函数开始时尝试读取,或者将计数逻辑放在后端服务器。时区考量: Date 对象在客户端运行时会受用户本地时区影响。如果您的应用需要全球用户统一的计数标准,建议将时间计算逻辑放在服务器端,或在客户端明确指定时区。可维护性: 可以将 60000 这样的“魔术数字”定义为常量(例如 const ONE_MINUTE_MS = 60 * 1000;),提高代码的可读性和可维护性。错误处理: 对于生产环境的应用,可能需要添加额外的错误处理机制,例如检查 Date 对象是否有效等。

通过以上详细的步骤和代码实现,您可以构建一个功能完善、逻辑清晰的JavaScript计数器,满足在特定工作日和工作时间内进行计数的复杂需求。

以上就是JavaScript实现基于工作日和工作时间的精确计数器的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1516470.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 10:04:35
下一篇 2025年12月20日 10:04:52

相关推荐

  • AG Grid 固定列宽度限制与横向滚动实现教程

    本教程旨在解决AG Grid中固定(pinned)列过多导致非固定列被遮挡的问题。通过一种“非官方”的DOM操作、事件监听及CSS覆盖方案,实现固定列区域的宽度限制和横向滚动,确保用户始终能访问所有数据。该方案适用于特定场景,尤其与AG Grid分页功能结合使用效果更佳,但需注意其潜在的兼容性风险。…

    2025年12月20日
    000
  • iframe 内容刷新不重置:实现持久化导航状态的教程

    本教程详细讲解如何在网页刷新后保持 iframe 内部的导航状态不被重置。我们将探讨两种主要方法:通过 sessionStorage 或 localStorage 手动存储和恢复 iframe 的 URL,以及更推荐的利用父页面 URL 路由(history.pushState)来序列化 ifram…

    2025年12月20日
    000
  • 解决Iframe刷新后内容重置问题:持久化内部导航状态

    本文旨在解决Iframe在父页面刷新后其内部导航状态丢失,导致内容重置回初始src的问题。我们将探讨两种主要策略:一是利用浏览器存储(如sessionStorage或localStorage)手动保存并恢复Iframe的当前URL;二是结合父页面的路由机制,通过history.pushState()…

    2025年12月20日
    000
  • iFrame状态持久化:刷新后保持内部导航位置的实现策略

    当页面刷新时,iFrame内部的导航状态通常会重置回其初始src链接,导致用户体验不佳。本文将深入探讨如何解决iFrame在页面刷新后无法保持其内部链接的问题,提供两种主要的解决方案:基于客户端存储进行状态恢复,以及通过父页面URL同步iFrame状态。通过详细的实现步骤、代码示例和注意事项,帮助开…

    2025年12月20日
    000
  • AG Grid 固定列最大宽度与滚动优化教程

    本教程旨在解决AG Grid中固定列过多导致非固定列数据被遮挡的问题。由于AG Grid核心功能缺乏直接解决方案,本文将介绍一种通过DOM操作、自定义容器包裹、事件监听实现滚动同步以及CSS样式覆盖的非标准方法。该方案能为固定列设置最大宽度并使其可水平滚动,同时保持与非固定列的协调,但需注意其“ha…

    2025年12月20日
    000
  • 动态HTML表格行中输入字段的联动自动填充教程

    本教程详细阐述了如何在动态生成的HTML表格行中实现输入字段的联动自动填充。通过摒弃对固定ID的依赖,转而利用CSS类和事件上下文传递,我们能够确保JavaScript函数准确地操作当前行内的元素,从而实现高效、可扩展的数据填充逻辑,尤其适用于需要重复创建相同结构元素的场景。 动态HTML表格行中的…

    2025年12月20日
    000
  • PHP循环中JavaScript代码去重与高效事件处理指南

    本教程旨在解决PHP循环中嵌入大量重复JavaScript代码的问题,这些代码通常用于处理动态生成元素的交互。我们将介绍如何通过事件委托机制、统一使用CSS类而非动态ID,并结合event.target、closest和querySelector等现代DOM操作方法,实现JavaScript代码的去…

    2025年12月20日
    000
  • PHP循环中动态生成JavaScript代码的优化策略

    本文旨在解决PHP循环中嵌入重复JavaScript代码导致的维护性与性能问题。通过引入事件委托机制,结合类选择器和DOM遍历方法(如event.target、closest、querySelector),实现JavaScript代码的集中管理和复用。教程将详细阐述如何重构HTML结构和JavaSc…

    2025年12月20日
    000
  • Nuxt useFetch 数据即时访问指南:SSR影响与解决方案

    本文深入探讨了Nuxt useFetch 在客户端生命周期钩子中数据访问延迟或返回null/proxy对象的问题。主要原因在于Nuxt默认的服务器端渲染(SSR)机制。教程提供了两种核心解决方案:一是通过routeRules禁用特定路由的SSR以实现客户端即时数据获取;二是在保持SSR的同时,利用u…

    2025年12月20日
    000
  • Nuxt useFetch 数据访问延迟问题及解决方案:SSR与拦截器深度解析

    本文深入探讨Nuxt useFetch 在获取API数据时,response.data.value 初期显示 null 的常见问题。该现象通常与Nuxt的默认服务器端渲染(SSR)行为有关。文章提供了三种有效的解决方案:通过 routeRules 关闭特定路由的SSR,利用 useFetch 的 o…

    2025年12月20日
    000
  • Iframe内容持久化:刷新后保持嵌入页面状态的策略与实践

    本教程详细探讨了如何解决HTML Iframe内容重置的原理 核心策略一:基于本地存储的状态持久化 这种方法的核心思想是:在 实现步骤 监听Iframe导航事件 同源策略限制: 这一点至关重要。如果 跨域情况: 如果 同源示例(假设iframe_a是同源): const iframe = docum…

    2025年12月20日
    000
  • 在GitHub上更新JSON文件:理解限制与正确方法

    本文旨在阐明通过客户端JavaScript直接修改GitHub上静态JSON文件的局限性,特别是涉及CORS策略的POST请求失败问题。我们将探讨为何这种直接操作不可行,介绍GitHub官方API作为文件更新途径,并最终推荐使用后端服务结合数据库的专业解决方案,以确保数据操作的安全性和可持续性。 1…

    2025年12月20日
    000
  • JavaScript实现HTML表格多列实时搜索功能

    本教程将指导您如何使用现代JavaScript和DOM操作,为HTML表格实现高效的多列实时搜索功能。通过利用forEach循环和Array.prototype.some()方法,我们将优化传统单列搜索逻辑,使用户能够同时在表格的所有列中查找匹配项,从而显著提升数据筛选的用户体验。 挑战:传统单列搜…

    2025年12月20日
    000
  • 在GitHub上安全更新JSON文件:理解CORS限制与API应用实践

    本文深入探讨了在GitHub上直接通过客户端JavaScript修改JSON文件时遇到的CORS错误,并解释了其背后的安全原理。我们将介绍两种正确的解决方案:利用GitHub REST API进行文件内容管理,以及更健壮的后端服务与数据库方案,旨在帮助开发者理解并实践安全有效的数据更新策略。 问题剖…

    2025年12月20日
    000
  • Puppeteer 爬取网页数据返回空数组问题解决方案

    本文针对使用 Puppeteer 爬取 naamhinaam.com 网站数据时,出现返回空数组的问题,提供了一套可行的解决方案。通过分析问题代码,找出选择器和循环逻辑上的错误,并提供优化后的代码示例,确保能正确抓取网页上的婴儿名字和含义信息,并避免因广告元素干扰导致的问题。本文还强调了headle…

    2025年12月20日
    000
  • 使用 Puppeteer 抓取网页数据返回空数组的解决方案

    本文旨在解决在使用 Puppeteer 抓取网页数据时,遇到返回空数组的问题。通过分析问题代码,找到导致问题的原因,并提供修正后的代码示例,确保能正确抓取目标网站的数据,并清晰地呈现抓取结果。本文重点关注选择器的使用和异步操作的处理,帮助开发者避免类似问题。 在使用 Puppeteer 进行网页数据…

    2025年12月20日
    000
  • 使用 Puppeteer 抓取网页数据返回空数组问题的解决方案

    本文旨在解决在使用 Puppeteer 抓取网页数据时,遇到返回空数组的问题。通过分析常见原因,并提供优化后的代码示例,帮助开发者更有效地抓取目标网站的数据,并避免抓取结果为空的情况。本文将重点关注选择器优化、页面元素加载以及数据提取等关键环节。 问题分析 在使用 Puppeteer 进行网页数据抓…

    2025年12月20日
    000
  • 什么是CommonJS和ES模块?

    CommonJS采用同步加载和值拷贝,模块导出的是静态值;ES模块支持异步加载和动态引用,导出绑定保持实时更新,两者在加载机制、缓存策略及变量绑定上存在本质差异。 CommonJS和ES模块是JavaScript中两种主要的模块化规范,它们定义了代码如何被组织、导入和导出。CommonJS主要用于N…

    2025年12月20日
    000
  • 浏览器JS线程模型是什么?

    JavaScript在浏览器中是单线程的,通过事件循环机制实现异步非阻塞操作。主线程负责执行JS代码、渲染页面和处理用户交互,为避免DOM操作冲突,一次只能执行一个任务。耗时操作由浏览器的Web APIs处理,完成后将回调放入任务队列。事件循环在执行栈空闲时,优先执行微任务队列中的任务(如Promi…

    2025年12月20日
    000
  • 如何调试第三方库问题?

    答案是调试第三方库需通过复现隔离、查阅文档、分析堆栈、使用调试器和日志等手段定位问题,针对无源码库可采用反编译、抓包、行为分析等方式,当问题严重、社区活跃且具备修复能力时,应贡献代码而非仅用临时方案。 调试第三方库问题,核心在于隔离、定位和理解。这通常意味着你需要从纷繁复杂的外部依赖中抽丝剥茧,找到…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信