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( ...@@ -20,7 +20,7 @@ setup(
keywords = 'zodb utility tool', keywords = 'zodb utility tool',
packages = find_packages(), 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 = { extras_require = {
'test': ['pytest'], 'test': ['pytest'],
......
...@@ -67,18 +67,24 @@ inclusive. Both tidmin and tidmax are optional and default to ...@@ -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: 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 - 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): Dates are parsed using https://dateparser.readthedocs.io/en/latest/ please refer
to this documentation for the more details on supported syntax.
- absolute timestamp,
- relative timestamp, e.g. yesterday, 1.week.ago
Example tid ranges: Example tid ranges:
.. whole database history .. whole database history
000000000000aaaa.. transactions starting from 000000000000aaaa till latest 000000000000aaaa.. transactions starting from 000000000000aaaa
..000000000000bbbb transactions starting from database beginning till 000000000000bbbb till latest
000000000000aaaa..000000000000bbbb transactions starting from 000000000000aaaa till 000000000000bbbb ..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 In commands <tidrange> is optional - if it is not given at all, it defaults to
0..+∞, i.e. to whole database history. 0..+∞, i.e. to whole database history.
......
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Nexedi SA and Contributors. # Copyright (C) 2019 Nexedi SA and Contributors.
# #
# This program is free software: you can Use, Study, Modify and Redistribute # This program is free software: you can Use, Study, Modify and Redistribute
...@@ -16,9 +17,25 @@ ...@@ -16,9 +17,25 @@
# See COPYING file for full licensing terms. # See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options. # 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(): def test_tidrange_tid():
...@@ -40,3 +57,31 @@ def test_tidrange_tid(): ...@@ -40,3 +57,31 @@ def test_tidrange_tid():
with raises(TidRangeInvalid) as exc: with raises(TidRangeInvalid) as exc:
parse_tidrange("invalid") parse_tidrange("invalid")
assert exc.value.args == ("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 ...@@ -22,6 +22,8 @@ import hashlib, struct, codecs
import zodburi import zodburi
from six.moves.urllib_parse import urlsplit, urlunsplit from six.moves.urllib_parse import urlsplit, urlunsplit
from zlib import crc32, adler32 from zlib import crc32, adler32
from ZODB.TimeStamp import TimeStamp
import dateparser
def ashex(s): def ashex(s):
return s.encode('hex') return s.encode('hex')
...@@ -64,6 +66,31 @@ def txnobjv(txn): ...@@ -64,6 +66,31 @@ def txnobjv(txn):
class TidRangeInvalid(Exception): class TidRangeInvalid(Exception):
pass 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). # parse_tidrange parses a string into (tidmin, tidmax).
# #
# see `zodb help tidrange` for accepted tidrange syntax. # see `zodb help tidrange` for accepted tidrange syntax.
...@@ -73,6 +100,9 @@ def parse_tidrange(tidrange): ...@@ -73,6 +100,9 @@ def parse_tidrange(tidrange):
except ValueError: # not exactly 2 parts in between ".." except ValueError: # not exactly 2 parts in between ".."
raise TidRangeInvalid(tidrange) raise TidRangeInvalid(tidrange)
tidmin = tid_from_date(tidmin)
tidmax = tid_from_date(tidmax)
try: try:
tidmin = tidmin.decode("hex") tidmin = tidmin.decode("hex")
tidmax = tidmax.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