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時間的話,還需要先偏移一個服務器時區。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章