1from ..base import *
2from .time_helpers import *
3
4TZ_OFFSET = (time.altzone // 3600)
5ABS_OFFSET = abs(TZ_OFFSET)
6TZ_NAME = time.tzname[1]
7ISO_FORMAT = '%s-%s-%sT%s:%s:%s.%sZ'
8
9
10@Js
11def Date(year, month, date, hours, minutes, seconds, ms):
12    return now().to_string()
13
14
15Date.Class = 'Date'
16
17
18def now():
19    return PyJsDate(int(time.time() * 1000), prototype=DatePrototype)
20
21
22@Js
23def UTC(year, month, date, hours, minutes, seconds, ms):  # todo complete this
24    args = arguments
25    y = args[0].to_number()
26    m = args[1].to_number()
27    l = len(args)
28    dt = args[2].to_number() if l > 2 else Js(1)
29    h = args[3].to_number() if l > 3 else Js(0)
30    mi = args[4].to_number() if l > 4 else Js(0)
31    sec = args[5].to_number() if l > 5 else Js(0)
32    mili = args[6].to_number() if l > 6 else Js(0)
33    if not y.is_nan() and 0 <= y.value <= 99:
34        y = y + Js(1900)
35    return TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
36
37
38@Js
39def parse(string):
40    return PyJsDate(
41        TimeClip(parse_date(string.to_string().value)),
42        prototype=DatePrototype)
43
44
45Date.define_own_property('now', {
46    'value': Js(now),
47    'enumerable': False,
48    'writable': True,
49    'configurable': True
50})
51
52Date.define_own_property('parse', {
53    'value': parse,
54    'enumerable': False,
55    'writable': True,
56    'configurable': True
57})
58
59Date.define_own_property('UTC', {
60    'value': UTC,
61    'enumerable': False,
62    'writable': True,
63    'configurable': True
64})
65
66
67class PyJsDate(PyJs):
68    Class = 'Date'
69    extensible = True
70
71    def __init__(self, value, prototype=None):
72        self.value = value
73        self.own = {}
74        self.prototype = prototype
75
76    # todo fix this problematic datetime part
77    def to_local_dt(self):
78        return datetime.datetime(1970, 1, 1) + datetime.timedelta(
79            seconds=UTCToLocal(self.value) // 1000)
80
81    def to_utc_dt(self):
82        return datetime.datetime(1970, 1, 1) + datetime.timedelta(
83            seconds=self.value // 1000)
84
85    def local_strftime(self, pattern):
86        if self.value is NaN:
87            return 'Invalid Date'
88        try:
89            dt = self.to_local_dt()
90        except:
91            raise MakeError(
92                'TypeError',
93                'unsupported date range. Will fix in future versions')
94        try:
95            return dt.strftime(pattern)
96        except:
97            raise MakeError(
98                'TypeError',
99                'Could not generate date string from this date (limitations of python.datetime)'
100            )
101
102    def utc_strftime(self, pattern):
103        if self.value is NaN:
104            return 'Invalid Date'
105        try:
106            dt = self.to_utc_dt()
107        except:
108            raise MakeError(
109                'TypeError',
110                'unsupported date range. Will fix in future versions')
111        try:
112            return dt.strftime(pattern)
113        except:
114            raise MakeError(
115                'TypeError',
116                'Could not generate date string from this date (limitations of python.datetime)'
117            )
118
119
120def parse_date(py_string):  # todo support all date string formats
121    try:
122        try:
123            dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ")
124        except:
125            dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%SZ")
126        return MakeDate(
127            MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
128            MakeTime(
129                Js(dt.hour), Js(dt.minute), Js(dt.second),
130                Js(dt.microsecond // 1000)))
131    except:
132        raise MakeError(
133            'TypeError',
134            'Could not parse date %s - unsupported date format. Currently only supported format is RFC3339 utc. Sorry!'
135            % py_string)
136
137
138def date_constructor(*args):
139    if len(args) >= 2:
140        return date_constructor2(*args)
141    elif len(args) == 1:
142        return date_constructor1(args[0])
143    else:
144        return date_constructor0()
145
146
147def date_constructor0():
148    return now()
149
150
151def date_constructor1(value):
152    v = value.to_primitive()
153    if v._type() == 'String':
154        v = parse_date(v.value)
155    else:
156        v = v.to_int()
157    return PyJsDate(TimeClip(v), prototype=DatePrototype)
158
159
160def date_constructor2(*args):
161    y = args[0].to_number()
162    m = args[1].to_number()
163    l = len(args)
164    dt = args[2].to_number() if l > 2 else Js(1)
165    h = args[3].to_number() if l > 3 else Js(0)
166    mi = args[4].to_number() if l > 4 else Js(0)
167    sec = args[5].to_number() if l > 5 else Js(0)
168    mili = args[6].to_number() if l > 6 else Js(0)
169    if not y.is_nan() and 0 <= y.value <= 99:
170        y = y + Js(1900)
171    t = TimeClip(
172        LocalToUTC(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))))
173    return PyJsDate(t, prototype=DatePrototype)
174
175
176Date.create = date_constructor
177
178DatePrototype = PyJsDate(float('nan'), prototype=ObjectPrototype)
179
180
181def check_date(obj):
182    if obj.Class != 'Date':
183        raise MakeError('TypeError', 'this is not a Date object')
184
185
186class DateProto:
187    def toString():
188        check_date(this)
189        if this.value is NaN:
190            return 'Invalid Date'
191        offset = (UTCToLocal(this.value) - this.value) // msPerHour
192        return this.local_strftime(
193            '%a %b %d %Y %H:%M:%S GMT') + '%s00 (%s)' % (pad(
194                offset, 2, True), GetTimeZoneName(this.value))
195
196    def toDateString():
197        check_date(this)
198        return this.local_strftime('%d %B %Y')
199
200    def toTimeString():
201        check_date(this)
202        return this.local_strftime('%H:%M:%S')
203
204    def toLocaleString():
205        check_date(this)
206        return this.local_strftime('%d %B %Y %H:%M:%S')
207
208    def toLocaleDateString():
209        check_date(this)
210        return this.local_strftime('%d %B %Y')
211
212    def toLocaleTimeString():
213        check_date(this)
214        return this.local_strftime('%H:%M:%S')
215
216    def valueOf():
217        check_date(this)
218        return this.value
219
220    def getTime():
221        check_date(this)
222        return this.value
223
224    def getFullYear():
225        check_date(this)
226        if this.value is NaN:
227            return NaN
228        return YearFromTime(UTCToLocal(this.value))
229
230    def getUTCFullYear():
231        check_date(this)
232        if this.value is NaN:
233            return NaN
234        return YearFromTime(this.value)
235
236    def getMonth():
237        check_date(this)
238        if this.value is NaN:
239            return NaN
240        return MonthFromTime(UTCToLocal(this.value))
241
242    def getDate():
243        check_date(this)
244        if this.value is NaN:
245            return NaN
246        return DateFromTime(UTCToLocal(this.value))
247
248    def getUTCMonth():
249        check_date(this)
250        if this.value is NaN:
251            return NaN
252        return MonthFromTime(this.value)
253
254    def getUTCDate():
255        check_date(this)
256        if this.value is NaN:
257            return NaN
258        return DateFromTime(this.value)
259
260    def getDay():
261        check_date(this)
262        if this.value is NaN:
263            return NaN
264        return WeekDay(UTCToLocal(this.value))
265
266    def getUTCDay():
267        check_date(this)
268        if this.value is NaN:
269            return NaN
270        return WeekDay(this.value)
271
272    def getHours():
273        check_date(this)
274        if this.value is NaN:
275            return NaN
276        return HourFromTime(UTCToLocal(this.value))
277
278    def getUTCHours():
279        check_date(this)
280        if this.value is NaN:
281            return NaN
282        return HourFromTime(this.value)
283
284    def getMinutes():
285        check_date(this)
286        if this.value is NaN:
287            return NaN
288        return MinFromTime(UTCToLocal(this.value))
289
290    def getUTCMinutes():
291        check_date(this)
292        if this.value is NaN:
293            return NaN
294        return MinFromTime(this.value)
295
296    def getSeconds():
297        check_date(this)
298        if this.value is NaN:
299            return NaN
300        return SecFromTime(UTCToLocal(this.value))
301
302    def getUTCSeconds():
303        check_date(this)
304        if this.value is NaN:
305            return NaN
306        return SecFromTime(this.value)
307
308    def getMilliseconds():
309        check_date(this)
310        if this.value is NaN:
311            return NaN
312        return msFromTime(UTCToLocal(this.value))
313
314    def getUTCMilliseconds():
315        check_date(this)
316        if this.value is NaN:
317            return NaN
318        return msFromTime(this.value)
319
320    def getTimezoneOffset():
321        check_date(this)
322        if this.value is NaN:
323            return NaN
324        return (this.value - UTCToLocal(this.value)) // 60000
325
326    def setTime(time):
327        check_date(this)
328        this.value = TimeClip(time.to_number().to_int())
329        return this.value
330
331    def setMilliseconds(ms):
332        check_date(this)
333        t = UTCToLocal(this.value)
334        tim = MakeTime(
335            Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms)
336        u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
337        this.value = u
338        return u
339
340    def setUTCMilliseconds(ms):
341        check_date(this)
342        t = this.value
343        tim = MakeTime(
344            Js(HourFromTime(t)), Js(MinFromTime(t)), Js(SecFromTime(t)), ms)
345        u = TimeClip(MakeDate(Day(t), tim))
346        this.value = u
347        return u
348
349    def setSeconds(sec, ms=None):
350        check_date(this)
351        t = UTCToLocal(this.value)
352        s = sec.to_number()
353        if not ms is None: milli = Js(msFromTime(t))
354        else: milli = ms.to_number()
355        date = MakeDate(
356            Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli))
357        u = TimeClip(LocalToUTC(date))
358        this.value = u
359        return u
360
361    def setUTCSeconds(sec, ms=None):
362        check_date(this)
363        t = this.value
364        s = sec.to_number()
365        if not ms is None: milli = Js(msFromTime(t))
366        else: milli = ms.to_number()
367        date = MakeDate(
368            Day(t), MakeTime(Js(HourFromTime(t)), Js(MinFromTime(t)), s, milli))
369        v = TimeClip(date)
370        this.value = v
371        return v
372
373    def setMinutes(min, sec=None, ms=None):
374        check_date(this)
375        t = UTCToLocal(this.value)
376        m = min.to_number()
377        if not sec is None: s = Js(SecFromTime(t))
378        else: s = sec.to_number()
379        if not ms is None: milli = Js(msFromTime(t))
380        else: milli = ms.to_number()
381        date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli))
382        u = TimeClip(LocalToUTC(date))
383        this.value = u
384        return u
385
386    def setUTCMinutes(min, sec=None, ms=None):
387        check_date(this)
388        t = this.value
389        m = min.to_number()
390        if not sec is None: s = Js(SecFromTime(t))
391        else: s = sec.to_number()
392        if not ms is None: milli = Js(msFromTime(t))
393        else: milli = ms.to_number()
394        date = MakeDate(Day(t), MakeTime(Js(HourFromTime(t)), m, s, milli))
395        v = TimeClip(date)
396        this.value = v
397        return v
398
399    def setHours(hour, min=None, sec=None, ms=None):
400        check_date(this)
401        t = UTCToLocal(this.value)
402        h = hour.to_number()
403        if not min is None: m = Js(MinFromTime(t))
404        else: m = min.to_number()
405        if not sec is None: s = Js(SecFromTime(t))
406        else: s = sec.to_number()
407        if not ms is None: milli = Js(msFromTime(t))
408        else: milli = ms.to_number()
409        date = MakeDate(Day(t), MakeTime(h, m, s, milli))
410        u = TimeClip(LocalToUTC(date))
411        this.value = u
412        return u
413
414    def setUTCHours(hour, min=None, sec=None, ms=None):
415        check_date(this)
416        t = this.value
417        h = hour.to_number()
418        if not min is None: m = Js(MinFromTime(t))
419        else: m = min.to_number()
420        if not sec is None: s = Js(SecFromTime(t))
421        else: s = sec.to_number()
422        if not ms is None: milli = Js(msFromTime(t))
423        else: milli = ms.to_number()
424        date = MakeDate(Day(t), MakeTime(h, m, s, milli))
425        v = TimeClip(date)
426        this.value = v
427        return v
428
429    def setDate(date):
430        check_date(this)
431        t = UTCToLocal(this.value)
432        dt = date.to_number()
433        newDate = MakeDate(
434            MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t))
435        u = TimeClip(LocalToUTC(newDate))
436        this.value = u
437        return u
438
439    def setUTCDate(date):
440        check_date(this)
441        t = this.value
442        dt = date.to_number()
443        newDate = MakeDate(
444            MakeDay(Js(YearFromTime(t)), Js(MonthFromTime(t)), dt), TimeWithinDay(t))
445        v = TimeClip(newDate)
446        this.value = v
447        return v
448
449    def setMonth(month, date=None):
450        check_date(this)
451        t = UTCToLocal(this.value)
452        m = month.to_number()
453        if not date is None: dt = Js(DateFromTime(t))
454        else: dt = date.to_number()
455        newDate = MakeDate(
456            MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t))
457        u = TimeClip(LocalToUTC(newDate))
458        this.value = u
459        return u
460
461    def setUTCMonth(month, date=None):
462        check_date(this)
463        t = this.value
464        m = month.to_number()
465        if not date is None: dt = Js(DateFromTime(t))
466        else: dt = date.to_number()
467        newDate = MakeDate(
468            MakeDay(Js(YearFromTime(t)), m, dt), TimeWithinDay(t))
469        v = TimeClip(newDate)
470        this.value = v
471        return v
472
473    def setFullYear(year, month=None, date=None):
474        check_date(this)
475        if not this.value is NaN: t = UTCToLocal(this.value)
476        else: t = 0
477        y = year.to_number()
478        if not month is None: m = Js(MonthFromTime(t))
479        else: m = month.to_number()
480        if not date is None: dt = Js(DateFromTime(t))
481        else: dt = date.to_number()
482        newDate = MakeDate(
483            MakeDay(y, m, dt), TimeWithinDay(t))
484        u = TimeClip(LocalToUTC(newDate))
485        this.value = u
486        return u
487
488    def setUTCFullYear(year, month=None, date=None):
489        check_date(this)
490        if not this.value is NaN: t = UTCToLocal(this.value)
491        else: t = 0
492        y = year.to_number()
493        if not month is None: m = Js(MonthFromTime(t))
494        else: m = month.to_number()
495        if not date is None: dt = Js(DateFromTime(t))
496        else: dt = date.to_number()
497        newDate = MakeDate(
498            MakeDay(y, m, dt), TimeWithinDay(t))
499        v = TimeClip(newDate)
500        this.value = v
501        return v
502
503    def toUTCString():
504        check_date(this)
505        return this.utc_strftime('%d %B %Y %H:%M:%S')
506
507    def toISOString():
508        check_date(this)
509        t = this.value
510        year = YearFromTime(t)
511        month, day, hour, minute, second, milli = pad(
512            MonthFromTime(t) + 1), pad(DateFromTime(t)), pad(
513                HourFromTime(t)), pad(MinFromTime(t)), pad(
514                    SecFromTime(t)), pad(msFromTime(t))
515        return ISO_FORMAT % (unicode(year) if 0 <= year <= 9999 else pad(
516            year, 6, True), month, day, hour, minute, second, milli)
517
518    def toJSON(key):
519        o = this.to_object()
520        tv = o.to_primitive('Number')
521        if tv.Class == 'Number' and not tv.is_finite():
522            return this.null
523        toISO = o.get('toISOString')
524        if not toISO.is_callable():
525            raise this.MakeError('TypeError', 'toISOString is not callable')
526        return toISO.call(o, ())
527
528
529def pad(num, n=2, sign=False):
530    '''returns n digit string representation of the num'''
531    s = unicode(abs(num))
532    if len(s) < n:
533        s = '0' * (n - len(s)) + s
534    if not sign:
535        return s
536    if num >= 0:
537        return '+' + s
538    else:
539        return '-' + s
540
541
542fill_prototype(DatePrototype, DateProto, default_attrs)
543
544Date.define_own_property(
545    'prototype', {
546        'value': DatePrototype,
547        'enumerable': False,
548        'writable': False,
549        'configurable': False
550    })
551
552DatePrototype.define_own_property('constructor', {
553    'value': Date,
554    'enumerable': False,
555    'writable': True,
556    'configurable': True
557})
558