Commit 78455126 authored by Jérome Perrin's avatar Jérome Perrin

tidrange: supports giving range as dates

parent fcaf4e23
......@@ -20,7 +20,7 @@ setup(
keywords = 'zodb utility tool',
packages = find_packages(),
install_requires = ['ZODB', 'zodburi', 'zope.interface', 'pygolang >= 0.0.0.dev6', 'six'],
install_requires = ['ZODB', 'zodburi', 'zope.interface', 'pygolang >= 0.0.0.dev6', 'six', 'dateparser'],
extras_require = {
'test': ['pytest'],
......
......@@ -67,18 +67,24 @@ inclusive. Both tidmin and tidmax are optional and default to
If a tid (tidmin or tidmax) is given, it has to be specified as follows:
- a 16-digit hex number specifying transaction ID, e.g. 0285cbac258bf266
- absolute date,
- relative date, e.g. yesterday, 1 week ago
TODO (recheck what git does and use dateparser):
- absolute timestamp,
- relative timestamp, e.g. yesterday, 1.week.ago
Dates are parsed using https://dateparser.readthedocs.io/en/latest/ please refer
to this documentation for the more details on supported syntax.
Example tid ranges:
.. whole database history
000000000000aaaa.. transactions starting from 000000000000aaaa till latest
..000000000000bbbb transactions starting from database beginning till 000000000000bbbb
000000000000aaaa..000000000000bbbb transactions starting from 000000000000aaaa till 000000000000bbbb
.. whole database history
000000000000aaaa.. transactions starting from 000000000000aaaa
till latest
..000000000000bbbb transactions starting from database beginning
till 000000000000bbbb
000000000000aaaa..000000000000bbbb transactions starting from 000000000000aaaa
till 000000000000bbbb
2018-01-01 01:23:45.6789..2018-01-02 UTC transactions between 2018-01-01 at 1:23:45.6789
in machine timezone and 2018-01-02 at 00:00:00.0000 UTC
1 week ago..yesterday transactions from one week ago until yesterday.
In commands <tidrange> is optional - if it is not given at all, it defaults to
0..+∞, i.e. to whole database history.
......
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Nexedi SA and Contributors.
#
# This program is free software: you can Use, Study, Modify and Redistribute
......@@ -16,9 +17,25 @@
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
from pytest import raises
import os
import time
from pytest import raises, fixture
from zodbtools.util import parse_tidrange, TidRangeInvalid, ashex
from ZODB.TimeStamp import TimeStamp
from zodbtools.util import parse_tidrange, TidRangeInvalid
@fixture
def europe_paris_timezone():
"""Pytest's fixture to run this test with Europe/Paris as default timezone.
"""
initial_tz = os.environ.get("TZ")
os.environ["TZ"] = "Europe/Paris"
time.tzset()
yield
del os.environ["TZ"]
if initial_tz:
os.environ["TZ"] = initial_tz
time.tzset()
def test_tidrange_tid():
......@@ -40,3 +57,31 @@ def test_tidrange_tid():
with raises(TidRangeInvalid) as exc:
parse_tidrange("invalid")
assert exc.value.args == ("invalid",)
def test_tidrange_date(europe_paris_timezone):
# dates in UTC
assert (
b"\x03\xc4\x85v\x00\x00\x00\x00",
b"\x03\xc4\x88\xa0\x00\x00\x00\x00",
) == parse_tidrange("2018-01-01 10:30:00 UTC..2018-01-02 UTC")
# these TIDs are ZODB.TimeStamp.TimeStamp
assert (TimeStamp(2018, 1, 1, 10, 30, 0).raw(), None) == parse_tidrange(
"2018-01-01 10:30:00 UTC.."
)
# dates in local timezone
assert (
b"\x03\xc4\x85:\x00\x00\x00\x00",
b"\x03\xc4\x88d\x00\x00\x00\x00",
) == parse_tidrange("2018-01-01 10:30:00..2018-01-02")
# dates in natural language (also in local timezone)
assert (
b"\x03\xc4\x85:\x00\x00\x00\x00",
b"\x03\xc4\x88d\x00\x00\x00\x00",
) == parse_tidrange("le 1er janvier 2018 à 10h30..2018年1月2日")
# or relative dates
assert (None, None) != parse_tidrange("1 month ago..yesterday")
......@@ -22,6 +22,8 @@ import hashlib, struct, codecs
import zodburi
from six.moves.urllib_parse import urlsplit, urlunsplit
from zlib import crc32, adler32
from ZODB.TimeStamp import TimeStamp
import dateparser
def ashex(s):
return s.encode('hex')
......@@ -64,6 +66,31 @@ def txnobjv(txn):
class TidRangeInvalid(Exception):
pass
def tid_from_date(date_string):
"""Try to parse `date_string` as a date and returns the
corresponding TID.
If `date_string` cannot be parsed as a date, assume it was
already a TID.
"""
if not date_string:
return date_string
date = dateparser.parse(
date_string,
settings={
'TO_TIMEZONE': 'UTC'})
if not date:
# parsing failed
return date_string
# build a ZODB.TimeStamp to convert as a TID
return ashex(
TimeStamp(
date.year,
date.month,
date.day,
date.hour,
date.minute,
date.second + date.microsecond / 1000000.).raw())
# parse_tidrange parses a string into (tidmin, tidmax).
#
# see `zodb help tidrange` for accepted tidrange syntax.
......@@ -73,6 +100,9 @@ def parse_tidrange(tidrange):
except ValueError: # not exactly 2 parts in between ".."
raise TidRangeInvalid(tidrange)
tidmin = tid_from_date(tidmin)
tidmax = tid_from_date(tidmax)
try:
tidmin = tidmin.decode("hex")
tidmax = tidmax.decode("hex")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment