Commits

Miki Tebeka committed adb8748

inital version

Comments (0)

Files changed (2)

+package jtime
+
+import (
+	"fmt"
+	"strconv"
+	"time"
+)
+
+type Time struct {
+	time.Time
+}
+
+type Marshaler interface {
+	Marshal(t Time) ([]byte, error)
+	Unmarshal(data []byte) (Time, error)
+}
+
+var marshaler Marshaler
+
+func SetMarshaler(m Marshaler) {
+	marshaler = m
+}
+
+type FormatMashaler struct {
+	Format string
+}
+
+func (fm *FormatMashaler) Marshal(t Time) ([]byte, error) {
+	return []byte(`"` + t.Format(fm.Format) + `"`), nil
+}
+
+func (fm *FormatMashaler) Unmarshal(data []byte) (Time, error) {
+	if len(data) < 2 {
+		return Time{}, fmt.Errorf("data too short - %v", data)
+	}
+	data = data[1 : len(data)-1]
+	t, err := time.Parse(fm.Format, string(data))
+	if err != nil {
+		return Time{}, err
+	}
+	return Time{t}, err
+}
+
+type UnixMarshaler struct {
+	MSec bool
+}
+
+func (um *UnixMarshaler) Marshal(t Time) ([]byte, error) {
+	data := fmt.Sprintf("%d", t.Unix())
+	if um.MSec {
+		data = fmt.Sprintf("%s%03d", data, t.Nanosecond()/1000)
+	}
+
+	return []byte(data), nil
+}
+
+func (um *UnixMarshaler) Unmarshal(data []byte) (Time, error) {
+	sec, err := strconv.ParseInt(string(data), 10, 64)
+	if err != nil {
+		return Time{}, err
+	}
+	nsec := int64(0)
+	if um.MSec {
+		tmp := sec
+		sec = sec / 1000
+		nsec = (tmp - sec) * 1000
+	}
+
+	return Time{time.Unix(sec, nsec)}, nil
+}
+
+func validJSONTime(t Time) bool {
+	if y := t.Year(); y < 0 || y >= 10000 {
+		return false
+	}
+	return true
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (t Time) MarshalJSON() ([]byte, error) {
+	if !validJSONTime(t) {
+		return nil, fmt.Errorf("vtime.Time.MarshalJson: year outside of range [0,9999]")
+	}
+	return marshaler.Marshal(t)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (t *Time) UnmarshalJSON(data []byte) (err error) {
+	*t, err = marshaler.Unmarshal(data)
+	return
+}
+
+func init() {
+	// Default behaviour
+	SetMarshaler(&FormatMashaler{time.RFC3339Nano})
+}
+package jtime
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+	"time"
+)
+
+const (
+	testYear = 2013
+)
+
+var testYearStr = fmt.Sprintf(`"%d"`, testYear)
+
+type TestStruct struct {
+	Created Time `json:"created"`
+}
+
+func testTime() Time {
+	tt, _ := time.ParseInLocation(`"2006"`, testYearStr, time.UTC)
+	return Time{tt}
+}
+
+func TestFormatMarshaller(t *testing.T) {
+	tm := testTime()
+	fm := &FormatMashaler{"2006"}
+	data, err := fm.Marshal(tm)
+	if err != nil {
+		t.Fatalf("can't marshal: %s", err)
+	}
+
+	if string(data) != testYearStr {
+		t.Fatalf("bad marshal: %s", string(data))
+	}
+
+	pt, err := fm.Unmarshal(data)
+	if err != nil {
+		t.Fatalf("can't unmarshal: %s", err)
+	}
+
+	if pt.Year() != testYear {
+		t.Fatalf("bad year: %d", pt.Year())
+	}
+}
+
+func checkUnmarshal(data []byte, t *testing.T) {
+	ts1 := TestStruct{}
+	err := json.Unmarshal(data, &ts1)
+	if err != nil {
+		t.Fatalf("can't unmarshal - %s", err)
+	}
+
+	if ts1.Created.In(time.UTC).Year() != testYear {
+		t.Fatalf("bad unmarshaled year - %d", ts1.Created.Year())
+	}
+}
+
+func TestJSON(t *testing.T) {
+	SetMarshaler(&FormatMashaler{"2006"})
+
+	ts := TestStruct{testTime()}
+	data, err := json.Marshal(ts)
+	if err != nil {
+		t.Fatalf("can't json.Marshal - %s", err)
+	}
+	if string(data) != `{"created":"2013"}` {
+		t.Fatalf("bad json encoding: %s", string(data))
+	}
+
+	checkUnmarshal(data, t)
+}
+
+func TestJSONUnix(t *testing.T) {
+	SetMarshaler(&UnixMarshaler{})
+	ts := TestStruct{testTime()}
+	data, err := json.Marshal(ts)
+	if err != nil {
+		t.Fatalf("can't json.Marshal - %s", err)
+	}
+	if string(data) != `{"created":1356998400}` {
+		t.Fatalf("bad JSONUnix marshal - %s", string(data))
+	}
+
+	checkUnmarshal(data, t)
+}
+
+func TestJSONUnixMSec(t *testing.T) {
+	SetMarshaler(&UnixMarshaler{true})
+	ts := TestStruct{testTime()}
+	data, err := json.Marshal(ts)
+	if err != nil {
+		t.Fatalf("can't json.Marshal - %s", err)
+	}
+	if string(data) != `{"created":1356998400000}` {
+		t.Fatalf("bad JSONUnix marshal - %s", string(data))
+	}
+
+	checkUnmarshal(data, t)
+}