详解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 相关文章推荐
checkbox 多选框 联动实现代码
Oct 22 Javascript
jquery validate使用攻略 第四步
Jul 01 Javascript
JavaScript NodeTree导航栏(菜单项JSON类型/自制)
Feb 01 Javascript
js+HTML5实现视频截图的方法
Jun 16 Javascript
js定义类的几种方法(推荐)
Jun 08 Javascript
JS实现简易刻度时钟示例代码
Mar 11 Javascript
Angularjs验证用户输入的字符串是否为日期时间
Jun 01 Javascript
JS请求servlet功能示例
Jun 01 Javascript
angular 用拦截器统一处理http请求和响应的方法
Jun 08 Javascript
Vue头像处理方案小结
Jul 26 Javascript
Node.js API详解之 module模块用法实例分析
May 13 Javascript
Vue OpenLayer测距功能的实现
Apr 20 Vue.js
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 数组遍历方法大全(foreach,list,each)
2010/06/30 PHP
浅析PHP中的UNICODE 编码与解码
2013/06/29 PHP
php接口实现拖拽排序功能
2018/04/23 PHP
Ext JS 4实现带week(星期)的日期选择控件(实战一)
2013/08/21 Javascript
a标签的href与onclick事件的区别详解
2014/11/12 Javascript
百度UEditor编辑器如何关闭抓取远程图片功能
2015/03/03 Javascript
JavaScript去除数组里重复值的方法
2015/07/13 Javascript
node.js微信公众平台开发教程
2016/03/04 Javascript
Bootstrap表单组件教程详解
2016/04/26 Javascript
无缝滚动的简单实现代码(推荐)
2016/06/07 Javascript
原生js三级联动的简单实现代码
2016/06/07 Javascript
jQuery文件上传控件 Uploadify 详解
2016/06/20 Javascript
JS添加删除DIV的简单实例
2016/07/08 Javascript
漂亮! js实现颜色渐变效果
2016/08/12 Javascript
JavaScript toUpperCase()方法使用详解
2016/08/26 Javascript
浅谈JS中的三种字符串连接方式及其性能比较
2016/09/02 Javascript
AngularJS操作键值对象类似java的hashmap(填坑小结)
2016/11/12 Javascript
JS去掉字符串前后空格、阻止表单提交的实现代码
2017/06/08 Javascript
微信小程序联网请求的轮播图
2017/07/07 Javascript
React进阶学习之组件的解耦之道
2017/08/07 Javascript
javascript+html5+css3自定义弹出窗口效果
2017/10/26 Javascript
Vue实战之vue登录验证的实现代码
2017/10/31 Javascript
angularJs提交文本框数据到后台的方法
2018/10/08 Javascript
[05:24]TI9采访——教练
2019/08/24 DOTA
简单介绍Python中的floor()方法
2015/05/15 Python
使用python实现省市三级菜单效果
2016/01/20 Python
pytorch .detach() .detach_() 和 .data用于切断反向传播的实现
2019/12/27 Python
python实现猜单词游戏
2020/05/22 Python
COS美国官网:知名服装品牌
2019/04/08 全球购物
优秀班干部事迹材料
2014/01/26 职场文书
车间主任岗位职责
2014/03/16 职场文书
小学生保护环境倡议书
2014/05/15 职场文书
行政诉讼答辩状
2015/05/21 职场文书
MySQL 使用事件(Events)完成计划任务
2021/05/24 MySQL
React Native项目框架搭建的一些心得体会
2021/05/28 Javascript
JS前端轻量fabric.js系列之画布初始化
2022/08/05 Javascript