1. 概述
在本篇 Reddit 應用案例研究中,我們將添加根據用户的時區安排帖子的功能
處理時區問題在技術上極具挑戰性,並且技術選項非常廣泛。我們的首要關注點是,我們需要根據用户的自身(可配置的)時區向用户顯示日期。我們還需要決定日期將以何種格式存儲在數據庫中
2. 一項新的用户偏好 – timezone
首先,我們將添加一個新的字段 – timezone – 到我們現有的偏好中:
@Entity
public class Preference {
...
private String timezone;
}
然後我們簡單地 在用户偏好頁面上配置 timezone – 利用一個簡單但非常實用的 jQuery 插件:
<select id="timezone" name="timezone"></select>
<script>
$(function() {
$('#timezone').timezones();
});
</script>
請注意,默認時區是服務器時區 – 它運行在 UTC。
3. 控制器
現在,讓我們進入有趣的部分。我們需要將用户時區中的日期轉換為服務器時區中的日期:
@Controller
@RequestMapping(value = "/api/scheduledPosts")
public class ScheduledPostRestController {
private static final SimpleDateFormat dateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm");
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public void schedule(
@RequestBody Post post,
@RequestParam(value = "date") String date) throws ParseException
{
post.setSubmissionDate(
calculateSubmissionDate(date, getCurrentUser().getPreference().getTimezone()));
...
}
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.OK)
public void updatePost(
@RequestBody Post post,
@RequestParam(value = "date") String date) throws ParseException
{
post.setSubmissionDate(
calculateSubmissionDate(date, getCurrentUser().getPreference().getTimezone()));
...
}
private synchronized Date calculateSubmissionDate(String dateString, String userTimeZone)
throws ParseException {
dateFormat.setTimeZone(TimeZone.getTimeZone(userTimeZone));
return dateFormat.parse(dateString);
}
}
轉換過程相當簡單,但請注意,它僅發生在寫入操作中——服務器仍然返回 UTC 時間的讀取。
這對我們的客户端來説完全沒問題,因為我們會使用 JS 進行轉換——但值得注意的是,對於讀取操作,服務器仍然返回 UTC 時間的日期。
4. 前端
現在,讓我們看看如何使用用户時區在前端:
4.1. 顯示帖子
我們需要使用用户的時區顯示帖子的 submissionDate:
<table><thead><tr>
<th>帖子標題</th>
<th>提交日期
(<span id="timezone" sec:authentication="principal.preference.timezone">UTC</span>)</th>
</tr></thead></table>
以下是我們的函數 loadPage():
function loadPage(page){
...
$('.table').append('<tr><td>'+post.title+'</td><td>'+
convertDate(post.submissionDate)+'</td></tr>');
...
}
function convertDate(date){
var serverTimezone = [[${#dates.format(#calendars.createToday(), 'z')}]];
var serverDate = moment.tz(date, serverTimezone);
var clientDate = serverDate.clone().tz($("#timezone").html());
var myformat = "YYYY-MM-DD HH:mm";
return clientDate.format(myformat);
}
Moment.js 在時區轉換中有所幫助。
4.2. 安排新帖子
我們也需要修改我們的 schedulePostForm.html:
提交日期 (<span sec:authentication="principal.preference.timezone">UTC</span>)
<input id="date" name="date" />
<script type="text/javascript">
function schedulePost(){
var data = {};
$('form').serializeArray().map(function(x){data[x.name] = x.value;});
$.ajax({
url: 'api/scheduledPosts?date='+$("#date").val(),
data: JSON.stringify(data),
type: 'POST',
contentType:'application/json',
success: function(result) {
window.location.href="scheduledPosts";
},
error: function(error) {
alert(error.responseText);
}
});
}
</script>
最後 – 我們還需要修改我們的 editPostForm.html 以本地化 submissonDate 的舊值:
$(function() {
var serverTimezone = [[${#dates.format(#calendars.createToday(), 'z')}]];
var serverDate = moment.tz($("#date").val(), serverTimezone);
var clientDate = serverDate.clone().tz($("#timezone").html());
var myformat = "YYYY-MM-DD HH:mm";
$("#date").val(clientDate.format(myformat));
});
5. 結論
在本文中,我們介紹了 Reddit 應用中的一個簡單但非常實用的功能——根據您自己的時區查看所有內容。
這曾經是當我使用該應用時的主要痛點——一切都顯示為 UTC 時間。現在——所有日期都已正確顯示在用户的時區中,正如它們應該的那樣。