详解js中常规日期格式处理、月历渲染和倒计时函数


Posted in Javascript onDecember 28, 2016

前言

相信大家都知道日期格式处理在前端的日常任务中非常常见,但是为此引入monent.js这样的类库又会显得有点臃肿,毕竟我们一个特定的项目中,并不需要monent.js那么全的涵盖范围。另外,如果现在公司让你自己手写一个日历组件(月历、周历),日历组件中需要耦合我们的其他业务需求,如果有一个任务列表,当11月22号的待进行任务,我需要在日历上有一个绿色圆点,表示当天有待办事项。

下面介绍一些常规的函数,希望对大家有用。

月历效果图

详解js中常规日期格式处理、月历渲染和倒计时函数

函数目录

  1. getFormatDateStr 获得指定日期格式的字符串;
  2. getDayPrevAfter 获得n天前/后的日期;
  3. formatDateWithTimeZone 格式化日期带时区,ISO 8601;
  4. countDownBySecond 倒计时;
  5. monthSize 获得指定日期所在月的天数;
  6. getCalendarMonth 获得指定日期所在月的第一周到第四/五周的数据组合;
  7. getOneDateWeekIndex 获得指定的某天所在该月的第几周,下标从0开始;

下面是详细的说明:

getFormatDateStr

/**
 * [zeroPadding 小于10的数字补0,必填]
 * @param {[Int]} value [description]
 * @return {[String]} [description]
 */
export function zeroPadding(value){
 return value < 10 ? `0${value}` : value;
}

/**
 * [_isDateStrSeparatorCh 判断日期格式字符串的分隔符是否是中文]
 * @param {[String]} str [必填]
 * @return {[String]} [分隔符]
 */
function _getDateStrSeparator(str, startIndex, endIndex) {
 startIndex = startIndex ? startIndex : 4;
 endIndex = endIndex ? endIndex : 5;
 let separator = str.slice(startIndex, endIndex);
 if (separator === '年' || separator === '月' ) {
 separator = 'Ch';
 }
 return separator;
}

/**
 * [_isDateStrSeparatorCh 判断日期格式字符串的分隔符是否是中文]
 * @param {[String]} str [必填]
 * @return {[String]} [分隔符]
 */
function _isDateStrSeparatorCh(str) {
 if ( str.indexOf('年')!=-1 || str.indexOf('月')!=-1 ) {
 return true;
 }
 return false;
}

/**
 * [getFormatDateStr 获得指定日期格式的字符串]
 * @param {[String or Date]} date [要转换的日期,必填]
 * @param {[String]} dateFormatStr [要转化的目标格式,必填,2016-11-22之间的分隔符可任意,可选项:
 * 'yyyy-mm-dd hh:mm:ss','yyyy/mm/dd hh:mm:ss','yyyy.mm.dd hh:mm:ss','yyyy年mm月dd hh:mm:ss',
 * 'yyyy-mm-dd hh:mm',
 * 'mm-dd hh:mm',
 * 'yyyy-mm-dd',
 * 'mm-dd',
 * 'hh:mm:ss',
 * 'hh:mm'
 * ]
 * @return {[String]}  [时间格式字符串]
 */
export function getFormatDateStr(date, dateFormatStr) {

 if ( !(date instanceof Date) ) {
 if ( date.indexOf('-') != -1 ) {
 date.replace(/\-/g,'/');
 }
  date = new Date(date);
 }

 dateFormatStr = dateFormatStr.toLowerCase();
 if (!dateFormatStr){
 return false;
 }

 let returnStr = '',
 separator = _getDateStrSeparator(dateFormatStr),
 year = date.getFullYear(),
  month = date.getMonth() + 1,
  day = date.getDate(),
  hour = date.getHours(),
  minute = date.getMinutes(),
 second = date.getSeconds();

 if ( /^yyyy(.{1})mm(.{1})dd hh:mm:ss$/.test(dateFormatStr) ) {
 if (_isDateStrSeparatorCh(dateFormatStr)) {
 returnStr = `${year}年${zeroPadding(month)}月${zeroPadding(day)}日`;
 } else {
 separator =
 returnStr = `${year}${separator}${zeroPadding(month)}${separator}${zeroPadding(day)}`;
 }
 returnStr += ` ${zeroPadding(hour)}:${zeroPadding(minute)}:${zeroPadding(second)}`;
 } else if ( /^yyyy(.{1})mm(.{1})dd hh:mm$/.test(dateFormatStr) ) {
 if (_isDateStrSeparatorCh(dateFormatStr)) {
 returnStr = `${year}年${zeroPadding(month)}月${zeroPadding(day)}日`;
 } else {
 returnStr = `${year}${separator}${zeroPadding(month)}${separator}${zeroPadding(day)}`;
 }
 returnStr += ` ${zeroPadding(hour)}:${zeroPadding(minute)}`;
 } else if ( /^mm(.{1})dd hh:mm$/.test(dateFormatStr) ) {
 if (_isDateStrSeparatorCh(dateFormatStr)) {
 returnStr = `${zeroPadding(month)}月${zeroPadding(day)}日`;
 } else {
 separator = _getDateStrSeparator(dateFormatStr, 2, 3);
 returnStr = `${zeroPadding(month)}${separator}${zeroPadding(day)}`;
 }
 returnStr += ` ${zeroPadding(hour)}:${zeroPadding(minute)}`;
 } else if ( /^yyyy(.{1})mm(.{1})dd$/.test(dateFormatStr) ) {
 if (_isDateStrSeparatorCh(dateFormatStr)) {
 returnStr = `${year}年${zeroPadding(month)}月${zeroPadding(day)}日`;
 } else {
 returnStr = `${year}${separator}${zeroPadding(month)}${separator}${zeroPadding(day)}`;
 }
 } else if ( /^mm(.{1})dd$/.test(dateFormatStr) ) {
 if (_isDateStrSeparatorCh(dateFormatStr)) {
 returnStr = `${zeroPadding(month)}月${zeroPadding(day)}日`;
 } else {
 separator = _getDateStrSeparator(dateFormatStr, 2, 3);
 returnStr = `${zeroPadding(month)}${separator}${zeroPadding(day)}`;
 }
 } else if ( /^hh:mm:ss$/.test(dateFormatStr) ) {
 returnStr = `${zeroPadding(hour)}:${zeroPadding(minute)}:${zeroPadding(second)}`;
 } else if ( /^hh:mm$/.test(dateFormatStr) ) {
 returnStr = `${zeroPadding(hour)}:${zeroPadding(minute)}`;
 }

 return returnStr;

}

getDayPrevAfter

/**
 * [getDayPrevAfter 获得n天前/后的日期]
 * @param {[String]} date [日期,非必填参数,表示调用时的时间]
 * @param {[String]} type [前一天还是后一天,非必填参数,默认后一天]
 * @param {[Int]} daysNum [天数,非必填参数,默认一天]
 * @return {[Date]}  [description]
 */
export function getDayPrevAfter(date, type, daysNum) {

 date = date ? date : new Date();
 type = type ? type : 'after';
 daysNum = daysNum ? daysNum : 1;

 if ( !(date instanceof Date) ) {
 if ( date.indexOf('-') != -1 ) {
 date.replace(/\-/g,'/');
 }
 date = new Date(date);
 }

 let returnDate = date;
 if (type === 'prev') {
 returnDate = new Date(date.getTime() - (daysNum * 24 * 60 * 60 * 1000));
 } else if (type === 'after') {
 returnDate = new Date(date.getTime() + (daysNum * 24 * 60 * 60 * 1000));
 }
 return returnDate;

}

formatDateWithTimeZone

/**
 * [formatDateWithTimeZone 格式化日期带时区,ISO 8601]
 * @param {[Date]} date [日期,非必填参数,表示调用时的时间]
 * @return {[String]} [ISO 8601格式的日期,example: 2016-11-21T14:09:15+08:00]
 */
export function formatDateWithTimeZone(date) {

 date = date ? date : new Date();
 if ( !(date instanceof Date) ) {
 if ( date.indexOf('-') != -1 ) {
 date.replace(/\-/g,'/');
 }
 date = new Date(date);
 }

 let tzo = -date.getTimezoneOffset(),
 dif = tzo >= 0 ? '+' : '-',
 pad = function (num) {
  let norm = Math.abs(Math.floor(num));
  return zeroPadding(norm);
 };
 return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}${dif}${pad(tzo / 60)}:${pad(tzo % 60)}`;

}

countDownBySecond

/**
 * [countDownBySecond 倒计时]
 * @param {[Int]} restSeconds [剩余秒数,必填]
 * @param {[Int]} timeInterval [时间间隔,非必填,默认1000ms]
 * @param {[Function]} func [每倒计时一次,就需要执行一次的回调函数名,非必填]
 * @param {[Function]} endFun [倒计时结束需要执行的函数名,非必填]
 * @return {[null]} [无返回值]
 */
export function countDownBySecond(restSeconds, timeInterval, func, endCallback) {
 let timer = null;
 let total = restSeconds;
 timeInterval = timeInterval ? timeInterval : 1000;
 timer = setInterval(function() {
 --total;
 if (total <= 0) {
  clearInterval(timer);
  endCallback && endCallback();
 }
 func && func(total);
 }, timeInterval);
}

monthSize

/**
 * [monthSize 获得指定日期所在月的天数]
 * @param {[Date]} oDate [指定的日期,非必填,默认为当天]
 * @return {[Int]} [总天数]
 */
function monthSize(oDate) {
 oDate = oDate ? oDate : new Date();
 let year = oDate.getFullYear(),
 month = oDate.getMonth(),
 _oDate = new Date();
 _oDate.setFullYear(year);
 _oDate.setMonth(month + 1, 0);
 return _oDate.getDate();
}

getCalendarMonth

/**
 * [getCalendarMonth 获得指定日期所在月的第一周到第四/五周的数据组合,形如:
 * [{
 "date": "2016/10/30", //日期字符串
 "dateNum": 30, //日
 "isCurMonth": false, //是否当前月
 "weekIndex": 0 //是本月的第几周,下标从0开始
 },{
 "date": "2016/10/31",
 "dateNum": 31,
 "isCurMonth": false,
 "weekIndex": 0
 },{
 "date": "2016/11/1",
 "dateNum": 1,
 "day": 2,
 "isCurMonth": true,
 "isToday": false,
 "weekIndex": 0
 }]
 ]
 * @param {[Date]} param [指定的日期,非必填,默认为当天]
 * @return {[Array]} [获得指定日期所在月的第一周到第四/五周的数据组合]
 */
export function getCalendarMonth(date) {
 date = date ? date : new Date();
 let y = date.getFullYear();
 let m = date.getMonth();
 let _m;
 let firstDay = new Date(y, m, 1).getDay(); //当月第一天 周期
 let days = monthSize(date);//当月天数
 let prevMonthDays = monthSize(new Date(y, m - 1));//上月天数
 let initPrevDay = prevMonthDays - firstDay;
 let lines = Math.ceil((firstDay + days) / 7);
 _m = new Array(lines * 7);
 let nextMonthDay = 0;

 for (let i = 0; i < _m.length; i++) {
 let weekIndex = parseInt(i / 7);
 if (i < firstDay) {
  let date = ++initPrevDay;
  if (m === 0 && date > 7) {
  _m[i] = {
   isCurMonth: false,
   dateNum: date,
   weekIndex,
   date: `${y - 1}/${12}/${date}`
  };
  } else {
  _m[i] = {
   isCurMonth: false,
   dateNum: date,
   weekIndex,
   date: `${y}/${m}/${date}`
  };
  }
 } else if (i >= (firstDay + days)) {
  let date = ++nextMonthDay;

  if (m === 11 && date <= 7) {
  _m[i] = {
   isCurMonth: false,
   dateNum: date,
   weekIndex,
   date: `${y + 1}/${1}/${date}`
  };
  } else {
  _m[i] = {
   isCurMonth: false,
   dateNum: date,
   weekIndex,
   date: `${y}/${m + 2}/${date}`
  };
  }
 } else {
  let _date = i - firstDay + 1;
  let today = new Date();
  let today_y = today.getFullYear();
  let today_m = today.getMonth();
  let today_d = today.getDate();
  let isToday = today_y === y && today_m === m && today_d === _date ? true : false;
  _m[i] = {
  dateNum: _date, //日期
  day: i % 7, //周期
  weekIndex,
  isCurMonth: true,
  isToday,
  date: `${y}/${m + 1}/${_date}`
  };
 }
 }
 return _m;
}

getOneDateWeekIndex

/**
 * [getOneDateWeekIndex 获得指定的某天所在该月的第几周,下标从0开始]
 * @param {[Date]} date [指定的日期,非必填,默认为当天]
 * @return {[Int]} [在该月的第几周]
 */
export function getOneDateWeekIndex(date) {
 date = date ? date : new Date();
 let monthDays = getCalendarMonth(date);
 let dateString = getFormatDateStr(date, '/', true, false, false);
 let returnDate = monthDays.filter(item => {
 return item.date === dateString;
 });
 let weekIndex = returnDate[0].weekIndex;
 return weekIndex ? weekIndex : 0;
}

总结

以上就是这篇文章的全部内容了,用上面的函数就能够实现日期格式转换,倒计时,自定义月历等常规的需要,希望对大家的学习或者工作能有一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
javascript阻止scroll事件多次执行的思路及实现
Nov 08 Javascript
jQuery CSS()方法改变现有的CSS样式
Aug 20 Javascript
JavaScript字符串对象substr方法入门实例(用于截取字符串)
Oct 16 Javascript
js数组的基本操作(很全自己整理的)
Oct 16 Javascript
iScroll.js 使用方法参考
May 16 Javascript
详解js实现线段交点的三种算法
Aug 09 Javascript
BootStrap 导航条实例代码
May 18 Javascript
vue组件 $children,$refs,$parent的使用详解
Jul 31 Javascript
vue中如何实现pdf文件预览的方法
Jul 12 Javascript
vue3.0 CLI - 2.4 - 新组件 Forms.vue 中学习表单
Sep 14 Javascript
浅谈layui 绑定form submit提交表单的注意事项
Oct 25 Javascript
javascript 内存模型实例详解
Apr 18 Javascript
JavaScript实现经典排序算法之冒泡排序
Dec 28 #Javascript
BootStrap Tooltip插件源码解析
Dec 27 #Javascript
获取当前月(季度/年)的最后一天(set相关操作及应用)
Dec 27 #Javascript
javascript实现文字无缝滚动
Dec 27 #Javascript
JavaScript仿聊天室聊天记录
Dec 27 #Javascript
基于jQuery实现顶部导航栏功能
Dec 27 #Javascript
js正则表达式最长匹配(贪婪匹配)和最短匹配(懒惰匹配)用法分析
Dec 27 #Javascript
You might like
php守护进程 加linux命令nohup实现任务每秒执行一次
2011/07/04 PHP
php实现的zip文件内容比较类
2014/09/24 PHP
windows中为php安装mongodb与memcache
2015/01/06 PHP
PHP获取ip对应地区和使用网络类型的方法
2015/03/11 PHP
php 无限分类 树形数据格式化代码
2016/10/11 PHP
解析Jquery中如何把一段html代码动态写入到DIV中(实例说明)
2013/07/09 Javascript
nodejs教程之环境安装及运行
2014/11/21 NodeJs
JS验证IP,子网掩码,网关和MAC的方法
2015/07/02 Javascript
jQuery实现信息提示框(带有圆角框与动画)效果
2015/08/07 Javascript
浅析Bootstrap组件之面板组件
2016/05/04 Javascript
javaScript事件学习小结(四)event的公共成员(属性和方法)
2016/06/09 Javascript
jquery 多个radio的click事件实例
2016/12/03 Javascript
vuejs实现折叠面板展开收缩动画效果
2018/09/06 Javascript
基于AngularJS拖拽插件ngDraggable.js实现拖拽排序功能
2019/04/02 Javascript
vue项目中使用scss的方法步骤
2019/05/16 Javascript
webpack4 optimization使用总结
2019/11/10 Javascript
[58:21]DOTA2亚洲邀请赛 4.3 突围赛 Liquid vs VGJ.T 第二场
2018/04/04 DOTA
Python实现的字典值比较功能示例
2018/01/08 Python
微信跳一跳python辅助脚本(总结)
2018/01/11 Python
使用Python爬取最好大学网大学排名
2018/02/24 Python
对python条件表达式的四种实现方法小结
2019/01/30 Python
Django中密码的加密、验密、解密操作
2019/12/19 Python
使用python处理题库表格并转化为word形式的实现
2020/04/14 Python
唤醒头发毛囊的秘密武器:Grow Gorgeous
2016/08/28 全球购物
澳大利亚在线批发商:Simply Wholesale
2021/02/24 全球购物
澳大利亚在线床零售商:Bedworks
2020/09/01 全球购物
工程造价专业大学生职业生涯规划书
2014/01/18 职场文书
歌唱比赛获奖感言
2014/01/21 职场文书
乌鸦喝水教学反思
2014/02/07 职场文书
便利店投资创业计划书
2014/02/08 职场文书
聚美优品广告词改编
2014/03/14 职场文书
运动会广播稿200米(5篇)
2014/10/15 职场文书
巾帼文明岗事迹材料
2014/12/24 职场文书
迁徙的鸟观后感
2015/06/09 职场文书
2016年中学清明节活动总结
2016/04/01 职场文书
解决MySQL Varchar 类型尾部空格的问题
2022/04/06 MySQL