如何使用xquery从xml比较GMT和EST时间

时间:2013-01-10 16:13:34

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

我遇到了这个问题:我从各种网页上获取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

这不起作用。而且我认为问题出现在GMT和EST的不同时间,因此$ date的顺序不能正常工作。 请帮忙,我被困在这里。

提前致谢!

修改

以下是我从pubDate标记中获取的日期和时间格式:

Mon, 10 Dec 2012 13:32:24 EST

Mon, 10 Dec 2012 13:32:24 GMT

3 个答案:

答案 0 :(得分:2)

尝试将$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)
};

答案 1 :(得分:1)

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

官方方式:DateTime-Module

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

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

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

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')

Dirty Hack:连接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(我实际上在这里测试过)中运行良好。

答案 2 :(得分:1)

您可以使用Ryan Grimm的基于XQuery的Date Parser模块。它是为MarkLogic构建的,但是对其他系统也可以进行少量修改。请参阅https://github.com/marklogic/commons/blob/master/dates/date-parser.xqy

正如模块中的注释所述,其支持的日期格式为:

  1. 2006年6月30日09:39:08 -0500
  2. Apr 16 13:49:06 2003 +0200
  3. Aug 04 04:44:58 EDT 2003
  4. 4 Jan 98 0:41 EDT
  5. 25-Oct-2004 17:06:46 -0500
  6. 星期一,9月23日0102 23:14:26 +0900