Home » Posts tagged "date"

Trac 1.2 裡 datepicker 每個禮拜的第一天改成星期天

Trac 1.0 搭配舊的 DateFieldPlugin 時,預設每週的第一天會是星期一,但這個設定可以在 trac.ini 內用 first_day 參數調整,像是這樣:

[datefield]
format = ymd
separator = -
first_day = 0

但在 Trac 1.2 在 trac.ini 裡就沒有提供設定讓人調整了... 而由於 Trac 是用 jQuery UIDatepicker,在這個套件裡有提供方法讓人調整,所以解決的方向變成在 site.html 內用 JavaScript 處理,把這段程式碼塞到 JavaScript 的區段內就可以了:

// Datepicker
jQuery.datepicker.setDefaults({firstDay: 0});

一整個繼續惡搞中... 然後把 wiki 上的 Trac 條目也更新上去。

Trac 1.2 的 Due Date...

在先前的文章提到了把自己在用的事件管理系統 Trac 從 1.0 升級到 1.2,然後 Due Date 的設計改變了:「Trac 1.1 增加的 time 欄位,以及 Due Date 資料的轉移」、「總算把手上的 Trac 1.0 升級到 1.2 了...」。

Trac 1.2 的資料型態是在底層存 unix timestamp 的變形 (乘以 1000000,然後前端補上 0 存成文字),這幾天用下來才發現一些以前沒遇到的問題。

一開始轉到 Trac 1.2 是設成 date,但意外的發現 (因為伺服器時間不是 UTC),不同時區的使用者在更新 ticket 時,系統會判定 Due Date 有變動而產生變更記錄,想了一下就改用 datetime 來處理這個問題。

用了 datetime 一陣子後,才發現先前的公司遇到的情境中,時區差異都很小,所以不會有 Due Date 理解上的問題 (像是從 +7 到 +9 的時區),如果今天是美國西岸跟台灣互相合作的話,只用 date 就會產生很明顯的理解問題了...

算是這陣子用 Trac 1.2 而對 Due Date 設計有不一樣的理解...

總算把手上的 Trac 1.0 升級到 1.2 了...

就如同上一篇提到的,Trac 在 1.1.1 後新增了 time 格式,所以本來的 DateFieldPlugin 有些資料要轉換。我這邊只有用在 Due Date,所以就是轉 due_date 的資料而已。

先把 due_date 都改成 due_date_bak

UPDATE ticket_custom SET name = 'due_date_bak' WHERE name = 'due_date';

然後重新計算資料,這邊是因為所有的系統都是 UTC,所以直接轉就可以了:

INSERT INTO ticket_custom (ticket, name, value) SELECT ticket, 'due_date', LPAD(UNIX_TIMESTAMP(STR_TO_DATE(value, '%Y-%m-%d')) * 1000000, 18, '0') FROM ticket_custom WHERE name = 'due_date_bak';

而我的 Report 有用到 due_date 欄位的東西,本來是 c.value 直接輸出,現在要改成:

FROM_UNIXTIME(CONVERT(c.value / 1000000, UNSIGNED INTEGER), '%Y-%m-%d') AS due_date

Trac 1.2 相較於 1.0 最不習慣的地方應該是修改界面的位置改變了,現在 Add Comment 變成在 Modify 下面,有點不太習慣,但之後用久了應該就會習慣了。其他的修一修改一改都會動了...

Trac 1.1 增加的 time 欄位,以及 Due Date 資料的轉移

Trac 的版本玩法跟早期 Linux Kernel 的模式有點像,也就是版號偶數是正式版,奇數是開發版... 雖然現在 Linux Kernel 已經不玩這套了,但 Trac 還是維持這樣的開發方式。

先前一直都是用 Trac 1.0,其中 Due Date 的功能則是用「DateFieldPlugin」這個套件,讓 Trac 支援 date 格式,於是就可以在 [ticket-custom] 裡面指定 Due Date 了:

due_date = text
due_date.date = true
due_date.date_empty = false
due_date.label = Due Date
due_date.value = <now>

在套件的頁面也有提到在 Trac 1.1.1 後就有內建的方式可以用了:

Notice: This plugin is deprecated in Trac 1.2 and later. Custom fields of type ​time were added in Trac 1.1.1.

連結是連到 1.1 的,我要測 1.2 的,所以往現在的版本翻資料,可以看到在 TracTicketsCustomFields 這邊的說明:(這邊就懶的照原來 html 排了,用 pre 直接放縮排)

time: Date and time picker. (Since 1.1.1.)
    label: Descriptive label.
    value: Default date.
    order: Sort order placement.
    format: One of:
        relative for relative dates.
        date for absolute dates.
        datetime for absolute date and time values.

這樣一來設定就會變成:

due_date = time
due_date.format = date
due_date.label = Due Date
due_date.value = now

但底層資料怎麼存?先看 ticket_custom 這個表格的結構,可以看到是 EAV 的架構:

+--------+------------+------+-----+---------+-------+
| Field  | Type       | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| ticket | int(11)    | NO   | PRI | NULL    |       |
| name   | mediumtext | NO   | PRI | NULL    |       |
| value  | mediumtext | YES  |     | NULL    |       |
+--------+------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

隨便拉一些可以看出來放法很簡單:

+--------+----------+------------+
| ticket | name     | value      |
+--------+----------+------------+
|      1 | due_date | 2016-10-03 |
+--------+----------+------------+

改成 Trac 1.2 內建的 time 後,塞 2018/02/28 變成:

+--------+----------+--------------------+
| ticket | name     | value              |
+--------+----------+--------------------+
|      1 | due_date | 001519776000000000 |
+--------+----------+--------------------+

拿掉後面的六個 0 後可以看到就是 2018/02/28 了,要注意的是,這邊會受到時區影響,我一開始測試的時候沒調整,寫進去的時間是用伺服器預設的時區計算的。另外也大概能理解前面放兩個 0 的目的,是為了讓 string 比較時的大小就會是數字實際的大小。

$ date --date=@1519776000
Wed Feb 28 00:00:00 UTC 2018

這樣就知道要怎麼做人工轉換了...

Facebook 在 MySQL 裡存時間的型態

MySQL at Facebook這邊說明提到了,Facebook 內部是使用 INT UNSIGNED 儲存時間:

Which gets us to the point that it is no different than storing INT (hello 2038?) or UNSIGNED INT (a bit later) or BIGINT (till the end of time) and possibly passing binary values in efficient protocols eventually.

If you got that far of this post, your likes in Facebook graph are stored with 'INT UNSIGNED' time field.

順道一提,INT 是 2038 年問題,INT UNSIGNED 是 2106 年問題。

而 Facebook 在 MySQL 上會選擇不使用 DATETIMETIMESTAMP 的原因其實跟技術搭不上太多關係,主因是因為 MySQL 根本沒打算修 XDDD

It is my favorite MySQL bug, simply because it forces any reasonable mind not to use TIMESTAMP, and MySQL is never going to fix it (nor will ever understand time). I lost my temper a bit on that bug: https://bugs.mysql.com/bug.php?id=38455

我的猜測是已經爛成一團了,而且大家都有 workaround (呃,其實就是 Facebook 推薦用 INT UNSIGNED 的方法),再考慮到有一票現有程式,在上面狂用 side effect 讓執行結果正確,不如就不要修這種吃力不討好的東西了 XDDD

另外一方面 timezone 資訊其實常常變化,常常需要更新 MySQL 的 timezone database (而這對於維運來說不是什麼開心的事情):

There're few ways around that. One of them is side-load and maintain timezone data inside MySQL itself - it has support for internal timezone database and tracks obscure time shifts like ones for "Pacific War Time" and "Pacific Peace Time". That is operationally feasible (you have to remind yourself to update the database whenever time rules change, and they do change a lot, if you consider every timezone in the world), but has limited value.

這就是為什麼大家遇到 MySQL 時都會推薦用 INT UNSIGNED 了...

另外可以參考三年前的文章「MySQL 裡儲存時間的方式...」,裡面引用了 Baron Schwartz 的說明:

All date and time columns shall be INT UNSIGNED NOT NULL, and shall store a Unix timestamp in UTC.

其實這已經是個 best practice 了...

DVD 的發行時間差異導致盜版

MPAA 資助的報告顯示 DVD 的發行時間差異導致盜版:「DVD Release Delays Boost Piracy and Hurt Sales, Study Shows」,報告在「Windows of Opportunity: The Impact of Piracy and Delayed International Availability on DVD Sales」這邊可以看到。

用真實資料計算得到:

"When we run our regressions on Spain and Italy alone, we observe a 10% drop in sales for every 10-day delay in legal availability, as compared to a 2% drop in sales for every 10-day delay in the entire sample," the paper reads.

"Our results suggest that an additional 10-day delay between the availability of digital piracy and the legitimate DVD release date in a particular country is correlated with a 2-3% reduction in DVD sales in that country," the researchers write.

時效的重要...

前幾天 Twitter 伺服器時間大爆炸的原因...

前幾天 Twitter 伺服器的時間大爆炸,跳到 2015 年:

剛剛在 Hacker News Daily 上看到原因:「If you're using YYYY in your JVM service or %G in anything, fix it now」,原因是這樣:

The single character G instead of Y took down Twitter's API today.
If you're using YYYY in your JVM service or %G in anything, fix it now. You're very likely using the wrong year format.

date
Mon Dec 29 00:44:45 EST 2014
date -u "+%G"
2015

ISO 8601 week numbering has 2015 start this week.

在自己電腦上也可以重製出來:

gslin@home [~] [01:22/W4] date 
Wed Dec 31 01:27:00 CST 2014
gslin@home [~] [01:27/W4] date +%Y
2014
gslin@home [~] [01:27/W4] date +%G
2015

依照說明,%G 的用途是:

year of ISO week number (see %V); normally useful only with %V

用錯好慘啊...

Archives