** Time interval calculations ** This page is spawned from [Years, months, days, etc. between two dates] This code might provide some useful insight for regression tests with tcl time features. It's been tested on tcl 8.5. On a couple of occasions, drafts of this code may have identified discrepancies. Future ones will be reported to bug tracker. ** procs ** * clock_scan_interval - outputs a timestamp in seconds given a timestammp in seconds and a relative time unit * interval_ymdhs counts - from earliest date forward. All measured units are positive values. * interval_ymdhs_w_units - includes units with interval_ymdhs output * interval_remains_ymdhs - counts from latest date backward. All measured units are negative values. * interval_remains_ymdhs_w_units - includes units with interval_remains_ymdhs ** Sample output ** The comments in the output identify when an audit finds and corrects an inconsistent measurement. ======none Test cases for interval_ymdhs from http://wiki.tcl.tk/3189 : t1 2002-01-31 01:23:45, t2 2002-02-28 12:34:56, 0 years 1 months 0 days 11 hours 11 minutes 11 seconds . t1 20020131T012345, t2 20020228T123456, 0 years 1 months 0 days 11 hours 11 minutes 11 seconds . Examples of Age() interval calculations from PostgreSQL docs t1 2001-04-10, t2 1957-06-13, 43 years 9 months 28 days 0 hours 0 minutes 0 seconds . t1 2001-04-10 14:39:53, t2 1957-06-13, 43 years 9 months 28 days 14 hours 39 minutes 53 seconds . t1 2013-12-25, t2 1955-12-10, 58 years 0 months 15 days 0 hours 0 minutes 0 seconds . t1 2007-04-14, t2 2007-02-15, 0 years 1 months 30 days 0 hours 0 minutes 0 seconds 'should not be 1 month 27 days.' t1 2007-03-14, t2 2007-01-15, 0 years 1 months 27 days 0 hours 0 minutes 0 seconds 'should not be 1 mon 30 days.' Test cases for interval_remains_ymdhs from http://wiki.tcl.tk/3189 : t1 2002-01-31 01:23:45, t2 2002-02-28 12:34:56, 0 years 1 months 0 days 11 hours 11 minutes 11 seconds . t1 20020131T012345, t2 20020228T123456, 0 years 1 months 0 days 11 hours 11 minutes 11 seconds . Examples of Age() interval calculations from PostgreSQL docs t1 2001-04-10, t2 1957-06-13, 43 years 9 months 28 days 0 hours 0 minutes 0 seconds . t1 2001-04-10 14:39:53, t2 1957-06-13, 43 years 9 months 28 days 14 hours 39 minutes 53 seconds . t1 2013-12-25, t2 1955-12-10, 58 years 0 months 15 days 0 hours 0 minutes 0 seconds . The following match: t1 2007-04-14, t2 2007-02-15, 0 years 1 months 30 days 0 hours 0 minutes 0 seconds 'should not be 1 month 27 days.' t1 2007-03-14, t2 2007-01-15, 0 years 1 months 27 days 0 hours 0 minutes 0 seconds 'should not be 1 mon 30 days.' ..providing insight into the quote on reference 1 that these results shouldn't match. 1. http://www.postgresql.org/message-id/17990.1183925224@sss.pgh.pa.us A block of random intervals tests: Tested intervals will show audit and/or error with debug info, if/when it finds discrepancies. Test begins. ................................................................................ ................................................................................ ........................................................................ interval_ymdhs: debug s1 795544645 s2 1300104282 y 15 m 11 d 23 h 19 s 17 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 20 ........ ................................................................................ interval_ymdhs: debug s1 773868169 s2 972852115 y 6 m 3 d 19 h 1 s 6 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 0 ...................................... interval_ymdhs: debug s1 802514196 s2 1446499190 y 20 m 4 d 26 h 12 s 14 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 11 .......................................... ................................................................................ ................... interval_ymdhs: debug s1 614028963 s2 1583772170 y 30 m 8 d 21 h 20 s 47 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 21 ......................................... interval_ymdhs: debug s1 1221969960 s2 1225732036 y 0 m 1 d 13 h 13 s 16 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 12 .................... ................................................................................ ............... interval_remains_ymdhs: debug s1 330594997 s2 104885460 y -7 m -1 d -24 h -9 s -37 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -10 ................................................................. ................................................................................ ................................................................................ ................................................................................ ............ interval_remains_ymdhs: debug s1 952707597 s2 199166607 y -23 m -10 d -16 h -11 s -30 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -12 ... interval_ymdhs: debug s1 104042908 s2 1225764937 y 35 m 6 d 15 h 22 s 9 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 21 ................................................................. ............................................ interval_remains_ymdhs: debug s1 1362525788 s2 1035648131 y -10 m -4 d -10 h -7 s -57 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -6 ....... interval_remains_ymdhs: debug s1 495054013 s2 309898501 y -5 m -10 d -12 h -1 s -12 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to 0 ............................. ............................................... interval_ymdhs: debug s1 1010658157 s2 1268724192 y 8 m 2 d 5 h 21 s 35 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 22 ............. interval_ymdhs: debug s1 448023037 s2 1464414828 y 32 m 2 d 14 h 19 s 11 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 20 .................... ................................................................................ ......................... interval_remains_ymdhs: debug s1 474635690 s2 215502457 y -8 m -2 d -16 h -5 s -13 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -4 ................................................ interval_ymdhs: debug s1 393806198 s2 1478513072 y 34 m 4 d 13 h 11 s 54 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 10 ....... ................... interval_ymdhs: debug s1 344370119 s2 576118453 y 7 m 4 d 5 h 6 s 14 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 7 ....................................................... interval_remains_ymdhs: debug s1 995503285 s2 451961965 y -17 m -2 d -20 h -23 s 0 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -24 ...... .............................................. interval_remains_ymdhs: debug s1 1455289699 s2 860169235 y -18 m -10 d -7 h -22 s -24 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -23 .. interval_remains_ymdhs: debug s1 1251552496 s2 404811884 y -26 m -9 d -30 h -6 s -32 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -5 ......................... interval_ymdhs: debug s1 117510593 s2 1067240517 y 30 m 1 d 5 h 5 s 4 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 4 ....... ............... interval_ymdhs: debug s1 678261625 s2 1112681187 y 13 m 9 d 5 h 23 s 2 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 24 ........................................... interval_remains_ymdhs: debug s1 1463595238 s2 576002387 y -28 m -1 d -16 h -1 s -11 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -2 ...................... ................................................................................ ................................................................................ ................................................................................ ......................... interval_ymdhs: debug s1 948119157 s2 1236591234 y 9 m 1 d 19 h 19 s 57 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 20 ....................................................... ................................................................................ ......... interval_remains_ymdhs: debug s1 673575922 s2 309880710 y -11 m -6 d -10 h -11 s -52 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -10 ....................................................................... ................................................................................ ................................................................................ .......... interval_ymdhs: debug s1 173639883 s2 972907883 y 25 m 3 d 26 h 18 s 20 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 17 ........................................ interval_ymdhs: debug s1 587734762 s2 1049684715 y 14 m 7 d 21 h 14 s 53 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 15 .............................. .......................................................... interval_remains_ymdhs: debug s1 1588066576 s2 458558268 y -35 m -9 d -15 h -1 s -28 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to 0 ...................... ... interval_remains_ymdhs: debug s1 1424495656 s2 278445616 y -36 m -3 d -23 h -11 s 0 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -10 ................ interval_ymdhs: debug s1 980893987 s2 1049706645 y 2 m 2 d 7 h 10 s 38 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 11 ............................................................. ................................. interval_remains_ymdhs: debug s1 1276131460 s2 1004209162 y -8 m -7 d -13 h -6 s -18 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -5 ... interval_ymdhs: debug s1 226268871 s2 986208849 y 24 m 0 d 29 h 14 s 18 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 15 ............................................ ................................................................................ ....................................... interval_remains_ymdhs: debug s1 573202583 s2 260816170 y -9 m -10 d -21 h -14 s -13 s2_diff 86400 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 day to -22 ................ interval_ymdhs: debug s1 5047667 s2 891887413 y 28 m 1 d 9 h 8 s 26 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 9 ......................... ...................................................................... interval_remains_ymdhs: debug s1 936015978 s2 814802018 y -3 m -10 d -2 h -23 s -40 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -22 .......... .............................................. interval_remains_ymdhs: debug s1 1038058494 s2 688431795 y -11 m 0 d -28 h -14 s -39 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -13 .................................. ................................................... interval_remains_ymdhs: debug s1 1123517493 s2 1035613334 y -2 m -9 d -13 h -10 s -19 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -9 ............................. .................................................................. interval_ymdhs: debug s1 954550381 s2 1225703272 y 8 m 7 d 2 h 9 s 51 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 8 .............. ..................................................................... interval_remains_ymdhs: debug s1 1009316566 s2 89091984 y -29 m -1 d -28 h -17 s -22 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -16 ........... ................................................................................ ................................................................................ ................................................................................ ....................................................... interval_remains_ymdhs: debug s1 425285642 s2 294170776 y -4 m -1 d -25 h -12 s -46 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -13 ...... interval_ymdhs: debug s1 113900171 s2 341526335 y 7 m 2 d 16 h 13 s 24 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 12 ................... ...................... interval_remains_ymdhs: debug s1 1583047629 s2 1354434706 y -7 m -2 d -26 h -23 s -23 s2_diff 86400 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 day to -27 .......................................................... ................................................................................ ................................................................................ ................................................................................ ................................................................................ .... interval_ymdhs: debug s1 173512015 s2 230858746 y 1 m 9 d 23 h 16 s 51 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 17 ............................................................................ ........... interval_remains_ymdhs: debug s1 1559989177 s2 467660052 y -34 m -7 d -12 h -17 s -25 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -16 ..................................................................... ................................................................................ ........... interval_ymdhs: debug s1 259336665 s2 452147506 y 6 m 1 d 8 h 14 s 1 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 15 .......................... interval_ymdhs: debug s1 411279019 s2 1363026452 y 30 m 1 d 26 h 14 s 13 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 15 ................................. interval_remains_ymdhs: debug s1 1306264869 s2 419898270 y -28 m -1 d -1 h -20 s -39 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -21 .......... ................................. interval_remains_ymdhs: debug s1 1236515704 s2 1194069620 y -1 m -4 d -5 h -7 s -44 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -6 ............................................... ................................................................................ ................................................................................ ............................................................. interval_remains_ymdhs: debug s1 462557747 s2 357019911 y -3 m -4 d -3 h -12 s -56 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -13 ................... ................................................................................ ................................................................................ ................................................................................ .......... interval_remains_ymdhs: debug s1 1264311047 s2 294154280 y -30 m -8 d -25 h -14 s -27 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -15 ...................................................................... ................................................................................ ................................................................................ ................................................................................ ................................................................................ ..................................................................... interval_ymdhs: debug s1 1265707034 s2 1320666744 y 1 m 8 d 29 h 3 s 10 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 2 ........... ........................ interval_ymdhs: debug s1 251047541 s2 1446419544 y 37 m 10 d 17 h 8 s 43 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 7 ........................................................ ................................................................................ ................................... interval_ymdhs: debug s1 68253130 s2 355659189 y 9 m 1 d 11 h 11 s 59 s2_diff 86400 interval_ymdhs: debug, audit adjustment. decreasing 1 day to 10 ........ interval_remains_ymdhs: debug s1 1441134558 s2 1236440237 y -6 m -5 d -25 h -3 s -1 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -4 . interval_remains_ymdhs: debug s1 1456786781 s2 326730870 y -35 m -9 d -19 h -7 s -11 s2_diff 86400 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 day to -20 ........ interval_remains_ymdhs: debug s1 1162068452 s2 923162339 y -7 m -6 d -25 h -2 s -33 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -3 ............................ ........................................................................... interval_remains_ymdhs: debug s1 281397091 s2 41337765 y -7 m -7 d -7 h -10 s -46 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -11 ..... ................... interval_ymdhs: debug s1 1214582303 s2 1583754060 y 11 m 8 d 10 h 18 s 37 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 19 .................................. interval_remains_ymdhs: debug s1 780334419 s2 575964275 y -6 m -5 d -21 h -9 s -4 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -10 ..................... interval_ymdhs: debug s1 136318146 s2 639024117 y 15 m 11 d 5 h 8 s 51 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 9 interval_remains_ymdhs: debug s1 639024117 s2 136318146 y -15 m -11 d -4 h -8 s -51 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -9 ...... ................................................................ interval_ymdhs: debug s1 1171369585 s2 1289190431 y 3 m 8 d 25 h 17 s 46 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 16 ................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ .......................................................................... interval_remains_ymdhs: debug s1 1568391576 s2 783421810 y -24 m -10 d -15 h -8 s -26 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -7 ...... ................................................................................ ........ interval_remains_ymdhs: debug s1 1387472622 s2 483447418 y -28 m -7 d -22 h -5 s -44 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -6 .. interval_remains_ymdhs: debug s1 1285140592 s2 909246434 y -11 m -10 d -28 h -16 s -38 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -15 ........................................ interval_remains_ymdhs: debug s1 670168621 s2 25607094 y -20 m -5 d -4 h -4 s -7 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -3 .............................. ..................................................................... interval_ymdhs: debug s1 386971903 s2 1425883279 y 32 m 11 d 2 h 10 s 36 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 11 ........... ........................................................................... interval_ymdhs: debug s1 939241184 s2 1415012035 y 15 m 0 d 27 h 14 s 11 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 13 ..... ............................ interval_remains_ymdhs: debug s1 264532516 s2 246965431 y 0 m -6 d -22 h -8 s -45 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -7 ......... interval_remains_ymdhs: debug s1 1078069690 s2 658426798 y -13 m -3 d -15 h -23 s -12 s2_diff 86400 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 day to -16 ........................................... ..... interval_remains_ymdhs: debug s1 1429258008 s2 1162095551 y -8 m -5 d -19 h -4 s -37 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -3 ........................................................................... ........................... interval_ymdhs: debug s1 657388149 s2 1018273118 y 11 m 5 d 7 h 21 s 29 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 22 ................................. interval_ymdhs: debug s1 488701862 s2 752213849 y 8 m 4 d 5 h 21 s 27 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 20 .................... ................................................................................ ................................................................................ .. interval_remains_ymdhs: debug s1 1277929532 s2 702367945 y -18 m -2 d -26 h -14 s -7 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -15 ............................ interval_remains_ymdhs: debug s1 777656299 s2 607386051 y -5 m -4 d -22 h -17 s -28 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -18 .................................................. .............................. interval_remains_ymdhs: debug s1 887532904 s2 783473080 y -3 m -3 d -16 h -9 s -24 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -8 ....................... interval_remains_ymdhs: debug s1 1278349167 s2 278437464 y -31 m -8 d -8 h -2 s -3 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -1 . interval_remains_ymdhs: debug s1 845617012 s2 388546009 y -14 m -5 d -23 h -4 s -3 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -5 .......................... ................................................................................ ................................................................................ ..... interval_remains_ymdhs: debug s1 702682379 s2 514931472 y -5 m -11 d -11 h -1 s -47 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -2 .................................... interval_ymdhs: debug s1 502149976 s2 1414974163 y 28 m 11 d 4 h 3 s 27 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 2 ....................................... ................................................................................ ........... interval_ymdhs: debug s1 803414336 s2 1583721272 y 24 m 8 d 20 h 6 s 36 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 7 ....................................... interval_remains_ymdhs: debug s1 582890609 s2 151968859 y -13 m -7 d -26 h -13 s -10 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -12 .............................. ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ................................................................................ ........................ interval_remains_ymdhs: debug s1 451606026 s2 151971556 y -9 m -5 d -28 h -23 s -50 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -22 ........................................................ .................... interval_remains_ymdhs: debug s1 442145683 s2 151995178 y -9 m -2 d -10 h -5 s -45 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -4 ............................................................ ............. interval_ymdhs: debug s1 168262202 s2 515011744 y 10 m 11 d 25 h 6 s 2 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 7 ................................................................... ................... interval_ymdhs: debug s1 43140100 s2 89340182 y 1 m 5 d 15 h 17 s 22 s2_diff 3600 interval_ymdhs: debug, audit adjustment. decreasing 1 hour to 16 ........ interval_remains_ymdhs: debug s1 889323577 s2 783473180 y -3 m -4 d -9 h -2 s -17 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -1 ....................................... interval_remains_ymdhs: debug s1 673134688 s2 372739398 y -9 m -6 d -8 h -20 s -10 s2_diff -3600 interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to -19 .............. ................................................................................ ................................................................................ ................................................................................ .................................. interval_ymdhs: debug s1 448021260 s2 1486197443 y 32 m 10 d 21 h 21 s 23 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 22 ............................. interval_ymdhs: debug s1 13015962 s2 986196954 y 30 m 10 d 1 h 15 s 12 s2_diff -3600 interval_ymdhs: debug, audit adjustment. increasing 1 hour to 16 .......... interval_ymdhs: debug s1 194446513 s2 1133377054 y 29 m 9 d 2 h 6 s 21 s2_diff 86400 interval_ymdhs: debug, audit adjustment. decreasing 1 day to 1 ....... interval_remains_ymdhs: debug s1 618148164 s2 230600053 y -12 m -3 d -10 h -12 s -11 s2_diff 3600 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to -13 ................................................................................ ................................................................... interval_remains_ymdhs: debug s1 699359768 s2 148923404 y -17 m -5 d -7 h -17 s -24 s2_diff 86400 interval_remains_ymdhs: debug, audit adjustment. decreasing 1 day to -8 ............. ................................................................................ ................................................................................ ................................................................................ ..............................% ====== ** Code ** ====== proc clock_scan_interval { seconds delta units } { # clock_scan_interval formats $seconds to a string for processing by clock scan # then returns new timestamp in seconds set stamp [clock format $seconds -format "%Y%m%dT%H%M%S"] if { $delta < 0 } { append stamp " - " [expr { abs( $delta ) } ] " " $units } else { append stamp " + " $delta " " $units } return [clock scan $stamp] } proc interval_ymdhs { s1 s2 } { # interval_ymdhs calculates the interval of time between # the earliest date and the last date # by starting to count at the earliest date. # This proc has audit features. It will automatically # attempt to correct and report any discrepancies it finds. # if s1 and s2 aren't in seconds, convert to seconds. if { ![string is integer -strict $s1] } { set s1 [clock scan $s1] } if { ![string is integer -strict $s2] } { set s2 [clock scan $s2] } # postgreSQL intervals determine month length based on earliest date in interval calculations. # set s1 to s2 in chronological sequence set sn_list [lsort -integer [list $s1 $s2]] set s1 [lindex $sn_list 0] set s2 [lindex $sn_list 1] # Arithmetic is done from most significant to least significant # The interval is spanned in largest units first. # A new position s1_pN is calculated for the Nth move along the interval. # s1 is s1_p0 # Calculate years from s1_p0 to s2 set y_count 0 set s1_p0 $s1 set s2_y_check $s1_p0 while { $s2_y_check <= $s2 } { set s1_p1 $s2_y_check set y $y_count incr y_count set s2_y_check [clock_scan_interval $s1_p0 $y_count years] } # interval s1_p0 to s1_p1 counted in y years # is the base offset incremented one too much? set s2_y_check [clock_scan_interval $s1 $y years] if { $s2_y_check > $s2 } { set y [expr { $y - 1 } ] set s2_y_check [clock_scan_interval $s1 $y years] } # increment s1 (s1_p0) forward y years to s1_p1 if { $y == 0 } { set s1_p1 $s1 } else { set s1_p1 [clock_scan_interval $s1 $y years] } # interval s1 to s1_p1 counted in y years # Calculate months from s1_p1 to s2 set m_count 0 set s2_m_check $s1_p1 while { $s2_m_check <= $s2 } { set s1_p2 $s2_m_check set m $m_count incr m_count set s2_m_check [clock_scan_interval $s1_p1 $m_count months] } # interval s1_p1 to s1_p2 counted in m months # Calculate interval s1_p2 to s2 in days # day_in_sec [expr { 60 * 60 * 24 } ] # 86400 # Since length of month is not relative, use math. # Clip any fractional part. set d [expr { int( ( $s2 - $s1_p2 ) / 86400. ) } ] # Ideally, this should always be true, but daylight savings.. # so, go backward one day and make hourly steps for last day. if { $d > 0 } { incr d -1 } # Move interval from s1_p2 to s1_p3 set s1_p3 [clock_scan_interval $s1_p2 $d days] # s1_p3 is less than a day from s2 # Calculate interval s1_p3 to s2 in hours # hour_in_sec [expr { 60 * 60 } ] # 3600 set h [expr { int( ( $s2 - $s1_p3 ) / 3600. ) } ] # Move interval from s1_p3 to s1_p4 set s1_p4 [clock_scan_interval $s1_p3 $h hours] # s1_p4 is less than an hour from s2 # Sometimes h = 24, yet is already included as a day! # For example, this case: # interval_ymdhs 20010410T000000 19570613T000000 # from Age() example in PostgreSQL documentation: # http://www.postgresql.org/docs/9.1/static/functions-datetime.html # psql test=# select age(timestamp '2001-04-10', timestamp '1957-06-13'); # age # ------------------------- # 43 years 9 mons 27 days # (1 row) # According to LibreCalc, the difference is 16007 days #puts "s2=s1+16007days? [clock format [clock_scan_interval $s1 16007 days] -format %Y%m%dT%H%M%S]" # ^ this calc is consistent with 16007 days # So, let's ignore the Postgresql irregularity for now. # Here's more background: # http://www.postgresql.org/message-id/5A86CA18-593F-4517-BB83-995115A6A402@morth.org # http://www.postgresql.org/message-id/200707060844.l668i89w097496@wwwmaster.postgresql.org # So, Postgres had a bug.. # Sanity check: if over 24 or 48 hours, push it up to a day unit set h_in_days [expr { int( $h / 24. ) } ] if { $h >= 1 } { # adjust hours to less than a day set h [expr { $h - ( 24 * $h_in_days ) } ] incr d $h_in_days set h_correction_p 1 } else { set h_correction_p 0 } # Calculate interval s1_p4 to s2 in minutes # minute_in_sec [expr { 60 } ] # 60 set mm [expr { int( ( $s2 - $s1_p4 ) / 60. ) } ] # Move interval from s1_p4 to s1_p5 set s1_p5 [clock_scan_interval $s1_p4 $mm minutes] # Sanity check: if 60 minutes, push it up to an hour unit if { $mm >= 60 } { # adjust 60 minutes to 1 hour # puts "interval_ymdhs: debug info mm - 60, h + 1" set mm [expr { $mm - 60 } ] incr h set mm_correction_p 1 } else { set mm_correction_p 0 } # Calculate interval s1_p5 to s2 in seconds set s [expr { int( $s2 - $s1_p5 ) } ] # Sanity check: if 60 seconds, push it up to one minute unit if { $s >= 60 } { # adjust 60 minutes to 1 hour set s [expr { $s - 60 } ] incr mm set s_correction_p 1 } else { set s_correction_p 0 } set return_list [list $y $m $d $h $mm $s] # test results by adding difference to s1 to get s2: set i 0 set s1_test [clock format $s1 -format "%Y%m%dT%H%M%S"] set signs_inconsistent_p 0 foreach unit {years months days hours minutes seconds} { set t_term [lindex $return_list $i] if { $t_term != 0 } { if { $t_term > 0 } { append s1_test " + $t_term $unit" } else { append s1_test " - [expr { abs( $t_term ) } ] $unit" set signs_inconsistent_p 1 } } incr i } set s2_test [clock scan $s1_test] # puts "test s2 '$s2_test' from: '$s1_test'" set counter 0 while { $s2 ne $s2_test && $counter < 30 } { set s2_diff [expr { $s2_test - $s2 } ] puts "\ninterval_ymdhs: debug s1 $s1 s2 $s2 y $y m $m d $d h $h s $s s2_diff $s2_diff" if { [expr { abs($s2_diff) } ] > 86399 } { if { $s2_diff > 0 } { incr d -1 puts "interval_ymdhs: debug, audit adjustment. decreasing 1 day to $d" } else { incr d puts "interval_ymdhs: debug, audit adjustment. increasing 1 day to $d" } } elseif { [expr { abs($s2_diff) } ] > 3599 } { if { $s2_diff > 0 } { incr h -1 puts "interval_ymdhs: debug, audit adjustment. decreasing 1 hour to $h" } else { incr h puts "interval_ymdhs: debug, audit adjustment. increasing 1 hour to $h" } } elseif { [expr { abs($s2_diff) } ] > 59 } { if { $s2_diff > 0 } { incr mm -1 puts "interval_ymdhs: debug, audit adjustment. decreasing 1 minute to $mm" } else { incr mm puts "interval_ymdhs: debug, audit adjustment. increasing 1 minute to $mm" } } elseif { [expr { abs($s2_diff) } ] > 0 } { if { $s2_diff > 0 } { incr s -1 puts "interval_ymdhs: debug, audit adjustment. decreasing 1 second to $s" } else { incr s puts "interval_ymdhs: debug, audit adjustment. increasing 1 second to $s" } } set return_list [list $y $m $d $h $mm $s] # set return_list [list [expr { abs($y) } ] [expr { abs($m) } ] [expr { abs($d) } ] [expr { abs($h) } ] [expr { abs($mm) } ] [expr { abs($s) } ]] # test results by adding difference to s1 to get s2: set i 0 set s1_test [clock format $s1 -format "%Y%m%dT%H%M%S"] foreach unit {years months days hours minutes seconds} { set t_term [lindex $return_list $i] if { $t_term != 0 } { if { $t_term > 0 } { append s1_test " + $t_term $unit" } else { append s1_test " - [expr { abs( $t_term ) } ] $unit" } } incr i } set s2_test [clock scan $s1_test] incr counter } if { ( $counter > 0 || $signs_inconsistent_p ) && ( $h_correction_p || $mm_correction_p || $s_correction_p ) } { # puts "interval_ymdhs: Corrections in the main calculation were applied: h ${h_correction_p}, mm ${mm_correction_p}, s ${s_correction_p}" } if { $signs_inconsistent_p } { puts "\ninterval_ymdhs: signs inconsistent y $y m $m d $d h $h mm $mm s $s" } if { $s2 eq $s2_test } { return $return_list } else { set s2_diff [expr { $s2_test - $s2 } ] puts "debug s1 $s1 s1_p1 $s1_p1 s1_p2 $s1_p2 s1_p3 $s1_p3 s1_p4 $s1_p4" puts "debug y $y m $m d $d h $h mm $mm s $s" puts "interval_ymdhs error: s2 is '$s2' but s2_test is '$s2_test' a difference of ${s2_diff} from s1 '$s1_test'." # error "result audit fails" "error: s2 is $s2 but s2_test is '$s2_test' a difference of ${s2_diff} from: '$s1_test'." } } proc interval_ymdhs_w_units { t1 t2 } { # interval_ymdhs_w_units # returns interval_ymdhs values with units set v_list [interval_ymdhs $t2 $t1] set i 0 set a "" foreach f {years months days hours minutes seconds} { append a "[lindex $v_list $i] $f \n" incr i } return $a } proc interval_remains_ymdhs { s1 s2 } { # interval_remains_ymdhs calculates the interval of time between # the earliest date and the last date # by starting to count at the last date and work backwards in time. # This proc has audit features. It will automatically # attempt to correct and report any discrepancies it finds. # if s1 and s2 aren't in seconds, convert to seconds. if { ![string is integer -strict $s1] } { set s1 [clock scan $s1] } if { ![string is integer -strict $s2] } { set s2 [clock scan $s2] } # set s1 to s2 in reverse chronological sequence set sn_list [lsort -decreasing -integer [list $s1 $s2]] set s1 [lindex $sn_list 0] set s2 [lindex $sn_list 1] # Arithmetic is done from most significant to least significant # The interval is spanned in largest units first. # A new position s1_pN is calculated for the Nth move along the interval. # s1 is s1_p0 # Calculate years from s1_p0 to s2 set y_count 0 set s1_p0 $s1 set s2_y_check $s1_p0 while { $s2_y_check > $s2 } { set s1_p1 $s2_y_check set y $y_count incr y_count -1 set s2_y_check [clock_scan_interval $s1_p0 $y_count years] } # interval s1_p0 to s1_p1 counted in y years # Calculate months from s1_p1 to s2 set m_count 0 set s2_m_check $s1_p1 while { $s2_m_check > $s2 } { set s1_p2 $s2_m_check set m $m_count incr m_count -1 set s2_m_check [clock_scan_interval $s1_p1 $m_count months] } # interval s1_p1 to s1_p2 counted in m months # Calculate interval s1_p2 to s2 in days # day_in_sec [expr { 60 * 60 * 24 } ] # 86400 # Since length of month is not relative, use math. # Clip any fractional part. set d [expr { int( ceil( ( $s2 - $s1_p2 ) / 86400. ) ) } ] # Ideally, this should always be true, but daylight savings.. # so, go backward one day and make hourly steps for last day. if { $d < 0 } { incr d } # Move interval from s1_p2 to s1_p3 set s1_p3 [clock_scan_interval $s1_p2 $d days] # s1_p3 is less than a day from s2 # Calculate interval s1_p3 to s2 in hours # hour_in_sec [expr { 60 * 60 } ] # 3600 set h [expr { int( ceil( ( $s2 - $s1_p3 ) / 3600. ) ) } ] # Move interval from s1_p3 to s1_p4 set s1_p4 [clock_scan_interval $s1_p3 $h hours] # s1_p4 is less than an hour from s2 # Sanity check: if over 24 or 48 hours, push it up to a day unit set h_in_days [expr { int( ceil( $h / 24. ) ) } ] if { $h_in_days <= -1 } { # adjust hours to less than a day set h [expr { $h - ( 24 * $h_in_days ) } ] incr d $h_in_days set h_correction_p 1 } else { set h_correction_p 0 } # Calculate interval s1_p4 to s2 in minutes # minute_in_sec [expr { 60 } ] # 60 set mm [expr { int( ceil( ( $s2 - $s1_p4 ) / 60. ) ) } ] # Move interval from s1_p4 to s1_p5 set s1_p5 [clock_scan_interval $s1_p4 $mm minutes] # Sanity check: if 60 minutes, push it up to an hour unit if { $mm <= -60 } { # adjust 60 minutes to 1 hour # puts "interval_remains_ymdhs: debug info mm + 60, h - 1" set mm [expr { $mm + 60 } ] incr h -1 set mm_correction_p 1 } else { set mm_correction_p 0 } # Calculate interval s1_p5 to s2 in seconds set s [expr { $s2 - $s1_p5 } ] # Sanity check: if 60 seconds, push it up to one minute unit if { $s <= -60 } { # adjust 60 minutes to 1 hour set s [expr { $s + 60 } ] incr mm -1 set s_correction_p 1 } else { set s_correction_p 0 } set return_list [list $y $m $d $h $mm $s] # set return_list [list [expr { abs($y) } ] [expr { abs($m) } ] [expr { abs($d) } ] [expr { abs($h) } ] [expr { abs($mm) } ] [expr { abs($s) } ]] # test results by adding difference to s1 to get s2: set i 0 set s1_test [clock format $s1 -format "%Y%m%dT%H%M%S"] set signs_inconsistent_p 0 foreach unit {years months days hours minutes seconds} { set t_term [lindex $return_list $i] if { $t_term != 0 } { if { $t_term > 0 } { append s1_test " + $t_term $unit" set signs_inconsistent_p 1 } else { append s1_test " - [expr { abs( $t_term ) } ] $unit" } } incr i } set s2_test [clock scan $s1_test] set counter 0 while { $s2 ne $s2_test && $counter < 3 } { set s2_diff [expr { $s2_test - $s2 } ] puts "\ninterval_remains_ymdhs: debug s1 $s1 s2 $s2 y $y m $m d $d h $h s $s s2_diff $s2_diff" if { [expr { abs($s2_diff) } ] >= 86399 } { if { $s2_diff > 0 } { incr d -1 puts "interval_remains_ymdhs: debug, audit adjustment. decreasing 1 day to $d" } else { incr d puts "interval_remains_ymdhs: debug, audit adjustment. increasing 1 day to $d" } } elseif { [expr { abs($s2_diff) } ] > 3599 } { if { $s2_diff > 0 } { incr h -1 puts "interval_remains_ymdhs: debug, audit adjustment. decreasing 1 hour to $h" } else { incr h puts "interval_remains_ymdhs: debug, audit adjustment. increasing 1 hour to $h" } } elseif { [expr { abs($s2_diff) } ] > 59 } { if { $s2_diff > 0 } { incr mm -1 puts "interval_remains_ymdhs: debug, audit adjustment. decreasing 1 minute to $mm" } else { incr mm puts "interval_remains_ymdhs: debug, audit adjustment. increasing 1 minute to $mm" } } elseif { [expr { abs($s2_diff) } ] > 0 } { if { $s2_diff > 0 } { incr s -1 puts "interval_remains_ymdhs: debug, audit adjustment. decreasing 1 second to $s" } else { incr s puts "interval_remains_ymdhs: debug, audit adjustment. increasing 1 second to $s" } } set return_list [list $y $m $d $h $mm $s] # set return_list [list [expr { abs($y) } ] [expr { abs($m) } ] [expr { abs($d) } ] [expr { abs($h) } ] [expr { abs($mm) } ] [expr { abs($s) } ]] # test results by adding difference to s1 to get s2: set i 0 set s1_test [clock format $s1 -format "%Y%m%dT%H%M%S"] foreach unit {years months days hours minutes seconds} { set t_term [lindex $return_list $i] if { $t_term != 0 } { if { $t_term > 0 } { append s1_test " + $t_term $unit" } else { append s1_test " - [expr { abs( $t_term ) } ] $unit" } } incr i } set s2_test [clock scan $s1_test] incr counter } if { ( $counter > 0 || $signs_inconsistent_p ) && ( $h_correction_p || $mm_correction_p || $s_correction_p ) } { # puts "interval_remains_ymdhs: Corrections in the main calculation were applied: h ${h_correction_p}, mm ${mm_correction_p}, s ${s_correction_p}" } if { $signs_inconsistent_p } { puts "\ninterval_remains_ymdhs: signs inconsistent y $y m $m d $d h $h mm $mm s $s" } if { $s2 eq $s2_test } { return $return_list } else { set s2_diff [expr { $s2_test - $s2 } ] puts "debug s1 $s1 s1_p1 $s1_p1 s1_p2 $s1_p2 s1_p3 $s1_p3 s1_p4 $s1_p4" puts "debug y $y m $m d $d h $h mm $mm s $s" puts "interval_remains_ymdhs error: s2 is '$s2' but s2_test is '$s2_test' a difference of ${s2_diff} from s1 '$s1_test'." # error "result audit fails" "error: s2 is $s2 but s2_test is '$s2_test' a difference of ${s2_diff} from: '$s1_test'." } } proc interval_remains_ymdhs_w_units { t1 t2 } { # interval_remains_ymdhs_w_units # returns interval_remains_ymdhs values with units set v_list [interval_ymdhs $t2 $t1] set i 0 set a "" foreach f {years months days hours minutes seconds} { append a "[lindex $v_list $i] $f \n" incr i } return $a } puts "\n\nTest cases for interval_ymdhs from http://wiki.tcl.tk/3189 :" set t1 "2002-01-31 01:23:45" set t2 "2002-02-28 12:34:56" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2]." set t1 "20020131T012345" set t2 "20020228T123456" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2]." puts "\n\nExamples of Age() interval calculations from PostgreSQL docs" # http://www.postgresql.org/docs/9.1/static/functions-datetime.html set t1 "2001-04-10" set t2 "1957-06-13" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2]." set t1 "2001-04-10 14:39:53" set t2 "1957-06-13" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2]." set t1 "2013-12-25" set t2 "1955-12-10" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2]." # following test per http://www.postgresql.org/message-id/17990.1183925224@sss.pgh.pa.us set t1 "2007-04-14" set t2 "2007-02-15" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2] 'should not be 1 month 27 days.'" set t1 "2007-03-14" set t2 "2007-01-15" puts "t1 $t1, t2 $t2, [interval_ymdhs_w_units $t1 $t2] 'should not be 1 mon 30 days.'" puts "\n\nTest cases for interval_remains_ymdhs from http://wiki.tcl.tk/3189 :" set t1 "2002-01-31 01:23:45" set t2 "2002-02-28 12:34:56" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2]." set t1 "20020131T012345" set t2 "20020228T123456" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2]." puts "Examples of Age() interval calculations from PostgreSQL docs" # http://www.postgresql.org/docs/9.1/static/functions-datetime.html set t1 "2001-04-10" set t2 "1957-06-13" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2]." set t1 "2001-04-10 14:39:53" set t2 "1957-06-13" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2]." set t1 "2013-12-25" set t2 "1955-12-10" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2]." # following test per http://www.postgresql.org/message-id/17990.1183925224@sss.pgh.pa.us set t1 "2007-04-14" set t2 "2007-02-15" puts "The following match:" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2] 'should not be 1 month 27 days.'" set t1 "2007-03-14" set t2 "2007-01-15" puts "t1 $t1, t2 $t2, [interval_remains_ymdhs_w_units $t1 $t2] 'should not be 1 mon 30 days.'" puts "..providing insight into the quote on reference 1 that these results shouldn't match." puts "1. http://www.postgresql.org/message-id/17990.1183925224@sss.pgh.pa.us " puts "\n\nA block of random intervals tests:" puts "Tested intervals will show audit and/or error with debug info, if/when it finds discrepancies." puts -nonewline "Test begins" set counter 0 for {set ii 1} {$ii < 10} {incr ii} { # puts "\nTesting loop $counter." for {set i 1} {$i < 1000} {incr i} { set t1 [clock format [expr { round( rand() * 1500000000 ) } ] -format "%Y%m%dT%H%M%S"] set t2 [clock format [expr { round( rand() * 1600000000 ) } ] -format "%Y%m%dT%H%M%S"] puts -nonewline "." set a [interval_ymdhs $t1 $t2] set b [interval_remains_ymdhs $t1 $t2] if { [expr { $counter / 80. } ] == [expr { $counter / 80 } ] } { # linebreak on 80 columns puts "" } incr counter } } ======