【问题标题】:How to compare GMT and EST time from xml using xquery如何使用 xquery 从 xml 比较 GMT 和 EST 时间
【发布时间】:2013-01-10 16:13:33
【问题描述】:

我遇到了这个问题:我从各种网页获取 rss 提要,然后我需要按日期对它们进行排序。我使用 xquery 进行 xml 操作,使用 eXist 数据库存储 rss/xml。 这是我的代码:

for $item in subsequence(collection('/db/bla/user/feed')//item[contains(title, $search)], $start, $num)
                 let $title:= $item/title/text()
                 let $desc:= $item/description/text()
                 let $link := $item/link/text()
                 let $date:= $item/pubDate/text()
                 order by $date

这不起作用。而且我认为问题出在格林威治标准时间和美国东部标准时间的不同时间,因此按 $date 的顺序无法正常工作。 请帮忙,我被困在这里了。

提前致谢!

编辑:

这是我从 pubDate 标签获取的日期和时间格式:

Mon, 10 Dec 2012 13:32:24 EST

Mon, 10 Dec 2012 13:32:24 GMT

【问题讨论】:

  • 看来您需要解析日期时间。一些 API 内置了这些类型的函数:MarkLogic、Java、.NET 等,因此您必须决定是在 XQuery 之外解析 dateTime 还是在 XQuery 中从头开始编写一个,在这种情况下应该可能是一个不同的问题。
  • 您使用的是什么版本的存在?
  • @Ranon 我可以使用 3.0 版和 1.0 版,因为我认为 eXist db 2.0 版都支持这两个版本......现在我使用 1.0 来满足我的需要
  • 我关心的不是你的 XQuery 版本,而是你的 eXist dv 版本;但你也回答了。请看下面我的回答。 :)

标签: xml xpath xml-parsing xquery exist-db


【解决方案1】:

尝试将$date 转换为实际日期/时间:

let $date:= xs:dateTime($item/pubDate/text())

如果日期/时间的格式一致,您可以实现自己的转换器。在您评论的示例中,会给您带来最多问题的两个部分是月份和时区。月份必须是数字,您需要将时区转换为等效的 UTC 偏移量。

这是一个例子:

declare variable $months := ('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec');
declare variable $timezoneMap := 
    <map>
        <tz>
            <numeric>-05:00</numeric>
            <alpha>est</alpha>
        </tz>
    </map>;

declare function local:formatDate($origdate as xs:string) as xs:dateTime {
    let $dateTokens := tokenize($origdate,' ')  
    let $timezone := $timezoneMap/tz[lower-case($dateTokens[6])=alpha]/numeric/text()
    let $month := string(index-of($months,lower-case($dateTokens[3])))
    let $newDate := concat($dateTokens[4],'-',if (string-length($month)=1) then concat('0',$month) else $month,'-',$dateTokens[2],
                           'T',$dateTokens[5],$timezone)
    return
        xs:dateTime($newDate)
};

然后你可以像这样使用这个函数:

let $date:= local:formatDate($item/pubDate)

另外,如果您使用的是 XQuery 3.0,您可以使用 maps 作为时区和月份:

declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare variable $months := map{"jan":="01","feb":="02","mar":="03","apr":="04","may":="05","jun":="06",
                                "jul":="07","aug":="08","sep":="09","oct":="10","nov":="11","dec":="12"};
declare variable $timezoneMap := map{"est":="-05:00"};

declare function local:formatDate($origdate as xs:string) as xs:dateTime {
    let $dateTokens := tokenize($origdate,' ')
    let $newDate := concat($dateTokens[4],'-',$months(lower-case($dateTokens[3])),'-',$dateTokens[2],
                           'T',$dateTokens[5],$timezoneMap(lower-case($dateTokens[6])))
    return
        xs:dateTime($newDate)
};

【讨论】:

  • 感谢您的帮助。但它不起作用。我得到了这个异常:.... err:FORG0001: 类似日期时间值的非法词法形式'Mon,2012 年 12 月 10 日 13:32:24 EST' 2012 年 12 月 10 日星期一 13:32:24 EST
  • 啊,这不是 xs:dateTime。
  • 感谢您的努力,您的解决方案看起来可以工作,但我不知道如何在我的情况下使用它,请参阅我的编辑以了解我的时间日期格式的类型。我该如何修改您的解决方案?谢谢
  • @user1598696 - 我的解决方案适用于您编辑中的时间/日期示例。唯一的区别是 GMT 没有偏移量(它不应该)。两者都返回有效的xs:dateTime。如果您的任何提要中有其他时区或月份格式,则必须添加它们。 (如果有帮助,我可以添加一个示例。)
  • @user1598696 - 糟糕。那是因为index-of() 在 1 月份返回 1 而不是 01。一分钟后我会更新。
【解决方案2】:

在 XQuery 中构建一些日期格式解析器可能不是最好的主意。您将不得不自己处理所有时区转换问题(更糟糕的是:夏令时......)。

官方方式:DateTime-Module

eXist DB 有自己的基于java.text.SimpleDateFormat 的 DateTime-Module。该模块必须在使用前编译和激活,并且不包含在 eXists 当前的稳定版本中(但在 1.4.3 开发者版本和版本 2 预发布中)。

注意,它使用您系统的默认语言环境,在我的情况下不是英语(所以识别日期失败了)。

如果您设法启用它(如果您没有启用它,这可能是在 SO 姊妹网站 Database Administrator 上的另一个问题),您应该能够运行此代码来解析给定格式的日期:

import module namespace datetime = "http://exist-db.org/xquery/datetime";
datetime:parse-dateTime('Mon, 10 Dec 2012 13:32:24 GMT', 'EEE, d MMM yyyy HH:mm:ss Z')

肮脏的黑客:Java 接口

如果您正在运行一些旧版本的 eXist 或由于其他原因无法使用该模块,您可以在 conf.xml 中激活 Java 绑定并直接调用此函数:

declare namespace sdtf="java:java.text.SimpleDateFormat";
declare namespace date="java:java.util.Date";

(: Parse given date format to java date :)    
let $parse := sdtf:new('EEE, d MMM yyyy HH:mm:ss Z')
(: Output as close to XQuery's xs:dateTime as possible :)
let $format := sdtf:new("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
let $javadate := sdtf:parse($parse, 'Mon, 10 Dec 2012 13:32:24 GMT')
(: Use regex to insert missing ':' and finally cast to xs:dateTime :)
return xs:dateTime(fn:replace(sdtf:format($format, $javadate), '(.*)(\d{2})', '$1:$2'))

再次提醒,如果不是默认设置,请记住将 java 语言环境设置为英语。

我想这至少在 Saxon(定义了这个 java 绑定)和 BaseX(我实际上在这里测试过)中应该可以正常运行。

【讨论】:

    【解决方案3】:

    您可以使用 Ryan Grimm 的基于 XQuery 的日期解析器模块。它是为 MarkLogic 构建的,但稍作修改也可以在其他系统上运行。见https://github.com/marklogic/commons/blob/master/dates/date-parser.xqy

    作为模块状态的cmets,其支持的日期格式有:

    1. 2006 年 6 月 30 日 09:39:08 -0500
    2. 2003 年 4 月 16 日 13:49:06 +0200
    3. 2003 年 8 月 4 日 11:44:58 EDT
    4. 美国东部时间 98 年 1 月 4 日 0:41
    5. 2004 年 10 月 25 日 17:06:46 -0500
    6. 0102 年 9 月 23 日星期一 23:14:26 +0900

    【讨论】:

      猜你喜欢
      • 2014-06-25
      • 2012-06-15
      • 2012-01-14
      • 2021-07-10
      • 2017-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多