Skip to main content

国会图书馆 EDTF(扩展日期时间格式)规范的 Python 实现

项目描述

python-edtf

Python 中 EDTF 格式的实现,以及用于解析自然语言日期文本并将 EDTF 日期转换为相关 Pythondate对象的实用程序函数。

有关当前的规范草案,请参见http://www.loc.gov/standards/datetime/

安装

pip install edtf

使用

>>> from edtf import parse_edtf
# Parse an EDTF string to an EDTFObject
>>> e = parse_edtf("1979-08~") # approx August 1979
>>> e
UncertainOrApproximate: '1979-08~'
# normalised string representation (some different EDTF strings have identical meanings)
>>> unicode(e)
u'1979-08~'

# Derive Python date objects
# lower and upper bounds that strictly adhere to the given range
>>> e.lower_strict()[:3], e.upper_strict()[:3]
((1979, 8, 1), (1979, 8, 31))
# lower and upper bounds that are padded if there's indicated uncertainty
>>> e.lower_fuzzy()[:3], e.upper_fuzzy()[:3]
((1979, 7, 1), (1979, 9, 30))

# Date intervals
>>> interval = parse_edtf("1979-08~/open")
>>> interval
Level1Interval: '1979-08~/open'
# Intervals have lower and upper EDTF objects.
>>> interval.lower, interval.upper
(UncertainOrApproximate: '1979-08~', UncertainOrApproximate: 'open')
>>> interval.lower.upper_strict()[:3]
(1979, 8, 31)
>>> interval.upper.lower_strict() # 'open' is interpreted to mean 'still happening'.
[Today's date]

# Date collections
>>> coll = parse_edtf('{1667,1668, 1670..1672}')
>>> coll
MultipleDates: '{1667, 1668, 1670..1672}'
>>> coll.objects
(Date: '1667', Date: '1668', Consecutives: '1670..1672')

返回的对象parse_edtf()edtf.parser.parser_classes.EDTFObject子类的实例,具体取决于解析的日期类型。这些类是:

# Level 0
Date
DateAndTime
Interval

# Level 1
UncertainOrApproximate
Unspecified
Level1Interval
LongYear
Season

# Level 2
PartialUncertainOrApproximate
PartialUnspecified
OneOfASet
MultipleDates
MaskedPrecision
Level2Interval
ExponentialYear

所有这些都实现upper/lower_strict/fuzzy()了派生 Pythondate对象的方法。

*Interval实例具有upper本身就是实例的lower属性EDTFObject

OneOfASet并且MultipleDates实例具有一个objects属性,该属性是在集合或列表中解析的所有 EDTF 日期的列表。

EDTF 规范包含

该库包括 EDTF 规范 0、1 和 2 级的实现。

测试覆盖率包括功能规格表中给出的每个示例。

0 级 ISO 8601 功能

  • 日期:

    >>> parse_edtf('1979-08') # August 1979
    Date: '1979-08'
    
  • 日期和时间:

    >>> parse_edtf('2004-01-01T10:10:10+05:00')
    DateAndTime: '2004-01-01T10:10:10+05:00'
    
  • 间隔(开始/结束):

    >>> parse_edtf('1979-08-28/1979-09-25') # From August 28 to September 25 1979
    Interval: '1979-08-28/1979-09-25'
    

1 级扩展

  • 不确定/大概日期:

    >>> parse_edtf('1979-08-28~') # Approximately August 28th 1979
    UncertainOrApproximate: '1979-08-28~'
    
  • 未指定日期:

    >>> parse_edtf('1979-08-uu') # An unknown day in August 1979
    Unspecified: '1979-08-uu'
    >>> parse_edtf('1979-uu') # Some month in 1979
    Unspecified: '1979-uu'
    
  • 延长间隔:

    >>> parse_edtf('1984-06-02?/2004-08-08~')
    Level1Interval: '1984-06-02?/2004-08-08~'
    
  • 超过四位数的年份:

    >>> parse_edtf('y-12000') # 12000 years BCE
    LongYear: 'y-12000'
    
  • 季节:

    >>> parse_edtf('1979-22') # Summer 1979
    Season: '1979-22'
    

2 级扩展

  • 部分不确定/近似:

    >>> parse_edtf('(2011)-06-04~') # year certain, month/day approximate.
    # Note that the result text is normalized
    PartialUncertainOrApproximate: '2011-(06-04)~'
    
  • 部分未指定:

    >>> parse_edtf('1979-uu-28') # The 28th day of an uncertain month in 1979
    PartialUnspecified: '1979-uu-28'
    
  • 一组之一:

    >>> parse_edtf("[..1760-12-03,1762]")
    OneOfASet: '[..1760-12-03, 1762]'
    
  • 多个日期:

    >>> parse_edtf('{1667,1668, 1670..1672}')
    MultipleDates: '{1667, 1668, 1670..1672}'
    
  • 掩蔽精度:

    >>> parse_edtf('197x') # A date in the 1970s.
    MaskedPrecision: '197x'
    
  • 2级延长间隔:

    >>> parse_edtf('2004-06-(01)~/2004-06-(20)~')
    Level2Interval: '2004-06-(01)~/2004-06-(20)~'
    
  • 需要超过 4 位数字的年份 - 指数形式:

    >>> parse_edtf('y-17e7')
    ExponentialYear: 'y-17e7'
    

自然语言表示

该库包括一个基本的英语自然语言解析器(它还不够聪明,无法处理“复活节”等场合或其他语言):

>>> from edtf import text_to_edtf
>>> text_to_edtf("circa August 1979")
'1979-08~'

请注意,结果是字符串,而不是ETDFObject.

解析器可以解析字符串,例如:

'January 12, 1940' => '1940-01-12'
'90' => '1990' #implied century
'January 2008' => '2008-01'
'the year 1800' => '1800'
'10/7/2008' => '2008-10-07' # in a full-specced date, assume US ordering

# uncertain/approximate
'1860?' => '1860?'
'1862 (uncertain)' => '1862?'
'circa Feb 1812' => '1812-02~'
'c.1860' => '1860~' #with or without .
'ca1860' => '1860~'
'approx 1860' => '1860~'

# masked precision
'1860s' => '186x' #186x has decade precision, 186u has year precision.
'1800s' => '18xx' # without uncertainty indicators, assume century

# masked precision + uncertainty
'ca. 1860s' => '186x~'
'circa 1840s' => '184x~'
'ca. 1860s?' => '186x?~'
'c1800s?' => '180x?~' # with uncertainty indicators, use the decade

# unspecified parts
'January 12' => 'uuuu-01-12'
'January' => 'uuuu-01'
'7/2008' => '2008-07'

#seasons
'Autumn 1872' => '1872-23'
'Fall 1872' => '1872-23'

# before/after
'earlier than 1928' => 'unknown/1928'
'later than 1928' => '1928/unknown'
'before January 1928' => 'unknown/1928-01'
'after about the 1920s' => '192x~/unknown'

# unspecified
'year in the 1860s' => '186u' #186x has decade precision, 186u has year precision.
('year in the 1800s', '18xu')
'month in 1872' => '1872-uu'
'day in January 1872' => '1872-01-uu'
'day in 1872' => '1872-uu-uu'

#centuries
'1st century' => '00xx'
'10c' => '09xx'
'19th century?' => '18xx?'

# just showing off now...
'a day in about Spring 1849?' => '1849-21-uu?~'

# simple ranges, which aren't as accurate as they could be. The parser is
limited to only picking the first year range it finds.
'1851-1852' => '1851/1852'
'1851-1852; printed 1853-1854' => '1851/1852'
'1851-52' => '1851/1852'
'1856-ca. 1865' => '1856/1865~'
'1860s-1870s' => '186x/187x'
'1920s -early 1930s' => '192x/193x'
'1938, printed 1940s-1950s' => '1938'

从 EDTF 表示生成自然文本是未来的目标。

自然文本解析器在解释不明确的日期时会做出哪些假设?

  • “1800年代”含糊不清是一个世纪或十年。如果给定日期不确定或近似,则使用十年解释。如果日期确定且准确,则使用世纪解释。

  • 如果未指定世纪 ( EDTF(natural_text="the '70s")),如果年份大于当前年份,我们暗示世纪为“19”,否则我们暗示世纪为当前世纪。

  • 美国订购的日期(mm/dd/yyyy)默认采用自然语言。要更改此设置DAY_FIRST,请在设置中设置为 True。

  • 如果自然语言使用“/”对日期进行分组,则将其解释为“或”而不是“和”。生成的 EDTF 文本是由[](“这些日期之一”)而不是{}(所有这些日期)括起来的列表。

在 Python 日期之间转换

由于 EDTF 日期通常是区域,而且通常不精确,因此我们需要根据具体情况使用几个不同的 Python 日期。通常,Python 日期用于排序和过滤,不会直接显示给用户。

struct_time日期表示

因为 Python 的datetime模块不支持 1 AD 到 9999 AD 范围之外的日期,所以我们time.struct_time默认将日期作为对象返回,而不是您可能期望的datetime.dateor对象。datetime.datetime

struct_time表示更难以使用,但可以按原样排序,这是主要用例,并且可以相对容易地转换为datedatetime对象(假设年份在公元 1 到 9999 年之间)或日期对象更多像astropy.time这样的灵活库已经超出了这些范围。

如果您确定您正在使用 Python 模块支持的范围内的日期,则可以使用和函数datetime获取这些更方便的对象。edtf.struct_time_to_dateedtf.struct_time_to_datetime

注意:在我们切换date到. 见票https://github.com/ixc/python-edtf/issues/26datetimestruct_time

lower_strictupper_strict

这些日期表示严格在日期范围内的最早和最晚日期,忽略不确定性或近似值。考虑这一点的一种方法是“如果您必须选择一个日期进行排序,那会是什么?”。

在升序排序(最近的最后一个)中,排序依据lower_strict以获得自然排序顺序。在降序排序(最近的第一个)中,排序方式upper_strict

>>> e = parse_edtf('1912-04~')

>>> e.lower_strict()  # Returns struct_time
>>> time.struct_time(tm_year=1912, tm_mon=4, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=0, tm_isdst=-1)

>>> e.lower_strict()[:3]  # Show only interesting parts of struct_time
(1912, 4, 01)

>>> from edtf import struct_time_to_date
>>> struct_time_to_date(e.lower_strict())  # Convert to date
datetime.date(1912, 4, 01)

>>> e.upper_strict()[:3]
(1912, 4, 30)

>>> struct_time_to_date(e.upper_strict())
datetime.date(1912, 4, 30)

lower_fuzzyupper_fuzzy


这些日期表示日期范围内可能的最早和最晚日期,对于“可能”的相当随意的定义。

这些值对于过滤结果很有用 - 即测试哪些 EDTF 日期可能会落入或重叠所需的日期范围。

模糊日期源自严格日期,加上或减去取决于日期规范的精确程度的填充级别。对于近似或不确定日期的情况,我们(任意)将表面范围填充不确定时间尺度的 100%,或者在季节的情况下填充 12 周。也就是说,如果一个日期在月尺度上是近似的,它会被填充一个月。如果它在年份尺度上是近似的,则用一年填充:

>>> e = parse_edtf('1912-04~')
>>> e.lower_fuzzy()[:3]  # padding is 100% of a month
(1912, 3, 1)
>>> e.upper_fuzzy()[:3]
(1912, 5, 30)

>>> e = parse_edtf('1912~')
>>> e.lower_fuzzy()[:3]  # padding is 100% of a year
(1911, 1, 1)
>>> e.upper_fuzzy()[:3]
(1913, 12, 31)

人们可以将不确定或近似的日期解释为“加或减 [精度水平]”。

如果日期既不确定近似,则应用两次填充,即获得 100% * 2 填充,或“正负两个 [精度级别]”。

四季

默认情况下,季节被解释为北半球。要更改这一点,请覆盖 中的月份映射appsettings.py

比较

如果两个 EDTF 日期的 unicode() 表示相同,则认为它们相等。如果 EDTF 日期较晚,则认为其日期大于另一个日期lower_strict

Django ORM 字段

实现了一个简单的 Django 字段,该edtf.fields.EDTFField字段将 EDTF 对象存储在数据库中。

要在模型上存储自然语言值,请定义另一个字段,并natural_text_field设置EDTFField.

保存模型时,natural_text_field将解析该值以设置该date_edtf值,并且基础 EDTF 对象会将模型上的_earliest_latest字段设置为表示儒略日期的浮点值。

警告:与儒略日期数值之间的转换可能不准确,尤其是对于可追溯到公元前数千年的古代日期。理想情况下,儒略日期值应仅在不需要完全准确的情况下用于范围和排序操作。它们应用于最终存储或往返转换后的显示。

示例用法:

from django.db import models
from edtf.fields import EDTFField

class MyModel(models.Model):
     date_display = models.CharField(
        "Date of creation (display)",
        blank=True,
        max_length=255,
     )
     date_edtf = EDTFField(
         "Date of creation (EDTF)",
         natural_text_field='date_display',
         lower_fuzzy_field='date_earliest',
         upper_fuzzy_field='date_latest',
         lower_strict_field='date_sort_ascending',
         upper_strict_field='date_sort_descending',
         blank=True,
         null=True,
     )
     # use for filtering
     date_earliest = models.FloatField(blank=True, null=True)
     date_latest = models.FloatField(blank=True, null=True)
     # use for sorting
     date_sort_ascending = models.FloatField(blank=True, null=True)
     date_sort_descending = models.FloatField(blank=True, null=True)

由于EDTFField_earliest_latest字段值是自动设置的,您可能希望将它们设置为只读,或者在模型管理员中不可见。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

edtf-4.0.1.tar.gz (37.3 kB 查看哈希)

已上传 source

内置分布

edtf-4.0.1-py2.py3-none-any.whl (33.0 kB 查看哈希

已上传 py2 py3