August 5, 2014

Javascript 處理 Timezone

No comments:
前陣子處理了一些 timezone 的問題,現在順手紀錄下來,不然我這顆容量只有幾十 mb 的大腦很快就會忘光了。疏誤之處還請鄉親不吝指導

結論

簡單來說,除非是夠新的瀏覽器,否則 無法 純粹用計算 timezone offset 的方式,在 javascript 裡面正確處理時區的問題,除非你透過 moment-timezone.js 這些已經正確處理完 timezone 的 3rd party library。原因在於 timezone offset 會隨著日光節約時間變換

名詞解釋

雖然大家可能都知道了,還是寫一點名詞解釋,看起來似乎比較認真

UTC

世界協調時間/世界標準時間,英文「Coordinated Universal Time」/法文「Temps Universel Cordonné」,也許跟民族自尊這種無法退讓的問題有關,因此折衷簡寫為 UTC。

這是透過原子鐘計算出來的時間,全世界看到的都應該一樣。再藉由 Z (Zulu) 來做調整,好比

Local time

如果 UTC 的時間是 02:03,對於設定 UTC+8 的人來說, local time 是 10:03;對於設定 UTC+9 的人來說, local time 是 11:03

Local offset

各地區透過 Zulu 來得到這個 offset,目的是希望該區的正午十二點恰好是日當正午。

(其實我個人還滿討厭這件事情,既然「12:00 等於中午」是人工教育出來的,幹嘛還把全世界的時間搞得那麼複雜。反正我們能不能星際旅行還不知道,在不考慮相對論的前提下,全球用同一個時間有多好)(阿宅的抱怨)

Daylight Saving Time

問題的元兇,日光節約時間,又稱夏時制。尤其在高緯度地區,夏天的日出時間跟冬天有不少落差,為了鼓勵大家多當晨型人吸收多點紫外線,用非常人工的方式強制在某幾個月內撥快時鐘。

(其實我很想要收集七顆龍珠,向神龍許願回到過去把提出這個概念的人毒打一頓)
(直接許願日光節約時間消失不是更好?)
(好像乾脆許願 IE 全部消失最好)

問題

其實問題可以用很簡單的例子來演示,因為我是臺灣人,假設我人在台北,打開瀏覽器裡面的時區就是 Asia/Taipei +08:00
  • 請問你看文章的當下,日本時間是什麼?
  • 請問你看文章的當下,紐約時間是什麼?
  • 請問你的時間是 2014/04/01 13:00:00,當時日本的時間是什麼?
  • 請問你的時間是 2014/12/01 13:00:00,當時日本的時間是什麼?
  • 請問你的時間是 2014/04/01 13:00:00,當時紐約的時間是什麼?
  • 請問你的時間是 2014/12/01 13:00:00,當時紐約的時間是什麼?
因為 browse 裡面的 Date 物件處理的都是 local time,所以你產生 Date 物件再吐出 String 的時候都會看見 +08:00。而且這些問題都沒有辦法透過簡單的查表計算,好比說「喔!日本是 UTC+9,所以我把電腦裡面的時間再加 3600 秒就可以了嘛」

因為那張表是浮動而非固定的。原因就是討厭的日光節約時間

對臺灣人來說日光節約時間可能很陌生而得到,「這種鬼東西可以不用鳥它啦」的結論,我一開始也是這麼想。可是一查才知道,美國紐約在 2014/03/09 就有實施日光節約時間,持續到 2014/11/2。

2014 年耶!紐約耶!

如果是火奴魯魯或是南極洲這種你可能一輩子都遇不上來自那裡的客戶地方就算了,如果連紐約最近的時間都無法正確處理可說不過去。

理解日光節約時間的行為,可以看這個 Time changes for USA

在實施日光節約時間以前,現在時間是紐約的凌晨 01:59:58
五秒過後,現在時間變成了 03:00:03,中間有一個小時不見了!

用 Linux 的宅友,可以用指令 tzselect 找出某地區的 timezone 字串,然後塞入變數 TZ 來執行 chrome
$ TZ='America/New_York' chrome

這樣就能偽裝成你去美國紐約玩,沒錢出國的時候不失為一個望梅止渴的旅行法。此時從 console 測試可以看見這些結果

new Date(Date.UTC(2014, 2, 9, 06, 01, 0, 0)).toString()
"Sun Mar 09 2014 01:01:00 GMT-0500 (EST)"

new Date(Date.UTC(2014, 2, 9, 06, 59, 0, 0)).toString()
"Sun Mar 09 2014 01:59:00 GMT-0500 (EST)"

new Date(Date.UTC(2014, 2, 9, 07, 00, 0, 0)).toString()
"Sun Mar 09 2014 03:00:00 GMT-0400 (EDT)"
直接跳到三點了!,無法顯示 02:00:00 ~02:59:59

同一年在 11/02 的時候紐約結束實施日光節約時間,因此那一天會有同一時間出現兩次的奇景

new Date(Date.UTC(2014, 10, 2, 04, 1, 0, 0)).toString()
"Sun Nov 02 2014 00:01:00 GMT-0400 (EDT)"

new Date(Date.UTC(2014, 10, 2, 05, 1, 0, 0)).toString()
"Sun Nov 02 2014 01:01:00 GMT-0400 (EDT)"

new Date(Date.UTC(2014, 10, 2, 06, 1, 0, 0)).toString()
"Sun Nov 02 2014 01:01:00 GMT-0500 (EST)"

雖說 01:01 會出現兩次,細看便知一個在 EDT(Estern Daylight Time) 一個在 EST (Estern Standard Time)。一個在 -0400 一個在 -0500

這就是問題所在。要把某一筆時間顯示為某區的 local time 時,你如果不知道該地區當時是否有實施日光節約時間,所以你不會知道 offset 該是多少,也因此無法計算。

而日光節約時間,比我一開始想像還要常見得多,因此無法假裝不存在而不處理。台灣也有實施過日光節約時間,鴨七有相當詳細的台灣日光節約時間之考據

解法

後來 Date object 有一個 method Date.toLocaleString,可以指定 timezone。不過捲到相容性那邊,看見要 IE11 才開始支援,希望我兒子開始學寫程式的時候已經可以放心使用這個 method

最簡單的就是拿 moment.js 來用,加上 moment-timezone.js 之後就擁有處理時區的能力。即便如此,還是要下載一大包的 data,內含各地區的日光節約時間資料,這三者加在一起才能正常運作。當然也不用每個地區都抓,你的產品有支援的地區包進去就好了。即便如此我還是隨隨便便就超過 100K 了 (淚)

August 4, 2014

犯傻找不到 てまりのおうち手鞠之家 - 東京自由行

No comments:
寫這篇其實只是小小的提醒,希望想去貓咪咖啡(Cat Cafe) てまりのおうち(手鞠之家) 的朋友不會跟我一樣犯傻

這次旅遊(2014/07/23)去東京以前,朋友丟了這篇 【日本東京】貓奴必看!森林般的吉祥寺貓咪咖啡-てまりのおうち(手鞠之家) 的連結過來,雖然我不是貓奴,因為家裡有隻很像狗的變態 虎斑貓,恰好我也要去吉祥寺那邊,就順道把這間店排進行程。

按照 Google Map 的指示走到那邊,看見咖啡店無限期休息的告示,心中吶喊一聲「這種店怎麼可能會收!」只好摸摸鼻子離開,直到前天我才發現我走錯地方。

首先,看看 Cat Cafe 的官方網站,寫明地址是 東京都武藏野市吉祥寺本町 2-13-14 3F


從 Google Map 來看相當近,事實上也真的很近,過兩個街口就到了。憑著人體導航的經驗,抓準方向直衝過去看見了門口貼上這張


雖然我只會日文五十音,但是看漢字也猜得出來
  • 這是咖啡店的公告
  • 這間咖啡店休業中,還不知道什麼時候會再開
但我直到前兩天才發現「唔....名字好像不太對」(出發前沒時間規劃行程,全部丟給老婆弄,所以我都沒認真看店名 XD)

原來.........


我走到了紅色箭頭的地方(以為那邊是路口),歇業那間叫做エコー咖啡店,但是貓咪咖啡店的入口在綠色箭頭進去三樓,就在旁邊而已....

就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已就在旁邊而已

好吧,反正就是我犯傻了,希望其他朋友不會跟我一樣笨 XD

April 12, 2014

g0v 是塊大草坪

No comments:
2019 Update: 隔了好多年,g0v 的組成與社群結構不斷地在變化,所以這篇文章講的東西跟現在很不一樣了。參考 PM5 說的:「這幾次參加 #g0v 黑客松,又開始覺得有些深層的社群結構跟一兩年前不太一樣了。這 6 年多來大概每 1~2 年就會覺得社群有些重大的深層運作機制變化


「沒有人可以代表 g0v」「g0v 是一個社群」,初次接觸 g0v 的人經常聽見這兩句話,也經常摸不著頭緒。

就我一位偶爾送點 patch,參加黑客松吃東西的阿宅工程師,也來說說我的想像,看看能否用譬喻的方式,提供一個理解的路徑。

modified by walkingice from https://www.flickr.com/photos/grahamking/3751576906. License: CC By-Nc-Sa
g0v 就像一塊大草坪,聚集著好幾群的人


在我的想像裡,g0v 是一塊大草坪,是一塊沒有圍牆的大草坪。草坪上插著一塊告示牌寫著 g0v 的宣言。因為草坪沒有圍牆,任何一位認同理念的人都可以隨時走進來,也可以隨時走出去。

虛擬世界的草坪是無限寬廣,任何人也可以另外找一塊草坪,寫上自己認同的宣言,邀請朋友進去同樂。

這塊草坪上的人是怎麼活動的呢?他們就像日本人賞櫻那樣,拉開席墊大家就各自坐下開始聊天,各自有其小群體。群體之間可能相互有交集,也可能沒有。對應到現實情況,就是不同的專案會吸引到不同的參與者。

有些人會抱怨:「怎麼沒有人理我」。試想,當你走進這塊大草地,大家在原本的席墊上聊得很開心,有多少人會注意到你呢?

當然,你如果帥到掉渣、美得冒泡,現實生活就是個響叮噹的人物,走進草地大家一定會圍著你多聊兩句。

在 g0v 你可以隨時開一個專案,拉開一個席墊。至於會不會有人進來坐,其實是要靠你自己爭取。不少人喜歡獨自坐著,準備自己的佳餚,默默地一句話都沒說,只是把蓋子掀開,香味就吸引了大批鄉民。

在這塊草地上,沒有人可以號令任何人,無趣的事情就算拿著大聲公拼命重複喊也無濟於事。

這是一塊大草坪,人們來來去去,你可以找到一些待得久的熟面孔,聽他們講述歷史,但是你找不到草坪上所有人的代表 (光是「草坪上的人數」都難以定義,人群是流動的)。

當你對著草坪上的人大喊:「負責人是誰?我有話想說」你只會發現大家疑惑的看著你,臉上寫著「你在說啥啊?」

這塊草坪上的人,樂於動手解決問題,支持言論自由,相信公開透明的力量。不只是寫程式,其他的藝術、文學、音樂、電影.....只要願意用開源的模式鼓勵大家參與,都可以進到草坪上一起同樂。

這是我對 g0v 的想像,一塊歡樂的大草坪。



延伸閱讀:開幹開團到開源:前往 g0v 的奇幻旅程

March 5, 2014

Awesome WM vocabulary widget

No comments:
換到 awesome wm 之後,上方的 bar 其實有很多空間。因為自己的記性不好,乾脆寫個小 widget,大約 60 秒就 show 一個單字,不然每次五十音我都會一直忘記

抄抄寫寫一堆很醜的 lua code 的結果如下,他會讀取 .vocabulary 這個檔案,每 60 秒亂數取其中一行顯示

檔案更新怎麼辦?重開一下 awesome 就好囉!

vocabulary_widget = widget({ type = "textbox" })

function vocabularyUpdate(lines)
    vocabulary_widget.text = lines[math.random(0, #lines)]
end

function readFile(filename)
    local arr = {}
    local i = 0
    local file=io.open(filename, "r")
    if not file then return "empty" end
    for line in file:lines() do
        arr[i] = line
        i = i + 1
    end
    return arr
end

local file = "/home/walkingice/.vocabulary"
local period = 60

math.randomseed(os.time())
content = readFile(file)

vocabulary_timer = timer({timeout = period})
vocabulary_timer:add_signal("timeout", function()  vocabularyUpdate(content) end)
vocabulary_timer:start()

March 1, 2014

三十而立

No comments:
某次飯局之後,回家的路上有幸搭乘長輩的順風車一路南下,一個小時的車程蒙長輩不嫌棄,天南地北談了不少話題。長輩有相當豐富的閱歷,甚至在國會裡面也有豐富的工作經歷,於是我談到了 g0v 的事情,不同於其他話題,長輩直接而簡短地說:「認真工作好好取得一個成就,這樣子別人才會尊敬你。年輕人啊,不要太多的理想性」
又有一次,跟一位相當要好的老同學在聊天。他對參與政治相當有熱誠,勤跑社團結交朋友,從學生時代到現在苦心經營超過十年。 聊到選舉,他說「不管談什麼理想,反正選輸了就是失敗,選輸了就是什麼都沒有」

我說,我打從心底同意他們說的話,甚至,我彷彿能聽見他們這麼說話,心中經過多少焠鍊。

人生有幸,認識了相當熱血的朋友,他勇於放下手中擁有的東西,只為了追逐自己的夢想。也有幸認識智識卓越,思慮清晰的朋友,他教了我如何分辨利弊得失,如何梳理出社會眾多現象背後的脈絡。我認真地傾聽了各種價值觀,也在心中有過無數次的衝擊。

最近則是一直在思索,為什麼要如此不理性地選擇理想化的路走?

我在 g0v 最關心的專案是推動國會透明化,但是我老實地向自己分析,我能夠促成的改變實在微薄。但我卻很矛盾地相信我做的事情非常有價值。我是說,對照前面兩段的經驗,我既同意他們說的事情,卻又反其道而行並且深刻認同,這太奇怪了。

忍不住想到「在資訊科技創業」這件事情。聽過有長輩的提問:「這些投入科技業創業的年輕人,十個有一個成功嗎?」『可能一百個都不見得有一個』「那為什麼還要做這個?」

真的,反正都是當老闆,投入其他產業甚至開店,讓自己致富的機率都比待在科技業高出許多。

我一直在想「為什麼」,為什麼我會如此熱衷地將自己的精力以不理性的方式消耗

自己最近給自己的答案,則是分成對內與對外兩個面向。

對內,則是尋求自我的實現。 而自我實現無法以職業、收入來滿足。當然並不是說我不喜歡錢,或不希望給家人好的生活。而是在維持家庭正常的生活水平前提下,追求我認同的價值,在這個過程之中我才能感受到自己的生命被充實。

對外,一個微小改進的價值,以一年、十年甚至百年的幅度來看,可能會有不同的結論。世上的英雄少之又少,多數的人是在時間的長河中互相推擠著緩緩向前。我知道個人無力指揮流向,但我也知道流向的改變總是各種作用力加成的結果。

對內的因素我無法超越,對外的因素又是我的信仰,加總起來就是我現在的選擇。我想這就是我如此快樂的原因。

當然這是我這個階段的想法,祝福我自己在四十的時候已然不惑