1package otto
2
3import (
4	"math"
5	Time "time"
6)
7
8// Date
9
10const (
11	// TODO Be like V8?
12	// builtinDate_goDateTimeLayout = "Mon Jan 2 2006 15:04:05 GMT-0700 (MST)"
13	builtinDate_goDateTimeLayout = Time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"
14	builtinDate_goDateLayout     = "Mon, 02 Jan 2006"
15	builtinDate_goTimeLayout     = "15:04:05 MST"
16)
17
18func builtinDate(call FunctionCall) Value {
19	date := &_dateObject{}
20	date.Set(newDateTime([]Value{}, Time.Local))
21	return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
22}
23
24func builtinNewDate(self *_object, argumentList []Value) Value {
25	return toValue_object(self.runtime.newDate(newDateTime(argumentList, Time.Local)))
26}
27
28func builtinDate_toString(call FunctionCall) Value {
29	date := dateObjectOf(call.runtime, call.thisObject())
30	if date.isNaN {
31		return toValue_string("Invalid Date")
32	}
33	return toValue_string(date.Time().Local().Format(builtinDate_goDateTimeLayout))
34}
35
36func builtinDate_toDateString(call FunctionCall) Value {
37	date := dateObjectOf(call.runtime, call.thisObject())
38	if date.isNaN {
39		return toValue_string("Invalid Date")
40	}
41	return toValue_string(date.Time().Local().Format(builtinDate_goDateLayout))
42}
43
44func builtinDate_toTimeString(call FunctionCall) Value {
45	date := dateObjectOf(call.runtime, call.thisObject())
46	if date.isNaN {
47		return toValue_string("Invalid Date")
48	}
49	return toValue_string(date.Time().Local().Format(builtinDate_goTimeLayout))
50}
51
52func builtinDate_toUTCString(call FunctionCall) Value {
53	date := dateObjectOf(call.runtime, call.thisObject())
54	if date.isNaN {
55		return toValue_string("Invalid Date")
56	}
57	return toValue_string(date.Time().Format(builtinDate_goDateTimeLayout))
58}
59
60func builtinDate_toISOString(call FunctionCall) Value {
61	date := dateObjectOf(call.runtime, call.thisObject())
62	if date.isNaN {
63		return toValue_string("Invalid Date")
64	}
65	return toValue_string(date.Time().Format("2006-01-02T15:04:05.000Z"))
66}
67
68func builtinDate_toJSON(call FunctionCall) Value {
69	object := call.thisObject()
70	value := object.DefaultValue(defaultValueHintNumber) // FIXME object.primitiveNumberValue
71	{                                                    // FIXME value.isFinite
72		value := value.float64()
73		if math.IsNaN(value) || math.IsInf(value, 0) {
74			return nullValue
75		}
76	}
77	toISOString := object.get("toISOString")
78	if !toISOString.isCallable() {
79		// FIXME
80		panic(call.runtime.panicTypeError())
81	}
82	return toISOString.call(call.runtime, toValue_object(object), []Value{})
83}
84
85func builtinDate_toGMTString(call FunctionCall) Value {
86	date := dateObjectOf(call.runtime, call.thisObject())
87	if date.isNaN {
88		return toValue_string("Invalid Date")
89	}
90	return toValue_string(date.Time().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
91}
92
93func builtinDate_getTime(call FunctionCall) Value {
94	date := dateObjectOf(call.runtime, call.thisObject())
95	if date.isNaN {
96		return NaNValue()
97	}
98	// We do this (convert away from a float) so the user
99	// does not get something back in exponential notation
100	return toValue_int64(int64(date.Epoch()))
101}
102
103func builtinDate_setTime(call FunctionCall) Value {
104	object := call.thisObject()
105	date := dateObjectOf(call.runtime, call.thisObject())
106	date.Set(call.Argument(0).float64())
107	object.value = date
108	return date.Value()
109}
110
111func _builtinDate_beforeSet(call FunctionCall, argumentLimit int, timeLocal bool) (*_object, *_dateObject, *_ecmaTime, []int) {
112	object := call.thisObject()
113	date := dateObjectOf(call.runtime, call.thisObject())
114	if date.isNaN {
115		return nil, nil, nil, nil
116	}
117
118	if argumentLimit > len(call.ArgumentList) {
119		argumentLimit = len(call.ArgumentList)
120	}
121
122	if argumentLimit == 0 {
123		object.value = invalidDateObject
124		return nil, nil, nil, nil
125	}
126
127	valueList := make([]int, argumentLimit)
128	for index := 0; index < argumentLimit; index++ {
129		value := call.ArgumentList[index]
130		nm := value.number()
131		switch nm.kind {
132		case numberInteger, numberFloat:
133		default:
134			object.value = invalidDateObject
135			return nil, nil, nil, nil
136		}
137		valueList[index] = int(nm.int64)
138	}
139	baseTime := date.Time()
140	if timeLocal {
141		baseTime = baseTime.Local()
142	}
143	ecmaTime := ecmaTime(baseTime)
144	return object, &date, &ecmaTime, valueList
145}
146
147func builtinDate_parse(call FunctionCall) Value {
148	date := call.Argument(0).string()
149	return toValue_float64(dateParse(date))
150}
151
152func builtinDate_UTC(call FunctionCall) Value {
153	return toValue_float64(newDateTime(call.ArgumentList, Time.UTC))
154}
155
156func builtinDate_now(call FunctionCall) Value {
157	call.ArgumentList = []Value(nil)
158	return builtinDate_UTC(call)
159}
160
161// This is a placeholder
162func builtinDate_toLocaleString(call FunctionCall) Value {
163	date := dateObjectOf(call.runtime, call.thisObject())
164	if date.isNaN {
165		return toValue_string("Invalid Date")
166	}
167	return toValue_string(date.Time().Local().Format("2006-01-02 15:04:05"))
168}
169
170// This is a placeholder
171func builtinDate_toLocaleDateString(call FunctionCall) Value {
172	date := dateObjectOf(call.runtime, call.thisObject())
173	if date.isNaN {
174		return toValue_string("Invalid Date")
175	}
176	return toValue_string(date.Time().Local().Format("2006-01-02"))
177}
178
179// This is a placeholder
180func builtinDate_toLocaleTimeString(call FunctionCall) Value {
181	date := dateObjectOf(call.runtime, call.thisObject())
182	if date.isNaN {
183		return toValue_string("Invalid Date")
184	}
185	return toValue_string(date.Time().Local().Format("15:04:05"))
186}
187
188func builtinDate_valueOf(call FunctionCall) Value {
189	date := dateObjectOf(call.runtime, call.thisObject())
190	if date.isNaN {
191		return NaNValue()
192	}
193	return date.Value()
194}
195
196func builtinDate_getYear(call FunctionCall) Value {
197	// Will throw a TypeError is ThisObject is nil or
198	// does not have Class of "Date"
199	date := dateObjectOf(call.runtime, call.thisObject())
200	if date.isNaN {
201		return NaNValue()
202	}
203	return toValue_int(date.Time().Local().Year() - 1900)
204}
205
206func builtinDate_getFullYear(call FunctionCall) Value {
207	// Will throw a TypeError is ThisObject is nil or
208	// does not have Class of "Date"
209	date := dateObjectOf(call.runtime, call.thisObject())
210	if date.isNaN {
211		return NaNValue()
212	}
213	return toValue_int(date.Time().Local().Year())
214}
215
216func builtinDate_getUTCFullYear(call FunctionCall) Value {
217	date := dateObjectOf(call.runtime, call.thisObject())
218	if date.isNaN {
219		return NaNValue()
220	}
221	return toValue_int(date.Time().Year())
222}
223
224func builtinDate_getMonth(call FunctionCall) Value {
225	date := dateObjectOf(call.runtime, call.thisObject())
226	if date.isNaN {
227		return NaNValue()
228	}
229	return toValue_int(dateFromGoMonth(date.Time().Local().Month()))
230}
231
232func builtinDate_getUTCMonth(call FunctionCall) Value {
233	date := dateObjectOf(call.runtime, call.thisObject())
234	if date.isNaN {
235		return NaNValue()
236	}
237	return toValue_int(dateFromGoMonth(date.Time().Month()))
238}
239
240func builtinDate_getDate(call FunctionCall) Value {
241	date := dateObjectOf(call.runtime, call.thisObject())
242	if date.isNaN {
243		return NaNValue()
244	}
245	return toValue_int(date.Time().Local().Day())
246}
247
248func builtinDate_getUTCDate(call FunctionCall) Value {
249	date := dateObjectOf(call.runtime, call.thisObject())
250	if date.isNaN {
251		return NaNValue()
252	}
253	return toValue_int(date.Time().Day())
254}
255
256func builtinDate_getDay(call FunctionCall) Value {
257	// Actually day of the week
258	date := dateObjectOf(call.runtime, call.thisObject())
259	if date.isNaN {
260		return NaNValue()
261	}
262	return toValue_int(dateFromGoDay(date.Time().Local().Weekday()))
263}
264
265func builtinDate_getUTCDay(call FunctionCall) Value {
266	date := dateObjectOf(call.runtime, call.thisObject())
267	if date.isNaN {
268		return NaNValue()
269	}
270	return toValue_int(dateFromGoDay(date.Time().Weekday()))
271}
272
273func builtinDate_getHours(call FunctionCall) Value {
274	date := dateObjectOf(call.runtime, call.thisObject())
275	if date.isNaN {
276		return NaNValue()
277	}
278	return toValue_int(date.Time().Local().Hour())
279}
280
281func builtinDate_getUTCHours(call FunctionCall) Value {
282	date := dateObjectOf(call.runtime, call.thisObject())
283	if date.isNaN {
284		return NaNValue()
285	}
286	return toValue_int(date.Time().Hour())
287}
288
289func builtinDate_getMinutes(call FunctionCall) Value {
290	date := dateObjectOf(call.runtime, call.thisObject())
291	if date.isNaN {
292		return NaNValue()
293	}
294	return toValue_int(date.Time().Local().Minute())
295}
296
297func builtinDate_getUTCMinutes(call FunctionCall) Value {
298	date := dateObjectOf(call.runtime, call.thisObject())
299	if date.isNaN {
300		return NaNValue()
301	}
302	return toValue_int(date.Time().Minute())
303}
304
305func builtinDate_getSeconds(call FunctionCall) Value {
306	date := dateObjectOf(call.runtime, call.thisObject())
307	if date.isNaN {
308		return NaNValue()
309	}
310	return toValue_int(date.Time().Local().Second())
311}
312
313func builtinDate_getUTCSeconds(call FunctionCall) Value {
314	date := dateObjectOf(call.runtime, call.thisObject())
315	if date.isNaN {
316		return NaNValue()
317	}
318	return toValue_int(date.Time().Second())
319}
320
321func builtinDate_getMilliseconds(call FunctionCall) Value {
322	date := dateObjectOf(call.runtime, call.thisObject())
323	if date.isNaN {
324		return NaNValue()
325	}
326	return toValue_int(date.Time().Local().Nanosecond() / (100 * 100 * 100))
327}
328
329func builtinDate_getUTCMilliseconds(call FunctionCall) Value {
330	date := dateObjectOf(call.runtime, call.thisObject())
331	if date.isNaN {
332		return NaNValue()
333	}
334	return toValue_int(date.Time().Nanosecond() / (100 * 100 * 100))
335}
336
337func builtinDate_getTimezoneOffset(call FunctionCall) Value {
338	date := dateObjectOf(call.runtime, call.thisObject())
339	if date.isNaN {
340		return NaNValue()
341	}
342	timeLocal := date.Time().Local()
343	// Is this kosher?
344	timeLocalAsUTC := Time.Date(
345		timeLocal.Year(),
346		timeLocal.Month(),
347		timeLocal.Day(),
348		timeLocal.Hour(),
349		timeLocal.Minute(),
350		timeLocal.Second(),
351		timeLocal.Nanosecond(),
352		Time.UTC,
353	)
354	return toValue_float64(date.Time().Sub(timeLocalAsUTC).Seconds() / 60)
355}
356
357func builtinDate_setMilliseconds(call FunctionCall) Value {
358	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
359	if ecmaTime == nil {
360		return NaNValue()
361	}
362
363	ecmaTime.millisecond = value[0]
364
365	date.SetTime(ecmaTime.goTime())
366	object.value = *date
367	return date.Value()
368}
369
370func builtinDate_setUTCMilliseconds(call FunctionCall) Value {
371	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false)
372	if ecmaTime == nil {
373		return NaNValue()
374	}
375
376	ecmaTime.millisecond = value[0]
377
378	date.SetTime(ecmaTime.goTime())
379	object.value = *date
380	return date.Value()
381}
382
383func builtinDate_setSeconds(call FunctionCall) Value {
384	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true)
385	if ecmaTime == nil {
386		return NaNValue()
387	}
388
389	if len(value) > 1 {
390		ecmaTime.millisecond = value[1]
391	}
392	ecmaTime.second = value[0]
393
394	date.SetTime(ecmaTime.goTime())
395	object.value = *date
396	return date.Value()
397}
398
399func builtinDate_setUTCSeconds(call FunctionCall) Value {
400	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false)
401	if ecmaTime == nil {
402		return NaNValue()
403	}
404
405	if len(value) > 1 {
406		ecmaTime.millisecond = value[1]
407	}
408	ecmaTime.second = value[0]
409
410	date.SetTime(ecmaTime.goTime())
411	object.value = *date
412	return date.Value()
413}
414
415func builtinDate_setMinutes(call FunctionCall) Value {
416	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true)
417	if ecmaTime == nil {
418		return NaNValue()
419	}
420
421	if len(value) > 2 {
422		ecmaTime.millisecond = value[2]
423		ecmaTime.second = value[1]
424	} else if len(value) > 1 {
425		ecmaTime.second = value[1]
426	}
427	ecmaTime.minute = value[0]
428
429	date.SetTime(ecmaTime.goTime())
430	object.value = *date
431	return date.Value()
432}
433
434func builtinDate_setUTCMinutes(call FunctionCall) Value {
435	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false)
436	if ecmaTime == nil {
437		return NaNValue()
438	}
439
440	if len(value) > 2 {
441		ecmaTime.millisecond = value[2]
442		ecmaTime.second = value[1]
443	} else if len(value) > 1 {
444		ecmaTime.second = value[1]
445	}
446	ecmaTime.minute = value[0]
447
448	date.SetTime(ecmaTime.goTime())
449	object.value = *date
450	return date.Value()
451}
452
453func builtinDate_setHours(call FunctionCall) Value {
454	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, true)
455	if ecmaTime == nil {
456		return NaNValue()
457	}
458
459	if len(value) > 3 {
460		ecmaTime.millisecond = value[3]
461		ecmaTime.second = value[2]
462		ecmaTime.minute = value[1]
463	} else if len(value) > 2 {
464		ecmaTime.second = value[2]
465		ecmaTime.minute = value[1]
466	} else if len(value) > 1 {
467		ecmaTime.minute = value[1]
468	}
469	ecmaTime.hour = value[0]
470
471	date.SetTime(ecmaTime.goTime())
472	object.value = *date
473	return date.Value()
474}
475
476func builtinDate_setUTCHours(call FunctionCall) Value {
477	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 4, false)
478	if ecmaTime == nil {
479		return NaNValue()
480	}
481
482	if len(value) > 3 {
483		ecmaTime.millisecond = value[3]
484		ecmaTime.second = value[2]
485		ecmaTime.minute = value[1]
486	} else if len(value) > 2 {
487		ecmaTime.second = value[2]
488		ecmaTime.minute = value[1]
489	} else if len(value) > 1 {
490		ecmaTime.minute = value[1]
491	}
492	ecmaTime.hour = value[0]
493
494	date.SetTime(ecmaTime.goTime())
495	object.value = *date
496	return date.Value()
497}
498
499func builtinDate_setDate(call FunctionCall) Value {
500	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
501	if ecmaTime == nil {
502		return NaNValue()
503	}
504
505	ecmaTime.day = value[0]
506
507	date.SetTime(ecmaTime.goTime())
508	object.value = *date
509	return date.Value()
510}
511
512func builtinDate_setUTCDate(call FunctionCall) Value {
513	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, false)
514	if ecmaTime == nil {
515		return NaNValue()
516	}
517
518	ecmaTime.day = value[0]
519
520	date.SetTime(ecmaTime.goTime())
521	object.value = *date
522	return date.Value()
523}
524
525func builtinDate_setMonth(call FunctionCall) Value {
526	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, true)
527	if ecmaTime == nil {
528		return NaNValue()
529	}
530
531	if len(value) > 1 {
532		ecmaTime.day = value[1]
533	}
534	ecmaTime.month = value[0]
535
536	date.SetTime(ecmaTime.goTime())
537	object.value = *date
538	return date.Value()
539}
540
541func builtinDate_setUTCMonth(call FunctionCall) Value {
542	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 2, false)
543	if ecmaTime == nil {
544		return NaNValue()
545	}
546
547	if len(value) > 1 {
548		ecmaTime.day = value[1]
549	}
550	ecmaTime.month = value[0]
551
552	date.SetTime(ecmaTime.goTime())
553	object.value = *date
554	return date.Value()
555}
556
557func builtinDate_setYear(call FunctionCall) Value {
558	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 1, true)
559	if ecmaTime == nil {
560		return NaNValue()
561	}
562
563	year := value[0]
564	if 0 <= year && year <= 99 {
565		year += 1900
566	}
567	ecmaTime.year = year
568
569	date.SetTime(ecmaTime.goTime())
570	object.value = *date
571	return date.Value()
572}
573
574func builtinDate_setFullYear(call FunctionCall) Value {
575	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, true)
576	if ecmaTime == nil {
577		return NaNValue()
578	}
579
580	if len(value) > 2 {
581		ecmaTime.day = value[2]
582		ecmaTime.month = value[1]
583	} else if len(value) > 1 {
584		ecmaTime.month = value[1]
585	}
586	ecmaTime.year = value[0]
587
588	date.SetTime(ecmaTime.goTime())
589	object.value = *date
590	return date.Value()
591}
592
593func builtinDate_setUTCFullYear(call FunctionCall) Value {
594	object, date, ecmaTime, value := _builtinDate_beforeSet(call, 3, false)
595	if ecmaTime == nil {
596		return NaNValue()
597	}
598
599	if len(value) > 2 {
600		ecmaTime.day = value[2]
601		ecmaTime.month = value[1]
602	} else if len(value) > 1 {
603		ecmaTime.month = value[1]
604	}
605	ecmaTime.year = value[0]
606
607	date.SetTime(ecmaTime.goTime())
608	object.value = *date
609	return date.Value()
610}
611
612// toUTCString
613// toISOString
614// toJSONString
615// toJSON
616