Fossil SCM

Merge from trunk.

brickviking 2024-12-27 11:39 bv-infotool merge
Commit 938ffaf0c82a7febf2f99c2fb38c9d95bd32613d4b993d373a4d5f814b778b1e
4 files changed +62 -29 +32 -9 +4 -4 +232 -93
+62 -29
--- src/db.c
+++ src/db.c
@@ -1349,10 +1349,50 @@
13491349
sqlite3_result_int64(context, rid);
13501350
}
13511351
}
13521352
}
13531353
1354
+
1355
+/*
1356
+** SETTING: timeline-utc boolean default=on
1357
+**
1358
+** If the timeline-utc setting is true, then Fossil tries to understand
1359
+** and display all time values using UTC. If this setting is false, Fossil
1360
+** tries to understand and display time values using the local timezone.
1361
+**
1362
+** The word "timeline" in the name of this setting is historical.
1363
+** This setting applies to all user interfaces of Fossil,
1364
+** not just the timeline.
1365
+**
1366
+** Note that when accessing Fossil using the web interface, the localtime
1367
+** used is the localtime on the server, not on the client.
1368
+*/
1369
+/*
1370
+** Return true if Fossil is set to display times as UTC. Return false
1371
+** if it wants to display times using the local timezone.
1372
+**
1373
+** False is returned if display is set to localtime even if the localtime
1374
+** happens to be the same as UTC.
1375
+*/
1376
+int fossil_ui_utctime(void){
1377
+ if( g.fTimeFormat==0 ){
1378
+ if( db_get_int("timeline-utc", 1) ){
1379
+ g.fTimeFormat = 1; /* UTC */
1380
+ }else{
1381
+ g.fTimeFormat = 2; /* Localtime */
1382
+ }
1383
+ }
1384
+ return g.fTimeFormat==1;
1385
+}
1386
+
1387
+/*
1388
+** Return true if Fossil is set to display times using the local timezone.
1389
+*/
1390
+int fossil_ui_localtime(void){
1391
+ return fossil_ui_utctime()==0;
1392
+}
1393
+
13541394
/*
13551395
** The toLocal() SQL function returns a string that is an argument to a
13561396
** date/time function that is appropriate for modifying the time for display.
13571397
** If UTC time display is selected, no modification occurs. If local time
13581398
** display is selected, the time is adjusted appropriately.
@@ -1364,18 +1404,11 @@
13641404
void db_tolocal_function(
13651405
sqlite3_context *context,
13661406
int argc,
13671407
sqlite3_value **argv
13681408
){
1369
- if( g.fTimeFormat==0 ){
1370
- if( db_get_int("timeline-utc", 1) ){
1371
- g.fTimeFormat = 1;
1372
- }else{
1373
- g.fTimeFormat = 2;
1374
- }
1375
- }
1376
- if( g.fTimeFormat==1 ){
1409
+ if( fossil_ui_utctime() ){
13771410
sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
13781411
}else{
13791412
sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
13801413
}
13811414
}
@@ -1393,18 +1426,11 @@
13931426
void db_fromlocal_function(
13941427
sqlite3_context *context,
13951428
int argc,
13961429
sqlite3_value **argv
13971430
){
1398
- if( g.fTimeFormat==0 ){
1399
- if( db_get_int("timeline-utc", 1) ){
1400
- g.fTimeFormat = 1;
1401
- }else{
1402
- g.fTimeFormat = 2;
1403
- }
1404
- }
1405
- if( g.fTimeFormat==1 ){
1431
+ if( fossil_ui_utctime() ){
14061432
sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
14071433
}else{
14081434
sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
14091435
}
14101436
}
@@ -1561,24 +1587,31 @@
15611587
** Register the SQL functions that are useful both to the internal
15621588
** representation and to the "fossil sql" command.
15631589
*/
15641590
void db_add_aux_functions(sqlite3 *db){
15651591
sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
1566
- sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0,
1567
- db_checkin_mtime_function, 0, 0);
1568
- sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0,
1569
- db_sym2rid_function, 0, 0);
1570
- sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0,
1571
- db_sym2rid_function, 0, 0);
1572
- sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0,
1592
+ sqlite3_create_function(db, "checkin_mtime", 2,
1593
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1594
+ 0, db_checkin_mtime_function, 0, 0);
1595
+ sqlite3_create_function(db, "symbolic_name_to_rid", 1,
1596
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1597
+ 0, db_sym2rid_function, 0, 0);
1598
+ sqlite3_create_function(db, "symbolic_name_to_rid", 2,
1599
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1600
+ 0, db_sym2rid_function, 0, 0);
1601
+ sqlite3_create_function(db, "now", 0,
1602
+ SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
15731603
db_now_function, 0, 0);
1574
- sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0,
1575
- db_tolocal_function, 0, 0);
1576
- sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0,
1577
- db_fromlocal_function, 0, 0);
1578
- sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0,
1579
- db_hextoblob, 0, 0);
1604
+ sqlite3_create_function(db, "toLocal", 0,
1605
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1606
+ 0, db_tolocal_function, 0, 0);
1607
+ sqlite3_create_function(db, "fromLocal", 0,
1608
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1609
+ 0, db_fromlocal_function, 0, 0);
1610
+ sqlite3_create_function(db, "hextoblob", 1,
1611
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1612
+ 0, db_hextoblob, 0, 0);
15801613
sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
15811614
0, capability_union_step, capability_union_finalize);
15821615
sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
15831616
capability_fullcap, 0, 0);
15841617
sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
15851618
--- src/db.c
+++ src/db.c
@@ -1349,10 +1349,50 @@
1349 sqlite3_result_int64(context, rid);
1350 }
1351 }
1352 }
1353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1354 /*
1355 ** The toLocal() SQL function returns a string that is an argument to a
1356 ** date/time function that is appropriate for modifying the time for display.
1357 ** If UTC time display is selected, no modification occurs. If local time
1358 ** display is selected, the time is adjusted appropriately.
@@ -1364,18 +1404,11 @@
1364 void db_tolocal_function(
1365 sqlite3_context *context,
1366 int argc,
1367 sqlite3_value **argv
1368 ){
1369 if( g.fTimeFormat==0 ){
1370 if( db_get_int("timeline-utc", 1) ){
1371 g.fTimeFormat = 1;
1372 }else{
1373 g.fTimeFormat = 2;
1374 }
1375 }
1376 if( g.fTimeFormat==1 ){
1377 sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
1378 }else{
1379 sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
1380 }
1381 }
@@ -1393,18 +1426,11 @@
1393 void db_fromlocal_function(
1394 sqlite3_context *context,
1395 int argc,
1396 sqlite3_value **argv
1397 ){
1398 if( g.fTimeFormat==0 ){
1399 if( db_get_int("timeline-utc", 1) ){
1400 g.fTimeFormat = 1;
1401 }else{
1402 g.fTimeFormat = 2;
1403 }
1404 }
1405 if( g.fTimeFormat==1 ){
1406 sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
1407 }else{
1408 sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
1409 }
1410 }
@@ -1561,24 +1587,31 @@
1561 ** Register the SQL functions that are useful both to the internal
1562 ** representation and to the "fossil sql" command.
1563 */
1564 void db_add_aux_functions(sqlite3 *db){
1565 sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
1566 sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0,
1567 db_checkin_mtime_function, 0, 0);
1568 sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0,
1569 db_sym2rid_function, 0, 0);
1570 sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0,
1571 db_sym2rid_function, 0, 0);
1572 sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0,
 
 
 
 
1573 db_now_function, 0, 0);
1574 sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0,
1575 db_tolocal_function, 0, 0);
1576 sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0,
1577 db_fromlocal_function, 0, 0);
1578 sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0,
1579 db_hextoblob, 0, 0);
 
 
 
1580 sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
1581 0, capability_union_step, capability_union_finalize);
1582 sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
1583 capability_fullcap, 0, 0);
1584 sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
1585
--- src/db.c
+++ src/db.c
@@ -1349,10 +1349,50 @@
1349 sqlite3_result_int64(context, rid);
1350 }
1351 }
1352 }
1353
1354
1355 /*
1356 ** SETTING: timeline-utc boolean default=on
1357 **
1358 ** If the timeline-utc setting is true, then Fossil tries to understand
1359 ** and display all time values using UTC. If this setting is false, Fossil
1360 ** tries to understand and display time values using the local timezone.
1361 **
1362 ** The word "timeline" in the name of this setting is historical.
1363 ** This setting applies to all user interfaces of Fossil,
1364 ** not just the timeline.
1365 **
1366 ** Note that when accessing Fossil using the web interface, the localtime
1367 ** used is the localtime on the server, not on the client.
1368 */
1369 /*
1370 ** Return true if Fossil is set to display times as UTC. Return false
1371 ** if it wants to display times using the local timezone.
1372 **
1373 ** False is returned if display is set to localtime even if the localtime
1374 ** happens to be the same as UTC.
1375 */
1376 int fossil_ui_utctime(void){
1377 if( g.fTimeFormat==0 ){
1378 if( db_get_int("timeline-utc", 1) ){
1379 g.fTimeFormat = 1; /* UTC */
1380 }else{
1381 g.fTimeFormat = 2; /* Localtime */
1382 }
1383 }
1384 return g.fTimeFormat==1;
1385 }
1386
1387 /*
1388 ** Return true if Fossil is set to display times using the local timezone.
1389 */
1390 int fossil_ui_localtime(void){
1391 return fossil_ui_utctime()==0;
1392 }
1393
1394 /*
1395 ** The toLocal() SQL function returns a string that is an argument to a
1396 ** date/time function that is appropriate for modifying the time for display.
1397 ** If UTC time display is selected, no modification occurs. If local time
1398 ** display is selected, the time is adjusted appropriately.
@@ -1364,18 +1404,11 @@
1404 void db_tolocal_function(
1405 sqlite3_context *context,
1406 int argc,
1407 sqlite3_value **argv
1408 ){
1409 if( fossil_ui_utctime() ){
 
 
 
 
 
 
 
1410 sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
1411 }else{
1412 sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
1413 }
1414 }
@@ -1393,18 +1426,11 @@
1426 void db_fromlocal_function(
1427 sqlite3_context *context,
1428 int argc,
1429 sqlite3_value **argv
1430 ){
1431 if( fossil_ui_utctime() ){
 
 
 
 
 
 
 
1432 sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
1433 }else{
1434 sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
1435 }
1436 }
@@ -1561,24 +1587,31 @@
1587 ** Register the SQL functions that are useful both to the internal
1588 ** representation and to the "fossil sql" command.
1589 */
1590 void db_add_aux_functions(sqlite3 *db){
1591 sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
1592 sqlite3_create_function(db, "checkin_mtime", 2,
1593 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1594 0, db_checkin_mtime_function, 0, 0);
1595 sqlite3_create_function(db, "symbolic_name_to_rid", 1,
1596 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1597 0, db_sym2rid_function, 0, 0);
1598 sqlite3_create_function(db, "symbolic_name_to_rid", 2,
1599 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1600 0, db_sym2rid_function, 0, 0);
1601 sqlite3_create_function(db, "now", 0,
1602 SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
1603 db_now_function, 0, 0);
1604 sqlite3_create_function(db, "toLocal", 0,
1605 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1606 0, db_tolocal_function, 0, 0);
1607 sqlite3_create_function(db, "fromLocal", 0,
1608 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1609 0, db_fromlocal_function, 0, 0);
1610 sqlite3_create_function(db, "hextoblob", 1,
1611 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
1612 0, db_hextoblob, 0, 0);
1613 sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
1614 0, capability_union_step, capability_union_finalize);
1615 sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
1616 capability_fullcap, 0, 0);
1617 sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
1618
+32 -9
--- src/name.c
+++ src/name.c
@@ -65,33 +65,51 @@
6565
** the string is a hash prefix and NULL is returned if it is. If the
6666
** bVerifyNotAHash flag is false, then the result is determined by syntax
6767
** of the input string only, without reference to the artifact table.
6868
*/
6969
char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
70
- static char zEDate[20];
70
+ static char zEDate[24];
7171
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
7272
int n = (int)strlen(zIn);
7373
int i, j;
74
+ int addZulu = 0;
7475
75
- /* Only three forms allowed:
76
- ** (1) YYYYMMDD
77
- ** (2) YYYYMMDDHHMM
78
- ** (3) YYYYMMDDHHMMSS
76
+ /* These forms are allowed:
77
+ **
78
+ ** 123456789 1234 123456789 123456789
79
+ ** (1) YYYYMMDD => YYYY-MM-DD
80
+ ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM
81
+ ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS
82
+ **
83
+ ** An optional "Z" zulu timezone designator is allowed at the end.
7984
*/
80
- if( n!=8 && n!=12 && n!=14 ) return 0;
85
+ if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
86
+ n--;
87
+ addZulu = 1;
88
+ }
89
+ if( n!=8 && n!=12 && n!=14 ){
90
+ return 0;
91
+ }
8192
8293
/* Every character must be a digit */
8394
for(i=0; fossil_isdigit(zIn[i]); i++){}
84
- if( i!=n ) return 0;
95
+ if( i!=n && (!addZulu || i!=n+1) ) return 0;
8596
8697
/* Expand the date */
87
- for(i=j=0; zIn[i]; i++){
98
+ for(i=j=0; i<n; i++){
8899
if( i>=4 && (i%2)==0 ){
89100
zEDate[j++] = aPunct[i/2];
90101
}
91102
zEDate[j++] = zIn[i];
92103
}
104
+ if( addZulu ){
105
+ if( j==10 ){
106
+ memcpy(&zEDate[10]," 00:00", 6);
107
+ j += 6;
108
+ }
109
+ zEDate[j++] = 'Z';
110
+ }
93111
zEDate[j] = 0;
94112
95113
/* Check for reasonable date values.
96114
** Offset references:
97115
** YYYY-MM-DD HH:MM:SS
@@ -111,11 +129,11 @@
111129
if( i>60 ) return 0;
112130
if( n==14 && atoi(zEDate+17)>60 ) return 0;
113131
}
114132
115133
/* The string is not also a hash prefix */
116
- if( bVerifyNotAHash ){
134
+ if( bVerifyNotAHash && !addZulu ){
117135
if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0;
118136
}
119137
120138
/* It looks like this may be a date. Return it with punctuation added. */
121139
return zEDate;
@@ -453,10 +471,12 @@
453471
}else if( fossil_strcmp(zTag, "next")==0 ){
454472
rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
455473
" ORDER BY isprim DESC, mtime DESC", ridCkout);
456474
}else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
457475
rid = RID_CKOUT;
476
+ assert(ridCkout>0);
477
+ g.localOpen = ridCkout;
458478
}
459479
if( rid ) return rid;
460480
}
461481
462482
/* Date and times */
@@ -699,10 +719,13 @@
699719
}else if( rid==0 ){
700720
fossil_error(iErrPriority, "cannot resolve name: %s", zName);
701721
return 1;
702722
}else{
703723
blob_reset(pName);
724
+ if( RID_CKOUT==rid ) {
725
+ rid = g.localOpen;
726
+ }
704727
db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
705728
return 0;
706729
}
707730
}
708731
709732
--- src/name.c
+++ src/name.c
@@ -65,33 +65,51 @@
65 ** the string is a hash prefix and NULL is returned if it is. If the
66 ** bVerifyNotAHash flag is false, then the result is determined by syntax
67 ** of the input string only, without reference to the artifact table.
68 */
69 char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
70 static char zEDate[20];
71 static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
72 int n = (int)strlen(zIn);
73 int i, j;
 
74
75 /* Only three forms allowed:
76 ** (1) YYYYMMDD
77 ** (2) YYYYMMDDHHMM
78 ** (3) YYYYMMDDHHMMSS
 
 
 
 
79 */
80 if( n!=8 && n!=12 && n!=14 ) return 0;
 
 
 
 
 
 
81
82 /* Every character must be a digit */
83 for(i=0; fossil_isdigit(zIn[i]); i++){}
84 if( i!=n ) return 0;
85
86 /* Expand the date */
87 for(i=j=0; zIn[i]; i++){
88 if( i>=4 && (i%2)==0 ){
89 zEDate[j++] = aPunct[i/2];
90 }
91 zEDate[j++] = zIn[i];
92 }
 
 
 
 
 
 
 
93 zEDate[j] = 0;
94
95 /* Check for reasonable date values.
96 ** Offset references:
97 ** YYYY-MM-DD HH:MM:SS
@@ -111,11 +129,11 @@
111 if( i>60 ) return 0;
112 if( n==14 && atoi(zEDate+17)>60 ) return 0;
113 }
114
115 /* The string is not also a hash prefix */
116 if( bVerifyNotAHash ){
117 if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0;
118 }
119
120 /* It looks like this may be a date. Return it with punctuation added. */
121 return zEDate;
@@ -453,10 +471,12 @@
453 }else if( fossil_strcmp(zTag, "next")==0 ){
454 rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
455 " ORDER BY isprim DESC, mtime DESC", ridCkout);
456 }else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
457 rid = RID_CKOUT;
 
 
458 }
459 if( rid ) return rid;
460 }
461
462 /* Date and times */
@@ -699,10 +719,13 @@
699 }else if( rid==0 ){
700 fossil_error(iErrPriority, "cannot resolve name: %s", zName);
701 return 1;
702 }else{
703 blob_reset(pName);
 
 
 
704 db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
705 return 0;
706 }
707 }
708
709
--- src/name.c
+++ src/name.c
@@ -65,33 +65,51 @@
65 ** the string is a hash prefix and NULL is returned if it is. If the
66 ** bVerifyNotAHash flag is false, then the result is determined by syntax
67 ** of the input string only, without reference to the artifact table.
68 */
69 char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
70 static char zEDate[24];
71 static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
72 int n = (int)strlen(zIn);
73 int i, j;
74 int addZulu = 0;
75
76 /* These forms are allowed:
77 **
78 ** 123456789 1234 123456789 123456789
79 ** (1) YYYYMMDD => YYYY-MM-DD
80 ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM
81 ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS
82 **
83 ** An optional "Z" zulu timezone designator is allowed at the end.
84 */
85 if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
86 n--;
87 addZulu = 1;
88 }
89 if( n!=8 && n!=12 && n!=14 ){
90 return 0;
91 }
92
93 /* Every character must be a digit */
94 for(i=0; fossil_isdigit(zIn[i]); i++){}
95 if( i!=n && (!addZulu || i!=n+1) ) return 0;
96
97 /* Expand the date */
98 for(i=j=0; i<n; i++){
99 if( i>=4 && (i%2)==0 ){
100 zEDate[j++] = aPunct[i/2];
101 }
102 zEDate[j++] = zIn[i];
103 }
104 if( addZulu ){
105 if( j==10 ){
106 memcpy(&zEDate[10]," 00:00", 6);
107 j += 6;
108 }
109 zEDate[j++] = 'Z';
110 }
111 zEDate[j] = 0;
112
113 /* Check for reasonable date values.
114 ** Offset references:
115 ** YYYY-MM-DD HH:MM:SS
@@ -111,11 +129,11 @@
129 if( i>60 ) return 0;
130 if( n==14 && atoi(zEDate+17)>60 ) return 0;
131 }
132
133 /* The string is not also a hash prefix */
134 if( bVerifyNotAHash && !addZulu ){
135 if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0;
136 }
137
138 /* It looks like this may be a date. Return it with punctuation added. */
139 return zEDate;
@@ -453,10 +471,12 @@
471 }else if( fossil_strcmp(zTag, "next")==0 ){
472 rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
473 " ORDER BY isprim DESC, mtime DESC", ridCkout);
474 }else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
475 rid = RID_CKOUT;
476 assert(ridCkout>0);
477 g.localOpen = ridCkout;
478 }
479 if( rid ) return rid;
480 }
481
482 /* Date and times */
@@ -699,10 +719,13 @@
719 }else if( rid==0 ){
720 fossil_error(iErrPriority, "cannot resolve name: %s", zName);
721 return 1;
722 }else{
723 blob_reset(pName);
724 if( RID_CKOUT==rid ) {
725 rid = g.localOpen;
726 }
727 db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
728 return 0;
729 }
730 }
731
732
+4 -4
--- src/statrep.c
+++ src/statrep.c
@@ -290,12 +290,12 @@
290290
nEventsPerYear += nCount;
291291
@<tr class='row%d(rowClass)'>
292292
@ <td>
293293
if(includeMonth){
294294
cgi_printf("<a href='%R/timeline?"
295
- "ym=%t&n=%d&y=%s",
296
- zTimeframe, nCount,
295
+ "ym=%t&y=%s",
296
+ zTimeframe,
297297
statsReportTimelineYFlag );
298298
/* Reminder: n=nCount is not actually correct for bymonth unless
299299
that was the only user who caused events.
300300
*/
301301
if( zUserName ){
@@ -731,12 +731,12 @@
731731
? (int)(100 * nCount / nMaxEvents)
732732
: 0;
733733
if(!nSize) nSize = 1;
734734
total += nCount;
735735
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
736
- cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
737
- zYear, zWeek, nCount,
736
+ cgi_printf("<td><a href='%R/timeline?yw=%t%s&y=%s",
737
+ zYear, zWeek,
738738
statsReportTimelineYFlag);
739739
if( zUserName ){
740740
cgi_printf("&u=%t",zUserName);
741741
}
742742
cgi_printf("'>%s</a></td>",zWeek);
743743
--- src/statrep.c
+++ src/statrep.c
@@ -290,12 +290,12 @@
290 nEventsPerYear += nCount;
291 @<tr class='row%d(rowClass)'>
292 @ <td>
293 if(includeMonth){
294 cgi_printf("<a href='%R/timeline?"
295 "ym=%t&n=%d&y=%s",
296 zTimeframe, nCount,
297 statsReportTimelineYFlag );
298 /* Reminder: n=nCount is not actually correct for bymonth unless
299 that was the only user who caused events.
300 */
301 if( zUserName ){
@@ -731,12 +731,12 @@
731 ? (int)(100 * nCount / nMaxEvents)
732 : 0;
733 if(!nSize) nSize = 1;
734 total += nCount;
735 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
736 cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
737 zYear, zWeek, nCount,
738 statsReportTimelineYFlag);
739 if( zUserName ){
740 cgi_printf("&u=%t",zUserName);
741 }
742 cgi_printf("'>%s</a></td>",zWeek);
743
--- src/statrep.c
+++ src/statrep.c
@@ -290,12 +290,12 @@
290 nEventsPerYear += nCount;
291 @<tr class='row%d(rowClass)'>
292 @ <td>
293 if(includeMonth){
294 cgi_printf("<a href='%R/timeline?"
295 "ym=%t&y=%s",
296 zTimeframe,
297 statsReportTimelineYFlag );
298 /* Reminder: n=nCount is not actually correct for bymonth unless
299 that was the only user who caused events.
300 */
301 if( zUserName ){
@@ -731,12 +731,12 @@
731 ? (int)(100 * nCount / nMaxEvents)
732 : 0;
733 if(!nSize) nSize = 1;
734 total += nCount;
735 cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
736 cgi_printf("<td><a href='%R/timeline?yw=%t%s&y=%s",
737 zYear, zWeek,
738 statsReportTimelineYFlag);
739 if( zUserName ){
740 cgi_printf("&u=%t",zUserName);
741 }
742 cgi_printf("'>%s</a></td>",zWeek);
743
+232 -93
--- src/timeline.c
+++ src/timeline.c
@@ -1312,39 +1312,78 @@
13121312
** Add missing "-" characters into a date/time. Examples:
13131313
**
13141314
** 20190419 => 2019-04-19
13151315
** 201904 => 2019-04
13161316
*/
1317
-const char *timeline_expand_datetime(const char *zIn){
1318
- static char zEDate[20];
1319
- static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
1317
+const char *timeline_expand_datetime(const char *zIn, int *pbZulu){
1318
+ static char zEDate[16];
13201319
int n = (int)strlen(zIn);
13211320
int i, j;
13221321
1323
- /* Only three forms allowed:
1322
+ /* These forms are recognized:
1323
+ **
13241324
** (1) YYYYMMDD
13251325
** (2) YYYYMM
13261326
** (3) YYYYWW
13271327
*/
1328
+ if( n && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
1329
+ n--;
1330
+ if( pbZulu ) *pbZulu = 1;
1331
+ }else{
1332
+ if( pbZulu ) *pbZulu = 0;
1333
+ }
13281334
if( n!=8 && n!=6 ) return zIn;
13291335
13301336
/* Every character must be a digit */
1331
- for(i=0; fossil_isdigit(zIn[i]); i++){}
1337
+ for(i=0; i<n && fossil_isdigit(zIn[i]); i++){}
13321338
if( i!=n ) return zIn;
13331339
13341340
/* Expand the date */
1335
- for(i=j=0; zIn[i]; i++){
1336
- if( i>=4 && (i%2)==0 ){
1337
- zEDate[j++] = aPunct[i/2];
1338
- }
1341
+ for(i=j=0; i<n; i++){
1342
+ if( j==4 || j==7 ) zEDate[j++] = '-';
13391343
zEDate[j++] = zIn[i];
13401344
}
13411345
zEDate[j] = 0;
13421346
13431347
/* It looks like this may be a date. Return it with punctuation added. */
13441348
return zEDate;
13451349
}
1350
+
1351
+/*
1352
+** Check to see if the argument is a date-span for the ymd= query
1353
+** parameter. A valid date-span is of the form:
1354
+**
1355
+** 0123456789 123456 <-- index
1356
+** YYYYMMDD-YYYYMMDD
1357
+**
1358
+** with an optional "Z" timeline modifier at the end. Return true if
1359
+** the input is a valid date space and false if not.
1360
+*/
1361
+static int timeline_is_datespan(const char *zDay){
1362
+ size_t n = strlen(zDay);
1363
+ int i, d, m;
1364
+
1365
+ if( n<17 || n>18 ) return 0;
1366
+ if( n==18 ){
1367
+ if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
1368
+ n--;
1369
+ }
1370
+ if( zDay[8]!='-' ) return 0;
1371
+ for(i=0; i<17 && (fossil_isdigit(zDay[i]) || i==8); i++){}
1372
+ if( i!=17 ) return 0;
1373
+ i = atoi(zDay);
1374
+ d = i%100;
1375
+ if( d<1 || d>31 ) return 0;
1376
+ m = (i/100)%100;
1377
+ if( m<1 || m>12 ) return 0;
1378
+ i = atoi(zDay+9);
1379
+ d = i%100;
1380
+ if( d<1 || d>31 ) return 0;
1381
+ m = (i/100)%100;
1382
+ if( m<1 || m>12 ) return 0;
1383
+ return 1;
1384
+}
13461385
13471386
/*
13481387
** Find the first check-in encountered with a particular tag
13491388
** when moving either forwards are backwards in time from a
13501389
** particular starting point (iFrom). Return the rid of that
@@ -1594,15 +1633,17 @@
15941633
** deltabg Background color red for delta manifests or green
15951634
** for baseline manifests
15961635
** namechng Show only check-ins that have filename changes
15971636
** forks Show only forks and their children
15981637
** cherrypicks Show all cherrypicks
1599
-** ym=YYYY-MM Show only events for the given year/month
1600
-** yw=YYYY-WW Show only events for the given week of the given year
1601
-** yw=YYYY-MM-DD Show events for the week that includes the given day
1602
-** ymd=YYYY-MM-DD Show only events on the given day. The use "ymd=now"
1603
-** to see all changes for the current week.
1638
+** ym=YYYYMM Show only events for the given year/month
1639
+** yw=YYYYWW Show only events for the given week of the given year
1640
+** yw=YYYYMMDD Show events for the week that includes the given day
1641
+** ymd=YYYYMMDD Show only events on the given day. The use "ymd=now"
1642
+** to see all changes for the current week. Add "z" at end
1643
+** to divide days at UTC instead of localtime days.
1644
+** Use ymd=YYYYMMDD-YYYYMMDD (with optional "z") for a range.
16041645
** year=YYYY Show only events on the given year. The use "year=0"
16051646
** to see all changes for the current year.
16061647
** days=N Show events over the previous N days
16071648
** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
16081649
** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
@@ -2372,46 +2413,55 @@
23722413
if( bisectLocal || zBisect!=0 ){
23732414
blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
23742415
}
23752416
if( zYearMonth ){
23762417
char *zNext;
2377
- zYearMonth = timeline_expand_datetime(zYearMonth);
2378
- if( strlen(zYearMonth)>7 ){
2379
- zYearMonth = mprintf("%.7s", zYearMonth);
2380
- }
2418
+ int bZulu = 0;
2419
+ const char *zTZMod;
2420
+ zYearMonth = timeline_expand_datetime(zYearMonth, &bZulu);
2421
+ zYearMonth = mprintf("%.7s", zYearMonth);
23812422
if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
23822423
zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
23832424
}
2384
- zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');",
2385
- zYearMonth);
2386
- if( db_int(0,
2387
- "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2388
- " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)",
2389
- zNext, blob_sql_text(&cond))
2390
- ){
2391
- zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2392
- zNewerButtonLabel = "Following month";
2393
- }
2394
- fossil_free(zNext);
2395
- zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');",
2396
- zYearMonth);
2397
- if( db_int(0,
2398
- "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2399
- " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)",
2400
- zYearMonth, blob_sql_text(&cond))
2401
- ){
2402
- zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2403
- zOlderButtonLabel = "Previous month";
2404
- }
2405
- fossil_free(zNext);
2406
- blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
2407
- zYearMonth);
2408
- nEntry = -1;
2425
+ zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2426
+ if( db_int(0,
2427
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2428
+ " WHERE blob.rid=event.objid"
2429
+ " AND mtime>=julianday('%q-01',%Q)%s)",
2430
+ zYearMonth, zTZMod, blob_sql_text(&cond))
2431
+ ){
2432
+ zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','+1 month');",
2433
+ &"Z"[!bZulu], zYearMonth);
2434
+ zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2435
+ zNewerButtonLabel = "Following month";
2436
+ fossil_free(zNext);
2437
+ }
2438
+ if( db_int(0,
2439
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2440
+ " WHERE blob.rid=event.objid"
2441
+ " AND mtime<julianday('%q-01',%Q)%s)",
2442
+ zYearMonth, zTZMod, blob_sql_text(&cond))
2443
+ ){
2444
+ zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','-1 month');",
2445
+ &"Z"[!bZulu], zYearMonth);
2446
+ zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2447
+ zOlderButtonLabel = "Previous month";
2448
+ fossil_free(zNext);
2449
+ }
2450
+ blob_append_sql(&cond,
2451
+ " AND event.mtime>=julianday('%q-01',%Q)"
2452
+ " AND event.mtime<julianday('%q-01',%Q,'+1 month')\n",
2453
+ zYearMonth, zTZMod, zYearMonth, zTZMod);
2454
+ nEntry = -1;
2455
+ /* Adjust the zYearMonth for the title */
2456
+ zYearMonth = mprintf("%z-01%s", zYearMonth, &"Z"[!bZulu]);
24092457
}
24102458
else if( zYearWeek ){
24112459
char *z, *zNext;
2412
- zYearWeek = timeline_expand_datetime(zYearWeek);
2460
+ int bZulu = 0;
2461
+ const char *zTZMod;
2462
+ zYearWeek = timeline_expand_datetime(zYearWeek, &bZulu);
24132463
z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
24142464
if( z && z[0] ){
24152465
zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
24162466
zYearWeek);
24172467
zYearWeek = z;
@@ -2428,64 +2478,152 @@
24282478
"SELECT date('now','-6 days','weekday 1');");
24292479
zYearWeek = db_text(0,
24302480
"SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
24312481
}
24322482
}
2433
- zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart);
2434
- if( db_int(0,
2435
- "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2436
- " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2437
- zNext, blob_sql_text(&cond))
2438
- ){
2439
- zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2440
- zNewerButtonLabel = "Following week";
2441
- }
2442
- fossil_free(zNext);
2443
- zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart);
2444
- if( db_int(0,
2445
- "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2446
- " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2447
- zYearWeekStart, blob_sql_text(&cond))
2448
- ){
2449
- zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2450
- zOlderButtonLabel = "Previous week";
2451
- }
2452
- fossil_free(zNext);
2453
- blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
2454
- zYearWeek);
2455
- nEntry = -1;
2483
+ zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2484
+ if( db_int(0,
2485
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2486
+ " WHERE blob.rid=event.objid"
2487
+ " AND mtime>=julianday(%Q,%Q)%s)",
2488
+ zYearWeekStart, zTZMod, blob_sql_text(&cond))
2489
+ ){
2490
+ zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'+7 day');",
2491
+ &"Z"[!bZulu], zYearWeekStart);
2492
+ zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2493
+ zNewerButtonLabel = "Following week";
2494
+ fossil_free(zNext);
2495
+ }
2496
+ if( db_int(0,
2497
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2498
+ " WHERE blob.rid=event.objid"
2499
+ " AND mtime<julianday(%Q,%Q)%s)",
2500
+ zYearWeekStart, zTZMod, blob_sql_text(&cond))
2501
+ ){
2502
+ zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'-7 days');",
2503
+ &"Z"[!bZulu], zYearWeekStart);
2504
+ zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2505
+ zOlderButtonLabel = "Previous week";
2506
+ fossil_free(zNext);
2507
+ }
2508
+ blob_append_sql(&cond,
2509
+ " AND event.mtime>=julianday(%Q,%Q)"
2510
+ " AND event.mtime<julianday(%Q,%Q,'+7 days')\n",
2511
+ zYearWeekStart, zTZMod, zYearWeekStart, zTZMod);
2512
+ nEntry = -1;
2513
+ if( fossil_ui_localtime() && bZulu ){
2514
+ zYearWeekStart = mprintf("%zZ", zYearWeekStart);
2515
+ }
2516
+ }
2517
+ else if( zDay && timeline_is_datespan(zDay) ){
2518
+ char *zNext;
2519
+ char *zStart, *zEnd;
2520
+ int nDay;
2521
+ int bZulu = 0;
2522
+ const char *zTZMod;
2523
+ zEnd = db_text(0, "SELECT date(%Q)",
2524
+ timeline_expand_datetime(zDay+9, &bZulu));
2525
+ zStart = db_text(0, "SELECT date('%.4q-%.2q-%.2q')",
2526
+ zDay, zDay+4, zDay+6);
2527
+ nDay = db_int(0, "SELECT julianday(%Q)-julianday(%Q)", zEnd, zStart);
2528
+ if( nDay==0 ){
2529
+ zDay = &zDay[9];
2530
+ goto single_ymd;
2531
+ }
2532
+ if( nDay<0 ){
2533
+ char *zTemp = zEnd;
2534
+ zEnd = zStart;
2535
+ zStart = zTemp;
2536
+ nDay = 1 - nDay;
2537
+ }else{
2538
+ nDay += 1;
2539
+ }
2540
+ zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2541
+ if( nDay>0 && db_int(0,
2542
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2543
+ " WHERE blob.rid=event.objid"
2544
+ " AND mtime>=julianday(%Q,'1 day',%Q)%s)",
2545
+ zEnd, zTZMod, blob_sql_text(&cond))
2546
+ ){
2547
+ zNext = db_text(0,
2548
+ "SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
2549
+ "strftime('%%Y%%m%%d%q',%Q,'%d day');",
2550
+ zStart, nDay, &"Z"[!bZulu], zEnd, nDay);
2551
+ zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2552
+ zNewerButtonLabel = mprintf("Following %d days", nDay);
2553
+ fossil_free(zNext);
2554
+ }
2555
+ if( nDay>1 && db_int(0,
2556
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2557
+ " WHERE blob.rid=event.objid"
2558
+ " AND mtime<julianday(%Q,'-1 day',%Q)%s)",
2559
+ zStart, zTZMod, blob_sql_text(&cond))
2560
+ ){
2561
+ zNext = db_text(0,
2562
+ "SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
2563
+ "strftime('%%Y%%m%%d%q',%Q,'%d day');",
2564
+ zStart, -nDay, &"Z"[!bZulu], zEnd, -nDay);
2565
+ zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2566
+ zOlderButtonLabel = mprintf("Previous %d days", nDay);
2567
+ fossil_free(zNext);
2568
+ }
2569
+ blob_append_sql(&cond,
2570
+ " AND event.mtime>=julianday(%Q,%Q)"
2571
+ " AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
2572
+ zStart, zTZMod, zEnd, zTZMod);
2573
+ nEntry = -1;
2574
+
2575
+ if( fossil_ui_localtime() && bZulu ){
2576
+ zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
2577
+ }else{
2578
+ zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
2579
+ }
24562580
}
24572581
else if( zDay ){
24582582
char *zNext;
2459
- zDay = timeline_expand_datetime(zDay);
2583
+ int bZulu = 0;
2584
+ const char *zTZMod;
2585
+ single_ymd:
2586
+ bZulu = 0;
2587
+ zDay = timeline_expand_datetime(zDay, &bZulu);
24602588
zDay = db_text(0, "SELECT date(%Q)", zDay);
24612589
if( zDay==0 || zDay[0]==0 ){
24622590
zDay = db_text(0, "SELECT date('now')");
24632591
}
2464
- zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay);
2465
- if( db_int(0,
2466
- "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2467
- " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2468
- zNext, blob_sql_text(&cond))
2469
- ){
2470
- zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2471
- zNewerButtonLabel = "Following day";
2472
- }
2473
- fossil_free(zNext);
2474
- zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay);
2475
- if( db_int(0,
2476
- "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2477
- " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2478
- zDay, blob_sql_text(&cond))
2479
- ){
2480
- zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2481
- zOlderButtonLabel = "Previous day";
2482
- }
2483
- fossil_free(zNext);
2484
- blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
2485
- zDay);
2486
- nEntry = -1;
2592
+ zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2593
+ if( db_int(0,
2594
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2595
+ " WHERE blob.rid=event.objid"
2596
+ " AND mtime>=julianday(%Q,'+1 day',%Q)%s)",
2597
+ zDay, zTZMod, blob_sql_text(&cond))
2598
+ ){
2599
+ zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'+1 day');",
2600
+ &"Z"[!bZulu], zDay);
2601
+ zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2602
+ zNewerButtonLabel = "Following day";
2603
+ fossil_free(zNext);
2604
+ }
2605
+ if( db_int(0,
2606
+ "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2607
+ " WHERE blob.rid=event.objid"
2608
+ " AND mtime<julianday(%Q,'-1 day',%Q)%s)",
2609
+ zDay, zTZMod, blob_sql_text(&cond))
2610
+ ){
2611
+ zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'-1 day');",
2612
+ &"Z"[!bZulu], zDay);
2613
+ zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2614
+ zOlderButtonLabel = "Previous day";
2615
+ fossil_free(zNext);
2616
+ }
2617
+ blob_append_sql(&cond,
2618
+ " AND event.mtime>=julianday(%Q,%Q)"
2619
+ " AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
2620
+ zDay, zTZMod, zDay, zTZMod);
2621
+ nEntry = -1;
2622
+ if( fossil_ui_localtime() && bZulu ){
2623
+ zDay = mprintf("%zZ", zDay); /* Add Z suffix to day for the title */
2624
+ }
24872625
}
24882626
else if( zNDays ){
24892627
nDays = atoi(zNDays);
24902628
if( nDays<1 ) nDays = 1;
24912629
blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
@@ -2728,11 +2866,11 @@
27282866
db_multi_exec("%s", blob_sql_text(&sql));
27292867
27302868
n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
27312869
zPlural = n==1 ? "" : "s";
27322870
if( zYearMonth ){
2733
- blob_appendf(&desc, "%d %s%s for the month beginning %h-01",
2871
+ blob_appendf(&desc, "%d %s%s for the month beginning %h",
27342872
n, zEType, zPlural, zYearMonth);
27352873
}else if( zYearWeek ){
27362874
blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
27372875
n, zEType, zPlural, zYearWeek, zYearWeekStart);
27382876
}else if( zDay ){
@@ -3617,10 +3755,11 @@
36173755
const char *zToday;
36183756
char *zStartOfProject;
36193757
int i;
36203758
Stmt q;
36213759
char *z;
3760
+ int bZulu = 0;
36223761
36233762
login_check_credentials();
36243763
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
36253764
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
36263765
return;
@@ -3627,11 +3766,11 @@
36273766
}
36283767
style_set_current_feature("timeline");
36293768
style_header("Today In History");
36303769
zToday = (char*)P("today");
36313770
if( zToday ){
3632
- zToday = timeline_expand_datetime(zToday);
3771
+ zToday = timeline_expand_datetime(zToday, &bZulu);
36333772
if( !fossil_isdate(zToday) ) zToday = 0;
36343773
}
36353774
if( zToday==0 ){
36363775
zToday = db_text(0, "SELECT date('now',toLocal())");
36373776
}
36383777
--- src/timeline.c
+++ src/timeline.c
@@ -1312,39 +1312,78 @@
1312 ** Add missing "-" characters into a date/time. Examples:
1313 **
1314 ** 20190419 => 2019-04-19
1315 ** 201904 => 2019-04
1316 */
1317 const char *timeline_expand_datetime(const char *zIn){
1318 static char zEDate[20];
1319 static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
1320 int n = (int)strlen(zIn);
1321 int i, j;
1322
1323 /* Only three forms allowed:
 
1324 ** (1) YYYYMMDD
1325 ** (2) YYYYMM
1326 ** (3) YYYYWW
1327 */
 
 
 
 
 
 
1328 if( n!=8 && n!=6 ) return zIn;
1329
1330 /* Every character must be a digit */
1331 for(i=0; fossil_isdigit(zIn[i]); i++){}
1332 if( i!=n ) return zIn;
1333
1334 /* Expand the date */
1335 for(i=j=0; zIn[i]; i++){
1336 if( i>=4 && (i%2)==0 ){
1337 zEDate[j++] = aPunct[i/2];
1338 }
1339 zEDate[j++] = zIn[i];
1340 }
1341 zEDate[j] = 0;
1342
1343 /* It looks like this may be a date. Return it with punctuation added. */
1344 return zEDate;
1345 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1346
1347 /*
1348 ** Find the first check-in encountered with a particular tag
1349 ** when moving either forwards are backwards in time from a
1350 ** particular starting point (iFrom). Return the rid of that
@@ -1594,15 +1633,17 @@
1594 ** deltabg Background color red for delta manifests or green
1595 ** for baseline manifests
1596 ** namechng Show only check-ins that have filename changes
1597 ** forks Show only forks and their children
1598 ** cherrypicks Show all cherrypicks
1599 ** ym=YYYY-MM Show only events for the given year/month
1600 ** yw=YYYY-WW Show only events for the given week of the given year
1601 ** yw=YYYY-MM-DD Show events for the week that includes the given day
1602 ** ymd=YYYY-MM-DD Show only events on the given day. The use "ymd=now"
1603 ** to see all changes for the current week.
 
 
1604 ** year=YYYY Show only events on the given year. The use "year=0"
1605 ** to see all changes for the current year.
1606 ** days=N Show events over the previous N days
1607 ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
1608 ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
@@ -2372,46 +2413,55 @@
2372 if( bisectLocal || zBisect!=0 ){
2373 blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
2374 }
2375 if( zYearMonth ){
2376 char *zNext;
2377 zYearMonth = timeline_expand_datetime(zYearMonth);
2378 if( strlen(zYearMonth)>7 ){
2379 zYearMonth = mprintf("%.7s", zYearMonth);
2380 }
2381 if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
2382 zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
2383 }
2384 zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');",
2385 zYearMonth);
2386 if( db_int(0,
2387 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2388 " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)",
2389 zNext, blob_sql_text(&cond))
2390 ){
2391 zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2392 zNewerButtonLabel = "Following month";
2393 }
2394 fossil_free(zNext);
2395 zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');",
2396 zYearMonth);
2397 if( db_int(0,
2398 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2399 " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)",
2400 zYearMonth, blob_sql_text(&cond))
2401 ){
2402 zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2403 zOlderButtonLabel = "Previous month";
2404 }
2405 fossil_free(zNext);
2406 blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
2407 zYearMonth);
2408 nEntry = -1;
 
 
 
 
 
 
 
2409 }
2410 else if( zYearWeek ){
2411 char *z, *zNext;
2412 zYearWeek = timeline_expand_datetime(zYearWeek);
 
 
2413 z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
2414 if( z && z[0] ){
2415 zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
2416 zYearWeek);
2417 zYearWeek = z;
@@ -2428,64 +2478,152 @@
2428 "SELECT date('now','-6 days','weekday 1');");
2429 zYearWeek = db_text(0,
2430 "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
2431 }
2432 }
2433 zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart);
2434 if( db_int(0,
2435 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2436 " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2437 zNext, blob_sql_text(&cond))
2438 ){
2439 zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2440 zNewerButtonLabel = "Following week";
2441 }
2442 fossil_free(zNext);
2443 zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart);
2444 if( db_int(0,
2445 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2446 " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2447 zYearWeekStart, blob_sql_text(&cond))
2448 ){
2449 zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2450 zOlderButtonLabel = "Previous week";
2451 }
2452 fossil_free(zNext);
2453 blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
2454 zYearWeek);
2455 nEntry = -1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2456 }
2457 else if( zDay ){
2458 char *zNext;
2459 zDay = timeline_expand_datetime(zDay);
 
 
 
 
2460 zDay = db_text(0, "SELECT date(%Q)", zDay);
2461 if( zDay==0 || zDay[0]==0 ){
2462 zDay = db_text(0, "SELECT date('now')");
2463 }
2464 zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay);
2465 if( db_int(0,
2466 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2467 " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",
2468 zNext, blob_sql_text(&cond))
2469 ){
2470 zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2471 zNewerButtonLabel = "Following day";
2472 }
2473 fossil_free(zNext);
2474 zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay);
2475 if( db_int(0,
2476 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2477 " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",
2478 zDay, blob_sql_text(&cond))
2479 ){
2480 zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2481 zOlderButtonLabel = "Previous day";
2482 }
2483 fossil_free(zNext);
2484 blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
2485 zDay);
2486 nEntry = -1;
 
 
 
 
 
 
 
 
 
 
2487 }
2488 else if( zNDays ){
2489 nDays = atoi(zNDays);
2490 if( nDays<1 ) nDays = 1;
2491 blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
@@ -2728,11 +2866,11 @@
2728 db_multi_exec("%s", blob_sql_text(&sql));
2729
2730 n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
2731 zPlural = n==1 ? "" : "s";
2732 if( zYearMonth ){
2733 blob_appendf(&desc, "%d %s%s for the month beginning %h-01",
2734 n, zEType, zPlural, zYearMonth);
2735 }else if( zYearWeek ){
2736 blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
2737 n, zEType, zPlural, zYearWeek, zYearWeekStart);
2738 }else if( zDay ){
@@ -3617,10 +3755,11 @@
3617 const char *zToday;
3618 char *zStartOfProject;
3619 int i;
3620 Stmt q;
3621 char *z;
 
3622
3623 login_check_credentials();
3624 if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
3625 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
3626 return;
@@ -3627,11 +3766,11 @@
3627 }
3628 style_set_current_feature("timeline");
3629 style_header("Today In History");
3630 zToday = (char*)P("today");
3631 if( zToday ){
3632 zToday = timeline_expand_datetime(zToday);
3633 if( !fossil_isdate(zToday) ) zToday = 0;
3634 }
3635 if( zToday==0 ){
3636 zToday = db_text(0, "SELECT date('now',toLocal())");
3637 }
3638
--- src/timeline.c
+++ src/timeline.c
@@ -1312,39 +1312,78 @@
1312 ** Add missing "-" characters into a date/time. Examples:
1313 **
1314 ** 20190419 => 2019-04-19
1315 ** 201904 => 2019-04
1316 */
1317 const char *timeline_expand_datetime(const char *zIn, int *pbZulu){
1318 static char zEDate[16];
 
1319 int n = (int)strlen(zIn);
1320 int i, j;
1321
1322 /* These forms are recognized:
1323 **
1324 ** (1) YYYYMMDD
1325 ** (2) YYYYMM
1326 ** (3) YYYYWW
1327 */
1328 if( n && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
1329 n--;
1330 if( pbZulu ) *pbZulu = 1;
1331 }else{
1332 if( pbZulu ) *pbZulu = 0;
1333 }
1334 if( n!=8 && n!=6 ) return zIn;
1335
1336 /* Every character must be a digit */
1337 for(i=0; i<n && fossil_isdigit(zIn[i]); i++){}
1338 if( i!=n ) return zIn;
1339
1340 /* Expand the date */
1341 for(i=j=0; i<n; i++){
1342 if( j==4 || j==7 ) zEDate[j++] = '-';
 
 
1343 zEDate[j++] = zIn[i];
1344 }
1345 zEDate[j] = 0;
1346
1347 /* It looks like this may be a date. Return it with punctuation added. */
1348 return zEDate;
1349 }
1350
1351 /*
1352 ** Check to see if the argument is a date-span for the ymd= query
1353 ** parameter. A valid date-span is of the form:
1354 **
1355 ** 0123456789 123456 <-- index
1356 ** YYYYMMDD-YYYYMMDD
1357 **
1358 ** with an optional "Z" timeline modifier at the end. Return true if
1359 ** the input is a valid date space and false if not.
1360 */
1361 static int timeline_is_datespan(const char *zDay){
1362 size_t n = strlen(zDay);
1363 int i, d, m;
1364
1365 if( n<17 || n>18 ) return 0;
1366 if( n==18 ){
1367 if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
1368 n--;
1369 }
1370 if( zDay[8]!='-' ) return 0;
1371 for(i=0; i<17 && (fossil_isdigit(zDay[i]) || i==8); i++){}
1372 if( i!=17 ) return 0;
1373 i = atoi(zDay);
1374 d = i%100;
1375 if( d<1 || d>31 ) return 0;
1376 m = (i/100)%100;
1377 if( m<1 || m>12 ) return 0;
1378 i = atoi(zDay+9);
1379 d = i%100;
1380 if( d<1 || d>31 ) return 0;
1381 m = (i/100)%100;
1382 if( m<1 || m>12 ) return 0;
1383 return 1;
1384 }
1385
1386 /*
1387 ** Find the first check-in encountered with a particular tag
1388 ** when moving either forwards are backwards in time from a
1389 ** particular starting point (iFrom). Return the rid of that
@@ -1594,15 +1633,17 @@
1633 ** deltabg Background color red for delta manifests or green
1634 ** for baseline manifests
1635 ** namechng Show only check-ins that have filename changes
1636 ** forks Show only forks and their children
1637 ** cherrypicks Show all cherrypicks
1638 ** ym=YYYYMM Show only events for the given year/month
1639 ** yw=YYYYWW Show only events for the given week of the given year
1640 ** yw=YYYYMMDD Show events for the week that includes the given day
1641 ** ymd=YYYYMMDD Show only events on the given day. The use "ymd=now"
1642 ** to see all changes for the current week. Add "z" at end
1643 ** to divide days at UTC instead of localtime days.
1644 ** Use ymd=YYYYMMDD-YYYYMMDD (with optional "z") for a range.
1645 ** year=YYYY Show only events on the given year. The use "year=0"
1646 ** to see all changes for the current year.
1647 ** days=N Show events over the previous N days
1648 ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
1649 ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
@@ -2372,46 +2413,55 @@
2413 if( bisectLocal || zBisect!=0 ){
2414 blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
2415 }
2416 if( zYearMonth ){
2417 char *zNext;
2418 int bZulu = 0;
2419 const char *zTZMod;
2420 zYearMonth = timeline_expand_datetime(zYearMonth, &bZulu);
2421 zYearMonth = mprintf("%.7s", zYearMonth);
2422 if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
2423 zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
2424 }
2425 zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2426 if( db_int(0,
2427 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2428 " WHERE blob.rid=event.objid"
2429 " AND mtime>=julianday('%q-01',%Q)%s)",
2430 zYearMonth, zTZMod, blob_sql_text(&cond))
2431 ){
2432 zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','+1 month');",
2433 &"Z"[!bZulu], zYearMonth);
2434 zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2435 zNewerButtonLabel = "Following month";
2436 fossil_free(zNext);
2437 }
2438 if( db_int(0,
2439 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2440 " WHERE blob.rid=event.objid"
2441 " AND mtime<julianday('%q-01',%Q)%s)",
2442 zYearMonth, zTZMod, blob_sql_text(&cond))
2443 ){
2444 zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','-1 month');",
2445 &"Z"[!bZulu], zYearMonth);
2446 zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
2447 zOlderButtonLabel = "Previous month";
2448 fossil_free(zNext);
2449 }
2450 blob_append_sql(&cond,
2451 " AND event.mtime>=julianday('%q-01',%Q)"
2452 " AND event.mtime<julianday('%q-01',%Q,'+1 month')\n",
2453 zYearMonth, zTZMod, zYearMonth, zTZMod);
2454 nEntry = -1;
2455 /* Adjust the zYearMonth for the title */
2456 zYearMonth = mprintf("%z-01%s", zYearMonth, &"Z"[!bZulu]);
2457 }
2458 else if( zYearWeek ){
2459 char *z, *zNext;
2460 int bZulu = 0;
2461 const char *zTZMod;
2462 zYearWeek = timeline_expand_datetime(zYearWeek, &bZulu);
2463 z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
2464 if( z && z[0] ){
2465 zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
2466 zYearWeek);
2467 zYearWeek = z;
@@ -2428,64 +2478,152 @@
2478 "SELECT date('now','-6 days','weekday 1');");
2479 zYearWeek = db_text(0,
2480 "SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
2481 }
2482 }
2483 zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2484 if( db_int(0,
2485 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2486 " WHERE blob.rid=event.objid"
2487 " AND mtime>=julianday(%Q,%Q)%s)",
2488 zYearWeekStart, zTZMod, blob_sql_text(&cond))
2489 ){
2490 zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'+7 day');",
2491 &"Z"[!bZulu], zYearWeekStart);
2492 zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2493 zNewerButtonLabel = "Following week";
2494 fossil_free(zNext);
2495 }
2496 if( db_int(0,
2497 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2498 " WHERE blob.rid=event.objid"
2499 " AND mtime<julianday(%Q,%Q)%s)",
2500 zYearWeekStart, zTZMod, blob_sql_text(&cond))
2501 ){
2502 zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'-7 days');",
2503 &"Z"[!bZulu], zYearWeekStart);
2504 zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
2505 zOlderButtonLabel = "Previous week";
2506 fossil_free(zNext);
2507 }
2508 blob_append_sql(&cond,
2509 " AND event.mtime>=julianday(%Q,%Q)"
2510 " AND event.mtime<julianday(%Q,%Q,'+7 days')\n",
2511 zYearWeekStart, zTZMod, zYearWeekStart, zTZMod);
2512 nEntry = -1;
2513 if( fossil_ui_localtime() && bZulu ){
2514 zYearWeekStart = mprintf("%zZ", zYearWeekStart);
2515 }
2516 }
2517 else if( zDay && timeline_is_datespan(zDay) ){
2518 char *zNext;
2519 char *zStart, *zEnd;
2520 int nDay;
2521 int bZulu = 0;
2522 const char *zTZMod;
2523 zEnd = db_text(0, "SELECT date(%Q)",
2524 timeline_expand_datetime(zDay+9, &bZulu));
2525 zStart = db_text(0, "SELECT date('%.4q-%.2q-%.2q')",
2526 zDay, zDay+4, zDay+6);
2527 nDay = db_int(0, "SELECT julianday(%Q)-julianday(%Q)", zEnd, zStart);
2528 if( nDay==0 ){
2529 zDay = &zDay[9];
2530 goto single_ymd;
2531 }
2532 if( nDay<0 ){
2533 char *zTemp = zEnd;
2534 zEnd = zStart;
2535 zStart = zTemp;
2536 nDay = 1 - nDay;
2537 }else{
2538 nDay += 1;
2539 }
2540 zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2541 if( nDay>0 && db_int(0,
2542 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2543 " WHERE blob.rid=event.objid"
2544 " AND mtime>=julianday(%Q,'1 day',%Q)%s)",
2545 zEnd, zTZMod, blob_sql_text(&cond))
2546 ){
2547 zNext = db_text(0,
2548 "SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
2549 "strftime('%%Y%%m%%d%q',%Q,'%d day');",
2550 zStart, nDay, &"Z"[!bZulu], zEnd, nDay);
2551 zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2552 zNewerButtonLabel = mprintf("Following %d days", nDay);
2553 fossil_free(zNext);
2554 }
2555 if( nDay>1 && db_int(0,
2556 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2557 " WHERE blob.rid=event.objid"
2558 " AND mtime<julianday(%Q,'-1 day',%Q)%s)",
2559 zStart, zTZMod, blob_sql_text(&cond))
2560 ){
2561 zNext = db_text(0,
2562 "SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
2563 "strftime('%%Y%%m%%d%q',%Q,'%d day');",
2564 zStart, -nDay, &"Z"[!bZulu], zEnd, -nDay);
2565 zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2566 zOlderButtonLabel = mprintf("Previous %d days", nDay);
2567 fossil_free(zNext);
2568 }
2569 blob_append_sql(&cond,
2570 " AND event.mtime>=julianday(%Q,%Q)"
2571 " AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
2572 zStart, zTZMod, zEnd, zTZMod);
2573 nEntry = -1;
2574
2575 if( fossil_ui_localtime() && bZulu ){
2576 zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
2577 }else{
2578 zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
2579 }
2580 }
2581 else if( zDay ){
2582 char *zNext;
2583 int bZulu = 0;
2584 const char *zTZMod;
2585 single_ymd:
2586 bZulu = 0;
2587 zDay = timeline_expand_datetime(zDay, &bZulu);
2588 zDay = db_text(0, "SELECT date(%Q)", zDay);
2589 if( zDay==0 || zDay[0]==0 ){
2590 zDay = db_text(0, "SELECT date('now')");
2591 }
2592 zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
2593 if( db_int(0,
2594 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2595 " WHERE blob.rid=event.objid"
2596 " AND mtime>=julianday(%Q,'+1 day',%Q)%s)",
2597 zDay, zTZMod, blob_sql_text(&cond))
2598 ){
2599 zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'+1 day');",
2600 &"Z"[!bZulu], zDay);
2601 zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2602 zNewerButtonLabel = "Following day";
2603 fossil_free(zNext);
2604 }
2605 if( db_int(0,
2606 "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
2607 " WHERE blob.rid=event.objid"
2608 " AND mtime<julianday(%Q,'-1 day',%Q)%s)",
2609 zDay, zTZMod, blob_sql_text(&cond))
2610 ){
2611 zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'-1 day');",
2612 &"Z"[!bZulu], zDay);
2613 zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
2614 zOlderButtonLabel = "Previous day";
2615 fossil_free(zNext);
2616 }
2617 blob_append_sql(&cond,
2618 " AND event.mtime>=julianday(%Q,%Q)"
2619 " AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
2620 zDay, zTZMod, zDay, zTZMod);
2621 nEntry = -1;
2622 if( fossil_ui_localtime() && bZulu ){
2623 zDay = mprintf("%zZ", zDay); /* Add Z suffix to day for the title */
2624 }
2625 }
2626 else if( zNDays ){
2627 nDays = atoi(zNDays);
2628 if( nDays<1 ) nDays = 1;
2629 blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
@@ -2728,11 +2866,11 @@
2866 db_multi_exec("%s", blob_sql_text(&sql));
2867
2868 n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
2869 zPlural = n==1 ? "" : "s";
2870 if( zYearMonth ){
2871 blob_appendf(&desc, "%d %s%s for the month beginning %h",
2872 n, zEType, zPlural, zYearMonth);
2873 }else if( zYearWeek ){
2874 blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
2875 n, zEType, zPlural, zYearWeek, zYearWeekStart);
2876 }else if( zDay ){
@@ -3617,10 +3755,11 @@
3755 const char *zToday;
3756 char *zStartOfProject;
3757 int i;
3758 Stmt q;
3759 char *z;
3760 int bZulu = 0;
3761
3762 login_check_credentials();
3763 if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
3764 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
3765 return;
@@ -3627,11 +3766,11 @@
3766 }
3767 style_set_current_feature("timeline");
3768 style_header("Today In History");
3769 zToday = (char*)P("today");
3770 if( zToday ){
3771 zToday = timeline_expand_datetime(zToday, &bZulu);
3772 if( !fossil_isdate(zToday) ) zToday = 0;
3773 }
3774 if( zToday==0 ){
3775 zToday = db_text(0, "SELECT date('now',toLocal())");
3776 }
3777

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button