0

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

3 回答 3

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,您可以使用地图作为时区和月份:

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)
};
于 2013-01-10T16:59:11.813 回答
1

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

官方方式:DateTime-Module

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

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

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

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 或由于其他原因无法使用该模块,您可以激活 Java 绑定conf.xml并直接调用此函数:

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

于 2013-01-11T00:17:26.913 回答
1

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

正如模块状态中的注释,它支持的日期格式是:

  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
于 2013-01-11T20:17:06.427 回答