JS 地下城挑戰 - 4F 時區

圖片來源: 六角學院

前言

這次闖關除了基本功能之外,試著加入以下一些小功能:

  • 新增及刪除時區
  • Local Storage 儲存使用者操作資料
  • 多國語系

這些功能主要是使用六角學院JavaScript 學徒試煉 課程作業應用到的一些技巧,因為額外新增了一些小功能,需要多花點時間,有興趣可以參考下面的連結。文末也會提供無額外功能版本的 Codepen Demo 供大家參考。

Demo
JavaScript Code
Github

關卡資訊

UI 設計稿

  • 僅能使用原生 JS 開始,不能使用套件
  • 特別注意必須用 JS 處理各國時區
  • 請寫一篇 BLOG 來介紹你的挑戰過程,並介紹 JavaScript 如何提供 GMT、UTC 時區語法,以及何謂 TimeStamp。

每日一字

GMT & UTC

GMT格林威治標準時間
UTC世界協調時間

兩者皆是世界標準時間,但是 UTC 的算法更加嚴謹,不過兩者差異極小,基本上是可以當作相同的。

TimeStamp

TimeStamp 是指格林威治時間1970年01月01日00時00分00秒起至現在的總秒數。通俗的講,TimeStamp 是一份能夠表示一份數據在一個特定時間點已經存在的完整的可驗證的數據。它的提出主要是為用戶提供一份電子證據, 以證明用戶的某些數據的產生時間。在實際應用上, 它可以使用在包括電子商務、金融活動的各個方面。

在 JavaScript 中取得 TimeStamp

1
2
const dateTime = new Date().getTime();
const timestamp = Math.floor(dateTime / 1000);

解題

HTML 結構

1
2
3
4
5
<div id="timeZone">
<h1 class="title">WORLD CLOCK</h1>
<main class="content">
</main>
</div>

Date.toLocaleString() API

首先我們需要先認識它,這個語法是完成這個關的關鍵,詳細用法可以參考以下的文件。

基本結構

dateObj.toLocaleString([locales [, options]])

  • locales 為一組字串,可選擇選擇語言環境

  • options 為一組物件,設定該 API 回傳的日期格式字串,可以設定時區、十二時制或二十四時制..等等。

使用範例

只帶入 locales 語言參數,預設為當前環境語言,可以不傳。

1
2
3
4
const date = new Date();
date.toLocaleString()     // output: "2019/6/19 下午2:15:44"
date.toLocaleString('zh-TW'); // output: "2019/6/19 下午2:15:44"
date.toLocaleString('en'); // output: "6/19/2019, 2:15:44 PM"

帶入 options 格式參數,可以進一步設置回傳字串的格式,這邊舉這次挑戰所使用到的參數,詳細的參數配置請參考 MDN

1
2
3
4
5
6
7
8
9
10
11
const date = new Date();
date.toLocaleString('zh-TW', {
timeZone: 'Asia/Taipei' // 地區
hour12: false, // 時制
year: 'numeric',
month: 'short',
day: '2-digit',
hour: 'numeric',
minute: 'numeric',
});
// output: "2019年6月19日 14:35"

開始撰寫程式碼

設定地區資料

將支援的地區宣告在一個陣列當中 ( 這些字串都是 Date.toLocaleString() 所支援的地區 )

1
let data = ['Europe/London', 'Australia/Sydney', 'Asia/Bangkok', 'America/New_york']

設定 config 樣板

宣告基礎的 Date.toLocaleString() API 需使用的 options 樣板

1
2
3
4
5
6
7
8
let config = {
hour12: false,
year: 'numeric',
month: 'short',
day: '2-digit',
hour: 'numeric',
minute: 'numeric',
}

將地區資料及 config 合併

利用陣列遍歷方法 mapdata 內的地區值和預設的 config 一一合併,並且使用 Date.toLocaleString() 來產生字串。

關於 map 方法可以參考之前的文章JavaScript陣列處理常用方法

1
2
3
4
let newData = data.map((item) => {
config.timeZone = item;
return new Date().toLocaleString('en', config);
})

輸出 newData 陣列

字串處理

利用 split方法 處理 newData 的值,把年月日時分資料拆開,方便之後將資料渲染到畫面上。
目前每筆資料是長這個樣子的 "Jun 19, 2019, 08:47",我們可以利用 split 將字串轉換成陣列

關於 split 方法也可以參考 JavaScript陣列處理常用方法

1
2
3
let _data = newData.map((item) => {
return item.split(' ');
})

輸出 _data 物件

畫面渲染

現在我們得到畫面所需要的時間資訊了,接著將資料渲染到畫面吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const wrapNode = document.querySelector('.content'); // 選取父層 DOM

_data.forEach((item, index) => {
const timeZoneParentNode = document.createElement('div'); // 每次迴圈創建一個 div 節點
timeZoneParentNode.classList.add('content__item'); // 為該節點新增 class 樣式

timeZoneParentNode.innerHTML = `
<div class="information">
<div class="information__title">
${(data[index].replace(/\w+\/(\w+)/g, '$1')).replace(/\_/g, ` `).toUpperCase()}
</div>
<div class="information__date">
${item[1]} ${item[0]}
</div>
</div>
<div class="time">
${item[2]}
</div>
`

wrapNode.appendChild(timeZoneParentNode);
}

${(data.replace(/\w+\/(\w+)/, '$1')).replace(/\_/g, ' ').toUpperCase()} 這段程式碼是利用正規式data 資料內原來的字串做處理,並且回傳新的字串。例如 data[0] 原來是 'Europe/London',透過正規式我們可以將 / 前的所有字串過濾掉,最後回傳 'London'。關於正規式可以參考文章初探正規式

setInterval 重複執行

將程式碼包裝成函式,並加入 while 判斷是否重複渲染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function render() {
let newData = data.map((item) => {
config.timeZone = item;
return new Date().toLocaleString('en', config);
})

let _data = newData.map((item) => {
return item.split(',');
})

const wrapNode = document.querySelector('.content');
// ---------------------
// 刪除子節點,避免重複渲染
while(wrapNode.hasChildNodes()) {
wrapNode.removeChild(wrapNode.firstChild);
}
// ---------------------
_data.forEach((item, index) => {
const timeZoneParentNode = document.createElement('div');
timeZoneParentNode.classList.add('content__item');

timeZoneParentNode.innerHTML = `
<div class="information">
<div class="information__title">
${(data[index].replace(/\w+\/(\w+)/g, '$1')).replace(/\_/g, ` `).toUpperCase()}
</div>
<div class="information__date">
${item[1]} ${item[0]}
</div>
</div>
<div class="time">
${item[2]}
</div>
`
wrapNode.appendChild(timeZoneParentNode);
})
}

每一分鐘重複執行

1
2
render();
setInterval(render, 60000);

基本版 Codepen demo