|
@@ -1,10 +1,12 @@
|
|
|
# coding: utf-8
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
+import calendar
|
|
|
import itertools
|
|
|
import json
|
|
|
import operator
|
|
|
import re
|
|
|
+import time
|
|
|
|
|
|
from functools import update_wrapper, wraps
|
|
|
|
|
@@ -12,8 +14,10 @@ from .utils import (
|
|
|
error_to_compat_str,
|
|
|
ExtractorError,
|
|
|
float_or_none,
|
|
|
+ int_or_none,
|
|
|
js_to_json,
|
|
|
remove_quotes,
|
|
|
+ str_or_none,
|
|
|
unified_timestamp,
|
|
|
variadic,
|
|
|
write_string,
|
|
@@ -475,6 +479,73 @@ class JSInterpreter(object):
|
|
|
flags |= cls.RE_FLAGS[ch]
|
|
|
return flags, expr[idx + 1:]
|
|
|
|
|
|
+ class JS_Date(object):
|
|
|
+ _t = None
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def __ymd_etc(*args, **kw_is_utc):
|
|
|
+ # args: year, monthIndex, day, hours, minutes, seconds, milliseconds
|
|
|
+ is_utc = kw_is_utc.get('is_utc', False)
|
|
|
+
|
|
|
+ args = list(args[:7])
|
|
|
+ args += [0] * (9 - len(args))
|
|
|
+ args[1] += 1 # month 0..11 -> 1..12
|
|
|
+ ms = args[6]
|
|
|
+ for i in range(6, 9):
|
|
|
+ args[i] = -1 # don't know
|
|
|
+ if is_utc:
|
|
|
+ args[-1] = 1
|
|
|
+ # TODO: [MDN] When a segment overflows or underflows its expected
|
|
|
+ # range, it usually "carries over to" or "borrows from" the higher segment.
|
|
|
+ try:
|
|
|
+ mktime = calendar.timegm if is_utc else time.mktime
|
|
|
+ return mktime(time.struct_time(args)) * 1000 + ms
|
|
|
+ except (OverflowError, ValueError):
|
|
|
+ return None
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def UTC(cls, *args):
|
|
|
+ t = cls.__ymd_etc(*args, is_utc=True)
|
|
|
+ return _NaN if t is None else t
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def parse(date_str, **kw_is_raw):
|
|
|
+ is_raw = kw_is_raw.get('is_raw', False)
|
|
|
+
|
|
|
+ t = unified_timestamp(str_or_none(date_str), False)
|
|
|
+ return int(t * 1000) if t is not None else t if is_raw else _NaN
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def now(**kw_is_raw):
|
|
|
+ is_raw = kw_is_raw.get('is_raw', False)
|
|
|
+
|
|
|
+ t = time.time()
|
|
|
+ return int(t * 1000) if t is not None else t if is_raw else _NaN
|
|
|
+
|
|
|
+ def __init__(self, *args):
|
|
|
+ if not args:
|
|
|
+ args = [self.now(is_raw=True)]
|
|
|
+ if len(args) == 1:
|
|
|
+ if isinstance(args[0], JSInterpreter.JS_Date):
|
|
|
+ self._t = int_or_none(args[0].valueOf(), default=None)
|
|
|
+ else:
|
|
|
+ arg_type = _js_typeof(args[0])
|
|
|
+ if arg_type == 'string':
|
|
|
+ self._t = self.parse(args[0], is_raw=True)
|
|
|
+ elif arg_type == 'number':
|
|
|
+ self._t = int(args[0])
|
|
|
+ else:
|
|
|
+ self._t = self.__ymd_etc(*args)
|
|
|
+
|
|
|
+ def toString(self):
|
|
|
+ try:
|
|
|
+ return time.strftime('%a %b %0d %Y %H:%M:%S %Z%z', self._t).rstrip()
|
|
|
+ except TypeError:
|
|
|
+ return "Invalid Date"
|
|
|
+
|
|
|
+ def valueOf(self):
|
|
|
+ return _NaN if self._t is None else self._t
|
|
|
+
|
|
|
@classmethod
|
|
|
def __op_chars(cls):
|
|
|
op_chars = set(';,[')
|
|
@@ -715,7 +786,7 @@ class JSInterpreter(object):
|
|
|
|
|
|
new_kw, _, obj = expr.partition('new ')
|
|
|
if not new_kw:
|
|
|
- for klass, konstr in (('Date', lambda x: int(unified_timestamp(x, False) * 1000)),
|
|
|
+ for klass, konstr in (('Date', lambda *x: self.JS_Date(*x).valueOf()),
|
|
|
('RegExp', self.JS_RegExp),
|
|
|
('Error', self.Exception)):
|
|
|
if not obj.startswith(klass + '('):
|
|
@@ -1034,6 +1105,7 @@ class JSInterpreter(object):
|
|
|
'String': compat_str,
|
|
|
'Math': float,
|
|
|
'Array': list,
|
|
|
+ 'Date': self.JS_Date,
|
|
|
}
|
|
|
obj = local_vars.get(variable)
|
|
|
if obj in (JS_Undefined, None):
|
|
@@ -1086,6 +1158,8 @@ class JSInterpreter(object):
|
|
|
assertion(len(argvals) == 2, 'takes two arguments')
|
|
|
return argvals[0] ** argvals[1]
|
|
|
raise self.Exception('Unsupported Math method ' + member, expr=expr)
|
|
|
+ elif obj is self.JS_Date:
|
|
|
+ return getattr(obj, member)(*argvals)
|
|
|
|
|
|
if member == 'split':
|
|
|
assertion(len(argvals) <= 2, 'takes at most two arguments')
|