详解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中Get和Set访问器的实现代码
Sep 19 Javascript
很棒的学习jQuery的12个网站推荐
Apr 28 Javascript
读jQuery之十一 添加事件核心方法
Jul 31 Javascript
IE事件对象(The Internet Explorer Event Object)
Jun 27 Javascript
使用JavaScript链式编程实现模拟Jquery函数
Dec 21 Javascript
AngularJS语法详解(续)
Jan 23 Javascript
JS实现的鼠标跟随代码(卡通手型点击效果)
Oct 26 Javascript
JS 事件绑定、事件监听、事件委托详细介绍
Sep 28 Javascript
js实现炫酷的左右轮播图
Jan 18 Javascript
vue的diff算法知识点总结
Mar 29 Javascript
对vux点击事件的优化详解
Aug 28 Javascript
vue 弹框产生的滚动穿透问题的解决
Sep 21 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 fsockopen解决办法 php实现多线程
2014/01/20 PHP
ThinkPHP打开验证码页面显示乱码的解决方法
2014/12/18 PHP
鼠标移动到一张图片时变为另一张图片
2006/12/05 Javascript
javascript 兼容FF的onmouseenter和onmouseleave的代码
2008/07/19 Javascript
jQuery源码中的chunker 正则过滤符分析
2012/07/31 Javascript
指定区域的图片自动按比例缩小的js代码(防止页面被图片撑破)
2014/02/21 Javascript
离开当前页面前使用js判断条件提示是否要离开页面
2014/05/02 Javascript
jquery uploadify 在FF下无效的解决办法
2014/09/26 Javascript
jQuery实现鼠标滑过点击事件音效试听
2015/08/31 Javascript
基于jQuery的ajax方法封装
2016/07/14 Javascript
javascript的document中的动态添加标签实现方法
2016/10/24 Javascript
JavaScript 数组的深度复制解析
2016/11/02 Javascript
node.js读取Excel数据(下载图片)的方法示例
2018/08/02 Javascript
JavaScript防止全局变量污染的方法总结
2018/08/02 Javascript
Vue使用Canvas绘制图片、矩形、线条、文字,下载图片
2019/04/26 Javascript
微信小程序wx.request拦截器使用详解
2019/07/09 Javascript
vue控制多行文字展开收起的实现示例
2019/10/11 Javascript
Vue两个版本的区别和使用方法(更深层次了解)
2020/02/16 Javascript
python基础练习之几个简单的游戏
2017/11/10 Python
英国高档时尚男装购物网站:MR PORTER
2016/08/09 全球购物
linux面试题参考答案(11)
2012/05/01 面试题
初中生自我鉴定
2014/02/04 职场文书
满月酒主持词
2014/03/27 职场文书
查摆问题自我剖析材料
2014/08/18 职场文书
2014年计生协会工作总结
2014/11/21 职场文书
十佳少年事迹材料
2014/12/25 职场文书
毕业生对母校寄语
2015/02/26 职场文书
2015年教研员工作总结
2015/05/26 职场文书
居安思危观后感
2015/06/11 职场文书
贷款工资证明范本
2015/06/12 职场文书
2016教师廉洁从教心得体会
2016/01/13 职场文书
读《皮囊》有感:理解是对他人的最大的善举
2019/11/14 职场文书
nginx反向代理时如何保持长连接
2021/03/31 Servers
Python Django 后台管理之后台模型属性详解
2021/04/25 Python
基于Python实现一个春节倒计时脚本
2022/01/22 Python
Windows和Linux上部署Golang并运行程序
2022/04/22 Servers