1. 概要

今回のRedditアプリのケーススタディでは、ユーザーのタイムゾーンに応じてスケジュール投稿を追加します。

タイムゾーンの処理は非常に難しいことで有名であり、技術的な選択肢は広く開かれています。 最初の懸念は、ユーザーが自分の(構成可能な)タイムゾーンに従って日付を表示する必要があることです。 また、データベース日付を保存する形式を決定する必要があります。

2. 新しいユーザー設定–タイムゾーン

まず、既存の設定にtimezoneという新しいフィールドを追加します。

@Entity
public class Preference {
    ...
    private String 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. 投稿を表示する

ユーザーのタイムゾーンを使用して、投稿のsubmitDateを表示する必要があります。

<table><thead><tr>
<th>Post title</th>
<th>Submission Date 
  (<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を変更する必要があります。

Submission Date (<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 を変更して、submitonDateの古い値をローカライズする必要があります。

$(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アプリにシンプルでありながら非常に便利な機能を紹介しました。これは、自分のタイムゾーンに従ってすべてを表示する機能です。

これは、私がアプリを使用していたときの主な問題点の1つであり、すべてがUTCであったという事実です。 現在–すべての日付は、本来あるべきユーザーのタイムゾーンで適切に表示されます。