CocosCreator - JavaScript内有关判断日期的需求

CocosCreator - JavaScript内有关判断日期的需求

基本需求

在游戏外围系统的开发过程中,经常会遇到倒计时等功能的实现。就需要掌握一些基本操作并灵活使用JS Date类型的技巧。

Date类型常用使用技巧

w3school上的相关文档

  • 创建当前的日期的对象
 new Date()
  • 创建自定义日期
// 下面这几种写法都是一样的,表示同样的北京时间15号早上6点55这个时间点:
new Date('Mon Jul 15 2019 06:55:40.160 GMT+0800')
new Date('Mon Jul 15 2019 06:55:40:160 GMT+0800')
new Date('2019-07-14T22:55:40.160Z')
new Date('2019-07-15T06:55:40.160+0800')
// log 出来的结果都是 2019-07-14T22:55:40.160Z

其中后面的Z表示 UTC时间,因此UTC时间还是14号。
而GMT,或者直接写+0800表示当前时区。因此,date类型内实际上可以表示全球唯一的时间点,时区只是显示形式,或者一些计算函数的依赖。

  • 获取星期几
// 0为周日 其它返回整数几就是星期几
d1.getDay()
  • 计算加减一个时间后的量,比如很多游戏中要算出一个时间之后多少秒是哪个时刻。
let d1 = new Date('2019-07-15T06:55:40.160+0800')
let d2 = d1.setSeconds(d1.getSeconds()+30);
let d3 = new Date(d2);
// 此时d1,d2,d3都是从原始d1往后推30秒之后的时间,但其中d1和d3是Date对象,d2是Date对象的原始值(整数)
// 而且注意到方便之处在于我们不用去维护进位了。
// 原始值是指 1970-01-01:00:00:00:000Z 到该时间点的毫秒数
// 其中还可以使用setUTCXxx和getUTCXxx的,只要配套使用就好了
  • 即时两个日期完全相同,但直接无法判断相等。但可以相减判断时间差与零的关系。
let d1 = new Date('2019-07-15T06:55:40.160+0800');
let d2 = new Date('2019-07-15T06:55:40.160+0800');
console.log(d1==d2) // false
console.log(d1-d2==0) // true
// 若d1-d2>0 则d1的时间点在d2之后,反之同理
  • 判断两个时间点在日期上为同一天(先忽略时区问题),比如每日奖励,每天就1次,0点整刷新
let d1 = new Date('2019-07-15T06:55:40.160Z');
let d2 = new Date('2019-07-15T19:55:40.160Z');
let d1t = d1.setUTCHours(0,0,0,0);
let d2t = d2.setUTCHours(0,0,0,0);
console.log(d1t, d2t, d1t == d2t);
// 思路是使用setUTCHours约去了日期之后的信息。那么日期相等就是同一天了。
  • 在上一个问题中,一般来说全球化的游戏需要以每个玩家地区的各自的零点来刷新。比如带入这两个日期:
let d1 = new Date('2019-07-15T07:55:40+0800');
let d2 = new Date('2019-07-15T08:05:40+0800');
let d1t = d1.setUTCHours(0,0,0,0);
let d2t = d2.setUTCHours(0,0,0,0);
console.log(d1t, d2t, d1t == d2t);
// 竟然false了。北京时间点7:55和8:05竟然不是同一天!

改正也很简单:把上面两个setUTCHours改为setHours即可

let d1 = new Date('2019-07-15T07:55:40+0800');
let d2 = new Date('2019-07-15T08:05:40+0800');
let d1t = d1.setHours(0,0,0,0);
let d2t = d2.setHours(0,0,0,0);
console.log(d1t, d2t, d1t == d2t);
// 这次为true。仔细研究发现其中的d1t和d2t这两个原始值均表示时刻2019-07-14T16:00:00.000Z
  • 上个需求再加点内容:要显示出来距离下一个刷新的点还需要多久?(下一个时间点就是明天0点)
let d1 = new Date('2019-07-15T07:55:40+0800'); // 输入上次领取的奖励时间记录
let d2 = new Date(); // 获取当前时间
d1.setHours(24,0,0,0); // d1此时已经表示下次刷新点, 如果是第二天0点的话这样写就足够了,还很简单。
// *如果没有理解上面的setHours(0,0,0,0),理解这里就会有障碍。如果仍然写为 d1.setHours(0,0,0,0),那么紧接着应该再写 d1.setDate(d1.getDate()+1); 写这两句是一样的。
// *还需要注意setHours后面写4个参数小时,时,分,秒没问题。而setDate后面仅仅只能跟年月日的日这一个参数。
let diff = d1 - d2;
// d1-d2>0 是指离下次刷新还有的毫秒数,反之是已经超过了刷新时间多少毫秒
  • 有关显示倒计时,游戏中经常的需求是超过了1天,仍然把天换算成24小时加在小时数字上面,或者说超过了1个月,也写做多少天,又或者说超过1天了,忽略显示时分秒等等。那么我经常使用到我封装好的一个函数。把时间差原始值带入即可。
let msResolve = function (tms) {
	let sign = Math.sign(tms);
	let ms = tms % 1000;
	let ts = Math.floor(tms / 1000);
	let s = ts % 60;
	let tm = Math.floor(ts / 60);
	let m = tm % 60;
	let th = Math.floor(tm / 60);
	let h = th % 24;
	let td = Math.floor(th / 24);

	return {
		sign: sign,
		td: td,
		th: th,
		tm: tm,
		ts: ts,
		tms: tms,
		h: h,
		m: m,
		s: s,
		ms: ms
	}
};

// 上一个需求的时间差带入,返回值则为
/* { sign: 1,
  td: 0,
  th: 12,
  tm: 764,
  ts: 45847,
  tms: 45847961,
  h: 12,
  m: 44,
  s: 7,
  ms: 961 } */
  //d h m s ms 表示 天 时 分 秒 毫秒,
  //t表示对应的总量
  // 这样在显示时就很方便的进行各种显示了

项目中用到的几种时间表示法:

// xD hh:mm:ss 小于1D时 xD隐藏
DateTools.solvDisplay1 = function (solv) {
    return "" + (solv.td > 0 ? solv.td + 'D  ' : '') + solv.h.toString().fill0(2) + ":" + solv.m.toString().fill0(2) + ":" + solv.s.toString().fill0(2);
};

// 大于1天时使用xD ,否则使用hh:mm:ss
DateTools.solvDisplay2 = function (solv) {
    return solv.td > 0 ? solv.td + 'D' : DateTools.solvDisplay1(solv);
};

// 大于1天 dd:hh:mm 否则 hh:mm:ss
DateTools.solvDisplay3 = function (solv, dayStr) {
    if (solv.td > 0) {
        return '{}{} {}:{}:{}'.format(solv.td, dayStr, solv.h.toString().fill0(2), solv.m.toString().fill0(2), solv.s.toString().fill0(2));
    } else {
        return solv.h.toString().fill0(2) + ":" + solv.m.toString().fill0(2) + ":" + solv.s.toString().fill0(2);
    }
};

// xD hh:mm:ss 小于1D时 xD隐藏
DateTools.solvDisplay4 = function (solv) {
    return solv.h.toString().fill0(2) + ":" + solv.m.toString().fill0(2) + ":" + solv.s.toString().fill0(2);
};

// *fill0(2)是为了补0到2位数
  • 然而在全球服的服务器代码中,问题又来了。在非本时区的服务器上,setHours经常都是以服务器端setHours为准,有的云服务,比如LeanCloud的云服务。都是以UTC时间为准。那么,相当于服务器端只能做setUTCXxx。(其实是set/getXxx 和set/getUTCXxx都一样作用),如果要服务器验证玩家领取的每日奖励是否为同一天呢?
    比如下面:
let d1 = new Date('2019-07-15T23:55:40+0800');
let d2 = new Date('2019-07-16T00:00:40+0800');
let d3 = new Date('2019-07-15T07:55:40+0800');
let d4 = new Date('2019-07-15T08:05:40+0800');
let d1t = d1.setUTCHours(0,0,0,0);
let d2t = d2.setUTCHours(0,0,0,0);
let d3t = d3.setUTCHours(0,0,0,0);
let d4t = d4.setUTCHours(0,0,0,0);
console.log('--------------')
console.log(d1, d1.toGMTString());
console.log(d2, d2.toGMTString());
console.log(d1t, d2t, d1t==d2t);
console.log('--------------')
console.log(d3, d3.toGMTString());
console.log(d4, d4.toGMTString());
console.log(d3t, d4t, d3t==d4t);
// 会发现 d1 d2 为同一天,而 d3 d4 不为同一天

这里面的原因就是服务器并不知道玩家所在时区,也就不知道针对每个时区每天刷新点。
改进:

let d1 = new Date('2019-07-15T23:55:40+0800');
let d2 = new Date('2019-07-16T00:00:40+0800');
let d3 = new Date('2019-07-15T07:55:40+0800');
let d4 = new Date('2019-07-15T08:05:40+0800');

let timezoneOffset = new Date().getTimezoneOffset(); // 从客户端传入,意味着客户端告诉服务器,我来自地球的哪个时区

// 先假设回0时区,过滤后可以只当输入的时间就是0时区玩家的时间,而玩家就是0时区玩家。(这里就是算法核心)
d1.setUTCMinutes(d1.getUTCMinutes() - timezoneOffset);
d2.setUTCMinutes(d2.getUTCMinutes() - timezoneOffset);
d3.setUTCMinutes(d3.getUTCMinutes() - timezoneOffset);
d4.setUTCMinutes(d4.getUTCMinutes() - timezoneOffset);

// 在按0时区的一天进行判断
let d1t = d1.setUTCHours(0,0,0,0);
let d2t = d2.setUTCHours(0,0,0,0);
let d3t = d3.setUTCHours(0,0,0,0);
let d4t = d4.setUTCHours(0,0,0,0);

// 输出结果
console.log('-------timezoneOffset------', timezoneOffset)
console.log('--------------')
console.log(d1, d1.toGMTString());
console.log(d2, d2.toGMTString());
console.log(d1t, d2t, d1t==d2t);
console.log('--------------')
console.log(d3, d3.toGMTString());
console.log(d4, d4.toGMTString());
console.log(d3t, d4t, d3t==d4t);

/*
输出:
-------timezoneOffset------ -480
--------------
2019-07-15T00:00:00.000Z 'Mon, 15 Jul 2019 00:00:00 GMT'
2019-07-16T00:00:00.000Z 'Tue, 16 Jul 2019 00:00:00 GMT'
1563148800000 1563235200000 false
--------------
2019-07-15T00:00:00.000Z 'Mon, 15 Jul 2019 00:00:00 GMT'
2019-07-15T00:00:00.000Z 'Mon, 15 Jul 2019 00:00:00 GMT'
1563148800000 1563148800000 true
*/

// *如果服务器时区不是0时区或者不采用UTC时间的话,还需要先偏移一个服务器时区。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值