日期转换:将.NET JSON转换为ISO格式

9
如何将JSON.Net中的日期时间格式转换为ISO-??格式,例如:

/Date(1154970000000+0700)/

最好使用Python或Javascript。 我选择后者,因为在JS世界中,它似乎是最广泛使用、易于阅读和自然排序的。我将按用户存储偏移量。

如果要求实现有些过多,请告诉我两种格式的正确名称,我可能更容易理解如何进行转换。

4个回答

3

以下是 Python 2.x 版本。只需更改测试部分的打印语句即可适用于 Python 3.x。

据我通过谷歌搜索得知:主要组成部分是自1970年1月1日以来的毫秒数。它可以是负数。正数不应该有“+”号。这之后可以跟一个可选的UTC偏移量,由5个字符组成:一个强制性符号(“+”或“-”),2位数字表示小时,2位数字表示分钟。所有这些都在“/Date(”之前,并在“)/”之后。

此答案提供了一个将 JSON.NET 字符串转换为 Python datetime.datetime(时间戳)对象的函数,以及两个返回截断到秒和毫秒的 ISO 格式的函数。

脚本:

# /Date(1154970000000+0700)/
# 0123456............7654321
# without timezone:
# /Date(1154970000000)/
# 0123456............21
# dodgy case
# /Date(-1234)/
# 3210987654321
import datetime

def json_date_as_datetime(jd):
    sign = jd[-7]
    if sign not in '-+' or len(jd) == 13:
        millisecs = int(jd[6:-2])
    else:
        millisecs = int(jd[6:-7])
        hh = int(jd[-7:-4])
        mm = int(jd[-4:-2])
        if sign == '-': mm = -mm
        millisecs += (hh * 60 + mm) * 60000
    return datetime.datetime(1970, 1, 1) \
        + datetime.timedelta(microseconds=millisecs * 1000)

def datetime_as_iso(dt):
    return dt.strftime("%Y-%m-%dT%H:%M:%SZ") # truncates

def datetime_as_iso_ms(dt): # with millisecs as fraction
    return dt.strftime("%Y-%m-%dT%H:%M:%S.%%03dZ") \
        % (dt.microsecond // 1000) # truncate    

if __name__ == "__main__":

    tests = """\
    /Date(1154970000000+0700)/
    /Date(-1234)/
    /Date(1000+0200)/
    /Date(0+0000)/
    /Date(0)/
    /Date(0-0700)/
    /Date(0-0730)/
    /Date(0-0030)/
    /Date(-1577923200000+0000)/
    /Date(1)/
    /Date(499)/
    /Date(500)/
    /Date(501)/
    /Date(999)/
    /Date(1000)/
    /Date(-1)/
    """.splitlines()

    for test in tests:
        test = test.strip()
        if not test: continue
        d = json_date_as_datetime(test)
        print datetime_as_iso_ms(d), test

输出:

2006-08-08T00:00:00.000Z /Date(1154970000000+0700)/
1969-12-31T23:59:58.766Z /Date(-1234)/
1970-01-01T02:00:01.000Z /Date(1000+0200)/
1970-01-01T00:00:00.000Z /Date(0+0000)/
1970-01-01T00:00:00.000Z /Date(0)/
1969-12-31T17:00:00.000Z /Date(0-0700)/
1969-12-31T16:30:00.000Z /Date(0-0730)/
1969-12-31T23:30:00.000Z /Date(0-0030)/
1920-01-01T00:00:00.000Z /Date(-1577923200000+0000)/
1970-01-01T00:00:00.001Z /Date(1)/
1970-01-01T00:00:00.499Z /Date(499)/
1970-01-01T00:00:00.500Z /Date(500)/
1970-01-01T00:00:00.501Z /Date(501)/
1970-01-01T00:00:00.999Z /Date(999)/
1970-01-01T00:00:01.000Z /Date(1000)/
1969-12-31T23:59:59.999Z /Date(-1)/

@mattcodes:更新了更好的风格,更少的错误和更多的测试 :-) - John Machin
json_date_as_datetime("/Date(1428145200000+1200)/") → datetime.date(2015, 4, 4, 23, 0)。这不是上面JS版本的作用... - Matthew Schinckel
因为一些测试用例是错误的(1、3、6、7和8)。除非我误解了这个(不好的)日期格式,毫秒部分是距离纪元的UTC偏移量,并且可选偏移将其转换为所表示的任何时区。所有带有偏移量的测试都错误地将其包含在UTC时间戳中。 - Electric Head

1
jsonDate = "/Date(1154970000000+0700)/";

var strDate = parseInt(jsonDate.replace(/\/Date\(([-\d]+).*$/, "$1"));
var strHour = parseInt(jsonDate.replace(/.*\d([+-]\d\d).*$/, "$1"), 10);
var strMin = parseInt(jsonDate.replace(/.*\d([+-])\d\d(\d\d).*$/, "$1$2"), 10);

var date = new Date(strDate);
if (!isNaN(strHour)) date.setHours(date.getHours() + strHour);
if (!isNaN(strMin)) date.setMinutes(date.getMinutes() + strMin);

var out = date.toISOString();

转换为ISO的函数:

var toISOString = Date.prototype.toISOString ?
    function(d){return d}:
    (function(){
        function t(i){return i<10?"0"+i:i};
        function h(i){return i.length<2?"00"+i:i.length<3?"0"+i:3<i.length?Math.round(i/Math.pow(10,i.length-3)):i};
        function toISOString(){
            return "".concat(
                this.getUTCFullYear(), "-",
                t(this.getUTCMonth() + 1), "-",
                t(this.getUTCDate()), "T",
                t(this.getUTCHours()), ":",
                t(this.getUTCMinutes()), ":",
                t(this.getUTCSeconds()), ".",
                h("" + this.getUTCMilliseconds()), "Z"
            );
        };
        return function(d){
            d.toISOString = toISOString;
            return d;
        }
    })();

类型错误:对象Tue Aug 08 2006 00:00:00 GMT+0700 (ICT)没有'format'方法。 - user53791
我的代码忽略了“+0700”,我添加了代码将其添加到转换日期的小时/分钟中。请检查这是否是正确的行为,如果不是,请忽略时间。 - ariel
@ariel,@mattcodes:那些硬编码的子字符串数字看起来有点可疑...1970年的日期是否带有前导零?那么在1970年之前的日期呢(比如记录像我这样皱巴巴的人的出生日期)? - John Machin
@ariel:嗯……它将负时区(-0700)视为正时区(+0700)。 - John Machin
谢谢John,已更新以允许负数和负时区。 - ariel
@ariel:为了让那些极难猜测代码含义和不知道如何运行JavaScript的人放心,我们考虑发布一些测试输出怎么样? - John Machin

1
返回时区感知日期时间并为John Machin的测试用例"/Date(1428145200000+1200)/"提供正确的输出。
Python> 3.3兼容。对于2.7,请使用pytz.utc而不是datetime.timezone.utc
from datetime import datetime, timedelta, timezone
import re


def jsondate(jsondate, tzinfo=timezone.utc):
    """Converts an ASP.NET json date: "/DATE(x)/" to tz-aware datetime object."""

    regex = (
        r"/Date\("
        r"(?P<milleseconds>[\-]?\d+)"
        r"(?P<offset>"
        r"(?P<offset_sign>[\+\-])"
        r"(?P<offset_hours>[01][0-9]|2[0-3])"
        r"(?P<offset_mins>[0-5][0-9])"
        r")?\)/"
    )
    try:
        parts = re.match(regex, jsondate).groupdict()
    except (AttributeError, TypeError):
        raise ValueError("Unsupported ASP.NET JSON Date Format: %s" % jsondate)

    since_epoch = timedelta(microseconds=1000 * int(parts['milleseconds']))
    if parts.get('offset'):
        since_epoch += timedelta(
            hours=int("%s%s" % (parts['offset_sign'], parts['offset_hours'])),
            minutes=int("%s%s" % (parts['offset_sign'], parts['offset_mins']))
        )
    return datetime(year=1970, month=1, day=1, tzinfo=tzinfo) + since_epoch

0

这是我多年前写的一个小类,用于清理某些.NET库生成的无效JSON:

class DotNETDecoder(simplejson.JSONDecoder):
    '''
    This is a decoder to convert .NET encoded JSON into python objects
    The motivation for this is the way .NET encodes dates.
    See:
    https://msdn.microsoft.com/en-us/library/bb299886.aspx#intro_to_json_topic2
    .NET encodes datetimes like this: "\/Date(628318530718)\/"
    '''

    def __init__(self, timezone, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.parse_string = self._date_parse_string(timezone)
        self.scan_once = py_make_scanner(self)

    @staticmethod
    def _date_parse_string(timezone):
        def _parse_string(string, idx, encoding, strict):
            obj = scanstring(string, idx, encoding, strict)
            if isinstance(obj[0], str):
                match = date_match.search(obj[0])
                if match:
                    return [dt.datetime.fromtimestamp(
                                int(match.group(1)) / 1000, timezone),
                            obj[1]]
            return obj
        return _parse_string

还有一个测试用例/示例:

def test_can_decode_dotnet_json_dates():                                                                                                                                                                     
    jsonstr = '{"date": "Date(1330848000000)", "f": "b", "l": [], "i": 5}'                                                                                                                                   
    timezone = pytz.timezone('America/New_York')                                                                                                                                                             
    obj = json.loads(jsonstr, cls=DotNETDecoder, timezone=timezone)                                                                                                                                          
    assert obj['date'] == timezone.localize(dt.datetime(2012, 3, 4, 3, 0))                                                                                                                                   
    assert obj['f'] == "b"                                                                                                                                                                                   
    assert obj['i'] == 5                                                                                                                                                                                     
    assert obj['l'] == []

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接