1package goja
2
3import (
4	"math"
5	"time"
6)
7
8const (
9	dateTimeLayout       = "Mon Jan 02 2006 15:04:05 GMT-0700 (MST)"
10	utcDateTimeLayout    = "Mon, 02 Jan 2006 15:04:05 GMT"
11	isoDateTimeLayout    = "2006-01-02T15:04:05.000Z"
12	dateLayout           = "Mon Jan 02 2006"
13	timeLayout           = "15:04:05 GMT-0700 (MST)"
14	datetimeLayout_en_GB = "01/02/2006, 15:04:05"
15	dateLayout_en_GB     = "01/02/2006"
16	timeLayout_en_GB     = "15:04:05"
17
18	maxTime   = 8.64e15
19	timeUnset = math.MinInt64
20)
21
22type dateObject struct {
23	baseObject
24	msec int64
25}
26
27type dateLayoutDesc struct {
28	layout   string
29	dateOnly bool
30}
31
32var (
33	dateLayoutsNumeric = []dateLayoutDesc{
34		{layout: "2006-01-02T15:04:05Z0700"},
35		{layout: "2006-01-02T15:04:05"},
36		{layout: "2006-01-02", dateOnly: true},
37		{layout: "2006-01-02 15:04:05"},
38
39		{layout: "2006", dateOnly: true},
40		{layout: "2006-01", dateOnly: true},
41
42		{layout: "2006T15:04"},
43		{layout: "2006-01T15:04"},
44		{layout: "2006-01-02T15:04"},
45
46		{layout: "2006T15:04:05"},
47		{layout: "2006-01T15:04:05"},
48
49		{layout: "2006T15:04Z0700"},
50		{layout: "2006-01T15:04Z0700"},
51		{layout: "2006-01-02T15:04Z0700"},
52
53		{layout: "2006T15:04:05Z0700"},
54		{layout: "2006-01T15:04:05Z0700"},
55	}
56
57	dateLayoutsAlpha = []dateLayoutDesc{
58		{layout: time.RFC1123},
59		{layout: time.RFC1123Z},
60		{layout: dateTimeLayout},
61		{layout: time.UnixDate},
62		{layout: time.ANSIC},
63		{layout: time.RubyDate},
64		{layout: "Mon, _2 Jan 2006 15:04:05 GMT-0700 (MST)"},
65		{layout: "Mon, _2 Jan 2006 15:04:05 -0700 (MST)"},
66		{layout: "Jan _2, 2006", dateOnly: true},
67	}
68)
69
70func dateParse(date string) (time.Time, bool) {
71	var t time.Time
72	var err error
73	var layouts []dateLayoutDesc
74	if len(date) > 0 {
75		first := date[0]
76		if first <= '9' && (first >= '0' || first == '-' || first == '+') {
77			layouts = dateLayoutsNumeric
78		} else {
79			layouts = dateLayoutsAlpha
80		}
81	} else {
82		return time.Time{}, false
83	}
84	for _, desc := range layouts {
85		var defLoc *time.Location
86		if desc.dateOnly {
87			defLoc = time.UTC
88		} else {
89			defLoc = time.Local
90		}
91		t, err = parseDate(desc.layout, date, defLoc)
92		if err == nil {
93			break
94		}
95	}
96	if err != nil {
97		return time.Time{}, false
98	}
99	unix := timeToMsec(t)
100	return t, unix >= -maxTime && unix <= maxTime
101}
102
103func (r *Runtime) newDateObject(t time.Time, isSet bool, proto *Object) *Object {
104	v := &Object{runtime: r}
105	d := &dateObject{}
106	v.self = d
107	d.val = v
108	d.class = classDate
109	d.prototype = proto
110	d.extensible = true
111	d.init()
112	if isSet {
113		d.msec = timeToMsec(t)
114	} else {
115		d.msec = timeUnset
116	}
117	return v
118}
119
120func dateFormat(t time.Time) string {
121	return t.Local().Format(dateTimeLayout)
122}
123
124func timeFromMsec(msec int64) time.Time {
125	sec := msec / 1000
126	nsec := (msec % 1000) * 1e6
127	return time.Unix(sec, nsec)
128}
129
130func timeToMsec(t time.Time) int64 {
131	return t.Unix()*1000 + int64(t.Nanosecond())/1e6
132}
133
134func (d *dateObject) toPrimitive() Value {
135	return d.toPrimitiveString()
136}
137
138func (d *dateObject) export(*objectExportCtx) interface{} {
139	if d.isSet() {
140		return d.time()
141	}
142	return nil
143}
144
145func (d *dateObject) setTimeMs(ms int64) Value {
146	if ms >= 0 && ms <= maxTime || ms < 0 && ms >= -maxTime {
147		d.msec = ms
148		return intToValue(ms)
149	}
150
151	d.unset()
152	return _NaN
153}
154
155func (d *dateObject) isSet() bool {
156	return d.msec != timeUnset
157}
158
159func (d *dateObject) unset() {
160	d.msec = timeUnset
161}
162
163func (d *dateObject) time() time.Time {
164	return timeFromMsec(d.msec)
165}
166
167func (d *dateObject) timeUTC() time.Time {
168	return timeFromMsec(d.msec).In(time.UTC)
169}
170