"""Test datetime parser. Part of the tagit test suite. A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ # standard imports import unittest from datetime import date as ddate from datetime import time as dtime from datetime import datetime # external imports from pyparsing import ParseException # tagit imports from tagit.utils import errors, Struct # objects to test #from tagit.parsing.datefmt import DatefmtError, DateParserError, TimeParserError, DateFormatError, guess_date, guess_time, guess_datetime, increment, PRIORITIES_US, DF from tagit.parsing.datefmt import guess_date, guess_time, PRIORITIES_US, DateParserError, DateFormatError, TimeParserError, guess_datetime, DF, parse_datetime, increment, DateTimeParser ## code ## class TestGuessDatetime(unittest.TestCase): def test_parse_datetime(self): parse_datetime = DateTimeParser() cyear = ddate.today().year cmon = ddate.today().month cday = ddate.today().day # date only: vary number formats self.assertEqual(parse_datetime('3.4.12'), datetime(2012, 4, 3)) self.assertEqual(parse_datetime('15.8.19'), datetime(2019, 8, 15)) self.assertEqual(parse_datetime('8.11.98'), datetime(1998, 11, 8)) self.assertEqual(parse_datetime('10.11.12'), datetime(2012, 11, 10)) self.assertEqual(parse_datetime('3.4.1912'), datetime(1912, 4, 3)) self.assertEqual(parse_datetime('15.8.1919'), datetime(1919, 8, 15)) self.assertEqual(parse_datetime('8.11.1998'), datetime(1998, 11, 8)) self.assertEqual(parse_datetime('10.11.1912'), datetime(1912, 11, 10)) self.assertEqual(parse_datetime('8.1998'), datetime(1998, 8, 1)) self.assertEqual(parse_datetime('10.1912'), datetime(1912, 10, 1)) self.assertRaises(errors.ParserError, parse_datetime, 'ab.cd.ef') self.assertRaises(errors.ParserError, parse_datetime, '123.123.2000') self.assertRaises(errors.ParserError, parse_datetime, '.123.2000') self.assertRaises(errors.ParserError, parse_datetime, '123..2000') self.assertRaises(errors.ParserError, parse_datetime, '12.12.20001') # date only: vary order (three-part) self.assertEqual(parse_datetime('15.98.8'), datetime(1998, 8, 15)) self.assertEqual(parse_datetime('8.98.15'), datetime(1998, 8, 15)) self.assertEqual(parse_datetime('8.15.98'), datetime(1998, 8, 15)) self.assertEqual(parse_datetime('15.8.98'), datetime(1998, 8, 15)) self.assertEqual(parse_datetime('98.8.15'), datetime(1998, 8, 15)) self.assertEqual(parse_datetime('98.15.8'), datetime(1998, 8, 15)) # date only: vary order (two-part) self.assertEqual(parse_datetime('15.98'), datetime(1998, 1, 15)) self.assertEqual(parse_datetime('08.98'), datetime(1998, 8, 1)) self.assertEqual(parse_datetime('08.15'), datetime(2015, 8, 1)) self.assertEqual(parse_datetime('15.08'), datetime(cyear, 8, 15)) self.assertEqual(parse_datetime('98.08'), datetime(1998, 8, 1)) self.assertEqual(parse_datetime('98.15'), datetime(1998, 1, 15)) # date only: one part self.assertEqual(parse_datetime('1998'), datetime(1998, 1, 1)) # date only: literal month self.assertEqual(parse_datetime('98.April'), datetime(1998, 4, 1)) # FIXME: Allow more patterns #self.assertEqual(parse_datetime('98, April'), datetime(1998, 4, 1)) #self.assertEqual(parse_datetime('April, 15'), datetime(2015, 4, 1)) # date only: day with suffix #self.assertEqual(parse_datetime('10th 2000'), datetime(2000, 1, 10)) #self.assertEqual(parse_datetime('2000, 10th'), datetime(2000, 1, 10)) #self.assertEqual(parse_datetime('April, 10th'), datetime(cyear, 4, 10)) #self.assertEqual(parse_datetime('April 10th'), datetime(cyear, 4, 10)) #self.assertEqual(parse_datetime('10th April'), datetime(cyear, 4, 10)) # date only: of notation #self.assertEqual(parse_datetime('10th of April, 2000'), datetime(2000, 4, 10)) #self.assertEqual(parse_datetime('10th of April'), datetime(cyear, 4, 10)) #self.assertEqual(parse_datetime('10 of 04'), datetime(cyear, 4, 10)) # invalid ranges self.assertRaises(DateParserError, parse_datetime, '10.93.2013') self.assertRaises(DateParserError, parse_datetime, '48.10.2013') self.assertRaises(DateParserError, parse_datetime, '48.93.2013') self.assertRaises(DateParserError, parse_datetime, "52.74") # time only: am/pm self.assertEqual(parse_datetime("10 am"), datetime(1970, 1, 1, 10)) self.assertEqual(parse_datetime("10 pm"), datetime(1970, 1, 1, 22)) self.assertEqual(parse_datetime("10:02 pm"), datetime(1970, 1, 1, 22, 2)) self.assertEqual(parse_datetime("14 am"), datetime(1970, 1, 1, 14)) self.assertRaises(TimeParserError, parse_datetime, "14 pm") # time only: 24hrs format self.assertEqual(parse_datetime("1:2"), datetime(1970, 1, 1, 1, 2)) self.assertEqual(parse_datetime("12:34"), datetime(1970, 1, 1, 12, 34)) self.assertEqual(parse_datetime("12:34:54"), datetime(1970, 1, 1, 12, 34, 54)) self.assertEqual(parse_datetime("12:34:54.123"), datetime(1970, 1, 1, 12, 34, 54, 123000)) self.assertEqual(parse_datetime("1:2:3.4"), datetime(1970, 1, 1, 1, 2, 3, 400000)) self.assertRaises(errors.ParserError, parse_datetime, '84:12') self.assertRaises(errors.ParserError, parse_datetime, '12:75') self.assertRaises(errors.ParserError, parse_datetime, '12:13:84') # time only: HH:MM self.assertEqual(parse_datetime("54:34"), datetime(1970, 1, 1, 0, 54, 34)) # time only: invalid format self.assertRaises(errors.ParserError, parse_datetime, '12:') # date and time self.assertEqual(parse_datetime("12:34 18.05.2012"), datetime(2012, 5, 18, 12, 34)) self.assertEqual(parse_datetime("12:34, 18.05.2012"), datetime(2012, 5, 18, 12, 34)) self.assertEqual(parse_datetime("18.05.2012 12:34"), datetime(2012, 5, 18, 12, 34)) self.assertEqual(parse_datetime("18.05.2012, 12:34"), datetime(2012, 5, 18, 12, 34)) self.assertEqual(parse_datetime("2012, 12:34"), datetime(2012, 1, 1, 12, 34)) self.assertEqual(parse_datetime("2012, 12am"), datetime(2012, 1, 1, 12)) self.assertRaises(errors.ParserError, parse_datetime, '12.34 18:05:2012') # invalid args self.assertRaises(errors.ParserError, parse_datetime, '') def test_guess_date(self): this_year = ddate.today().year # some unambiguous formats self.assertEqual(guess_date('18 . 05 . 2012'.split()), (ddate(2012, 5, 18), 'DMY')) self.assertEqual(guess_date('18 5 2012'.split()), (ddate(2012, 5, 18), 'DMY')) self.assertEqual(guess_date('2012 , 05 , 18'.split()), (ddate(2012, 5, 18), 'YMD')) self.assertEqual(guess_date('2012 5 18'.split()), (ddate(2012, 5, 18), 'YMD')) self.assertEqual(guess_date('18 5 2004'.split()), (ddate(2004, 5, 18), 'DMY')) self.assertEqual(guess_date('2004 5 18'.split()), (ddate(2004, 5, 18), 'YMD')) self.assertEqual(guess_date('10 11 12'.split()), (ddate(2012, 11, 10), 'DMY')) self.assertEqual(guess_date('10 11 12'.split(), priorities=PRIORITIES_US), (ddate(2012, 10, 11), 'MDY')) self.assertEqual(guess_date('2012 04 05'.split()), (ddate(2012, 4, 5), 'YMD')) self.assertEqual(guess_date('2012 4 5'.split()), (ddate(2012, 4, 5), 'YMD')) self.assertEqual(guess_date('2012 May , 4th'.split()), (ddate(2012, 5, 4), 'YMD')) self.assertEqual(guess_date('4 5 2012'.split()), (ddate(2012, 5, 4), 'DMY')) self.assertEqual(guess_date('4th of May 2012'.split()), (ddate(2012, 5, 4), 'DMY')) self.assertEqual(guess_date('2012 4th of May'.split()), (ddate(2012, 5, 4), 'YDM')) # three-part format # unambiguous MD ranges, full year self.assertEqual(guess_date('28 11 2018'.split()), (ddate(2018, 11, 28), 'DMY')) self.assertEqual(guess_date('28 2018 11'.split()), (ddate(2018, 11, 28), 'DYM')) self.assertEqual(guess_date('11 28 2018'.split()), (ddate(2018, 11, 28), 'MDY')) self.assertEqual(guess_date('11 2018 28'.split()), (ddate(2018, 11, 28), 'MYD')) self.assertEqual(guess_date('2018 11 28'.split()), (ddate(2018, 11, 28), 'YMD')) self.assertEqual(guess_date('2018 28 11'.split()), (ddate(2018, 11, 28), 'YDM')) # unambiguous MDY ranges self.assertEqual(guess_date('28 11 98'.split()), (ddate(1998, 11, 28), 'DMY')) self.assertEqual(guess_date('28 98 11'.split()), (ddate(1998, 11, 28), 'DYM')) self.assertEqual(guess_date('11 28 98'.split()), (ddate(1998, 11, 28), 'MDY')) self.assertEqual(guess_date('11 98 28'.split()), (ddate(1998, 11, 28), 'MYD')) self.assertEqual(guess_date('98 11 28'.split()), (ddate(1998, 11, 28), 'YMD')) self.assertEqual(guess_date('98 28 11'.split()), (ddate(1998, 11, 28), 'YDM')) # explicit YMD self.assertEqual(guess_date('10th of April 2018'.split()), (ddate(2018, 4, 10), 'DMY')) self.assertEqual(guess_date('April 10th 98'.split()), (ddate(1998, 4, 10), 'MDY')) self.assertEqual(guess_date('98 April 10th'.split()), (ddate(1998, 4, 10), 'YMD')) self.assertEqual(guess_date('2018 10th of April'.split()), (ddate(2018, 4, 10), 'YDM')) self.assertEqual(guess_date('2018 10 of 04'.split()), (ddate(2018, 4, 10), 'YDM')) # explicit MY self.assertEqual(guess_date('2018 10 April'.split()), (ddate(2018, 4, 10), 'YDM')) self.assertEqual(guess_date('2018 April 10'.split()), (ddate(2018, 4, 10), 'YMD')) self.assertEqual(guess_date('April 10 2018'.split()), (ddate(2018, 4, 10), 'MDY')) # explicit DY self.assertEqual(guess_date('10th 04 98'.split()), (ddate(1998, 4, 10), 'DMY')) self.assertEqual(guess_date('2018 10th 04'.split()), (ddate(2018, 4, 10), 'YDM')) self.assertEqual(guess_date('2018 04 10th'.split()), (ddate(2018, 4, 10), 'YMD')) # explicit DM self.assertEqual(guess_date('10th April 10'.split()), (ddate(2010, 4, 10), 'DMY')) self.assertEqual(guess_date('10 10th April'.split()), (ddate(2010, 4, 10), 'YDM')) self.assertEqual(guess_date('10 April 10th'.split()), (ddate(2010, 4, 10), 'YMD')) # ambiguous formats: explicit Y self.assertEqual(guess_date('2018 04 08'.split()), (ddate(2018, 4, 8), 'YMD')) self.assertEqual(guess_date('04 2018 08'.split()), (ddate(2018, 8, 4), 'DYM')) self.assertEqual(guess_date('08 04 2018'.split()), (ddate(2018, 4, 8), 'DMY')) self.assertEqual(guess_date('4 8 11'.split()), (ddate(2011, 8, 4), 'DMY')) # ambiguous formats: explicit D self.assertEqual(guess_date('10 4th 11'.split()), (ddate(2011, 10, 4), 'MDY')) self.assertEqual(guess_date('11 10 4th'.split()), (ddate(2011, 10, 4), 'YMD')) self.assertEqual(guess_date('4th 11 10'.split()), (ddate(2010, 11, 4), 'DMY')) self.assertEqual(guess_date('4th 10 11'.split()), (ddate(2011, 10, 4), 'DMY')) # ambiguous formats: explicit M self.assertEqual(guess_date('April 21 08'.split()), (ddate(2008, 4, 21), 'MDY')) self.assertEqual(guess_date('08 April 21'.split()), (ddate(2021, 4, 8), 'DMY')) self.assertEqual(guess_date('21 08 April'.split()), (ddate(2021, 4, 8), 'YDM')) # fully ambiguous self.assertEqual(guess_date('04 08 10'.split()), (ddate(2010, 8, 4), 'DMY')) # errors self.assertRaises(DateParserError, guess_date, '2012 98 10'.split()) self.assertRaises(DateParserError, guess_date, 'April 98 April'.split()) self.assertRaises(DateParserError, guess_date, '10th 98 29'.split()) # two-part format # unambiguous DY ranges self.assertEqual(guess_date('28 98'.split()), (ddate(1998, 1, 28), 'DY')) self.assertEqual(guess_date('98 28'.split()), (ddate(1998, 1, 28), 'YD')) self.assertEqual(guess_date('2010 28'.split()), (ddate(2010, 1, 28), 'YD')) self.assertEqual(guess_date('28 2010'.split()), (ddate(2010, 1, 28), 'DY')) # explicit DY self.assertEqual(guess_date('28th 2010'.split()), (ddate(2010, 1, 28), 'DY')) self.assertEqual(guess_date('2010 28th'.split()), (ddate(2010, 1, 28), 'YD')) # explicit MY self.assertEqual(guess_date('2010 April'.split()), (ddate(2010, 4, 1), 'YM')) self.assertEqual(guess_date('April 2010'.split()), (ddate(2010, 4, 1), 'MY')) # explicit DM self.assertEqual(guess_date('April 10th'.split()), (ddate(this_year, 4, 10), 'MD')) self.assertEqual(guess_date('10th April'.split()), (ddate(this_year, 4, 10), 'DM')) self.assertEqual(guess_date('10th of April'.split()), (ddate(this_year, 4, 10), 'DM')) self.assertEqual(guess_date('10 of 04'.split()), (ddate(this_year, 4, 10), 'DM')) self.assertEqual(guess_date('10th 4'.split()), (ddate(this_year, 4, 10), 'DM')) # explicit Y self.assertEqual(guess_date('2010 04'.split()), (ddate(2010, 4, 1), 'YM')) self.assertEqual(guess_date('04 2010'.split()), (ddate(2010, 4, 1), 'MY')) self.assertEqual(guess_date('04 98'.split()), (ddate(1998, 4, 1), 'MY')) self.assertEqual(guess_date('98 04'.split()), (ddate(1998, 4, 1), 'YM')) # explicit M self.assertEqual(guess_date('April 10'.split()), (ddate(2010, 4, 1), 'MY')) self.assertEqual(guess_date('10 April'.split()), (ddate(this_year, 4, 10), 'DM')) # explicit D self.assertEqual(guess_date('10th 08'.split()), (ddate(this_year, 8, 10), 'DM')) self.assertEqual(guess_date('08 10th'.split()), (ddate(this_year, 8, 10), 'MD')) # some hints self.assertEqual(guess_date('18 5'.split()), (ddate(this_year, 5, 18), 'DM')) self.assertEqual(guess_date('4 8'.split()), (ddate(this_year, 8, 4), 'DM')) # fully ambiguous self.assertEqual(guess_date('08 10'.split()), (ddate(this_year, 10, 8), 'DM')) # one-part format # full year self.assertEqual(guess_date('2018'.split()), (ddate(2018, 1, 1), 'Y')) # short year self.assertEqual(guess_date('18'.split()), (ddate(2018, 1, 1), 'Y')) self.assertEqual(guess_date('98'.split()), (ddate(1998, 1, 1), 'Y')) self.assertEqual(guess_date('08'.split()), (ddate(2008, 1, 1), 'Y')) # non-year token self.assertRaises(DateParserError, guess_date, ('1', )) self.assertRaises(DateParserError, guess_date, ('April', )) self.assertRaises(DateParserError, guess_date, ('10th', )) # other errors self.assertRaises(DateParserError, guess_date, '') self.assertRaises(DateParserError, guess_date, 'fuuu'.split()) self.assertRaises(DateParserError, guess_date, '1 fuuu'.split()) self.assertRaises(DateParserError, guess_date, '1 fuuu bar 2'.split()) self.assertRaises(DateParserError, guess_date, '1 2 3 4'.split()) def test_guess_time(self): # single token self.assertEqual(guess_time(['9']), (dtime(hour=9), 'h')) # am/pm notation self.assertEqual(guess_time("9 am".split()), (dtime(hour=9), 'h')) self.assertEqual(guess_time("10 am".split()), (dtime(hour=10), 'h')) self.assertEqual(guess_time("09 am".split()), (dtime(hour=9), 'h')) self.assertEqual(guess_time("9 pm".split()), (dtime(hour=21), 'h')) self.assertEqual(guess_time("10 pm".split()), (dtime(hour=22), 'h')) self.assertEqual(guess_time("09 pm".split()), (dtime(hour=21), 'h')) self.assertEqual(guess_time("10 02 am".split()), (dtime(hour=10, minute=2), 'hm')) self.assertEqual(guess_time("10 02 pm".split()), (dtime(hour=22, minute=2), 'hm')) self.assertEqual(guess_time("14 am".split()), (dtime(hour=14), 'h')) self.assertRaises(TimeParserError, guess_time, "14 pm".split()) # 24-hrs notation self.assertEqual(guess_time("12 34".split()), (dtime(hour=12, minute=34), 'hm')) self.assertEqual(guess_time("15 32".split()), (dtime(hour=15, minute=32), 'hm')) self.assertEqual(guess_time("12 04".split()), (dtime(hour=12, minute=4), 'hm')) self.assertEqual(guess_time("12 4".split()), (dtime(hour=12, minute=4), 'hm')) # range self.assertEqual(guess_time("12 58".split()), (dtime(hour=12, minute=58), 'hm')) self.assertEqual(guess_time("31 04".split()), (dtime(minute=31, second=4), 'ms')) self.assertEqual(guess_time("31 58".split()), (dtime(minute=31, second=58), 'ms')) # three terms self.assertEqual(guess_time("12 34 54".split()), (dtime(hour=12, minute=34, second=54), 'hms')) # four terms self.assertEqual(guess_time("12 34 54 984".split()), (dtime(hour=12, minute=34, second=54, microsecond=984000), 'hmsn')) # trailing zeros self.assertEqual(guess_time("12 34 54 98400".split()), (dtime(hour=12, minute=34, second=54, microsecond=984000), 'hmsn')) # leading zeros self.assertEqual(guess_time("12 34 54 098400".split()), (dtime(hour=12, minute=34, second=54, microsecond=98400), 'hmsn')) # invalid formats self.assertRaises(TimeParserError, guess_time, []) self.assertRaises(TimeParserError, guess_time, ['0', '1', '2', '3', '4']) self.assertRaises(TimeParserError, guess_time, "83 02".split()) self.assertRaises(TimeParserError, guess_time, "52 74".split()) def test_guess_datetime(self): self.assertEqual(guess_datetime( Struct({'date': '18 05 2012'.split(), 'time': '12 34'.split()})), (datetime(2012, 5, 18, 12, 34), 'DMYhm')) self.assertEqual(guess_datetime(Struct({'date': '18 05 2012'.split()})), (datetime(2012, 5, 18), 'DMY')) self.assertEqual(guess_datetime(Struct({'time': '12 34'.split()})), (datetime(1970, 1, 1, 12, 34), 'hm')) self.assertRaises(DateFormatError, guess_datetime, Struct({})) def test_DF(self): # msb self.assertRaises(DateFormatError, DF('').msb) self.assertRaises(DateFormatError, DF('abc').msb) self.assertRaises(DateFormatError, DF('ydHSN').msb) self.assertEqual('Y', DF('Yab').msb()) self.assertEqual('Y', DF('YDM').msb()) self.assertEqual('Y', DF('MdY').msb()) self.assertEqual('D', DF('mDn').msb()) self.assertEqual('m', DF('mdn').msb()) self.assertEqual('n', DF('nab').msb()) # lsb self.assertRaises(DateFormatError, DF('').lsb) self.assertRaises(DateFormatError, DF('abc').lsb) self.assertEqual('Y', DF('Yab').lsb()) self.assertEqual('D', DF('YDM').lsb()) self.assertEqual('M', DF('MdY').lsb()) self.assertEqual('n', DF('mDn').lsb()) self.assertEqual('n', DF('nab').lsb()) # is_time self.assertTrue(DF('mshn').is_time()) self.assertTrue(DF('mh').is_time()) self.assertTrue(DF('h').is_time()) self.assertTrue(DF('m').is_time()) self.assertTrue(DF('s').is_time()) self.assertTrue(DF('n').is_time()) self.assertFalse(DF('').is_time()) self.assertFalse(DF('abc').is_time()) self.assertFalse(DF('Msnh').is_time()) self.assertFalse(DF('Ymsnh').is_time()) self.assertFalse(DF('Dmsnh').is_time()) self.assertFalse(DF('YDM').is_time()) # is_date self.assertTrue(DF('YDM').is_date()) self.assertTrue(DF('YM').is_date()) self.assertTrue(DF('DM').is_date()) self.assertTrue(DF('DY').is_date()) self.assertTrue(DF('Y').is_date()) self.assertTrue(DF('D').is_date()) self.assertTrue(DF('M').is_date()) self.assertFalse(DF('').is_date()) self.assertFalse(DF('abc').is_date()) self.assertFalse(DF('YDMn').is_date()) self.assertFalse(DF('YDm').is_date()) self.assertFalse(DF('YDh').is_date()) self.assertFalse(DF('hmsn').is_date()) # valid self.assertTrue(DF('Y').valid()) self.assertTrue(DF('YDMhsmn').valid()) self.assertTrue(DF('Yabc').valid()) self.assertFalse(DF('').valid()) self.assertFalse(DF('abc').valid()) self.assertFalse(DF('ydHSN').valid()) def test_increment(self): self.assertRaises(DateFormatError, increment, None, '') self.assertEqual(increment(datetime(1970, 1, 1, 0, 0, 0, 10), DF('n')), datetime(1970, 1, 1, 0, 0, 0, 11)) self.assertEqual(increment(datetime(1970, 1, 1, 0, 0, 1), DF('s')), datetime(1970, 1, 1, 0, 0, 2)) self.assertEqual(increment(datetime(1970, 1, 1, 0, 1), DF('m')), datetime(1970, 1, 1, 0, 2)) self.assertEqual(increment(datetime(1970, 1, 1, 1), DF('h')), datetime(1970, 1, 1, 2)) self.assertEqual(increment(datetime(1970, 1, 1, 1), DF('h')), datetime(1970, 1, 1, 2)) self.assertEqual(increment(datetime(1970, 2, 3, 4, 5, 6), DF('D')), datetime(1970, 2, 4)) self.assertEqual(increment(datetime(1970, 2, 3, 4, 5, 6), DF('M')), datetime(1970, 3, 1)) self.assertEqual(increment(datetime(1970, 2, 3, 4, 5, 6), DF('Y')), datetime(1971, 1, 1)) self.assertRaises(DateFormatError, increment, datetime(1970, 2, 3, 4, 5, 6), DF('abc')) ## main ## if __name__ == '__main__': unittest.main() ## EOF ##