about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-21 00:03:24 +0200
committersternenseemann <0rpkxez4ksa01gb3typccl0i@systemli.org>2020-08-21 00:06:24 +0200
commit875a5ce6222fa1027dc134b73c5cd6e8c06c1e4a (patch)
tree39bceb66616964571c0c23ac0acd3046195c8779
parentcada2ed89e5a0b68e2cfd002bf707107fb6d7111 (diff)
feat(timeutil): output proper RFC3339 format for atom
Previously, since we used strftime %z it'd output an offset as +HHMM
while RFC3339 requires +HH:MM. Currently we implement outputting this
manually which proved to be non-trivial because of the tzset() API.
This probably should be improved in the future since I suspect it
produces incorrect results in some edge cases.
-rw-r--r--Makefile2
-rw-r--r--TODO1
-rw-r--r--bitutil.c25
-rw-r--r--bitutil.h1
-rw-r--r--cgiutil.c26
-rw-r--r--timeutil.c71
-rw-r--r--timeutil.h2
7 files changed, 95 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index 27826c4..165bcf9 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ include config.mk
 
 ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
 
-sternenblog.cgi: xml.o entry.o index.o cgiutil.o timeutil.o $(TEMPLATE).o main.o
+sternenblog.cgi: xml.o entry.o index.o bitutil.o cgiutil.o timeutil.o $(TEMPLATE).o main.o
 	$(CC) $(CFLAGS) -o $@ $^
 
 main.o: main.c core.h timeutil.h config.h
diff --git a/TODO b/TODO
index d89a742..f4e9b92 100644
--- a/TODO
+++ b/TODO
@@ -3,6 +3,7 @@ atom feed | id:44aca4a04ff19c567e08968370cc535139807bbd
 add support for caching | id:472d68f1af5489020f4c5808e805822e3da77ffc
 rethink logging / debug output | id:5734e2717f28d80ac65d3f2123a0f46c01d4a0c1
 if possible determine BLOG_SERVER_URL automatically | id:5ad1fabab42a5776ab1446d09659c7a99a104245
+rework timezone offsets without tzset() | id:64d715e826ba9fdbcc72cb1b92a77f004642d2a3
 support markdown markup via lowdown | id:90c72d144f21d78d1b56790a927f6eea52df2a2c
 get rid of as many fixed size buffers as possible | id:9281f3a25aa08f6ba5d67c02e65cd0fb7f0a6b3c
 templating: give all functions context (index or not, SCRIPT_NAME, entry struct if applicable) | id:930c2e4f2c4c9ec3efef87b6ddd50a6ba6dd4903
diff --git a/bitutil.c b/bitutil.c
new file mode 100644
index 0000000..8b2807f
--- /dev/null
+++ b/bitutil.c
@@ -0,0 +1,25 @@
+char nibble_hex(short h) {
+    switch(h) {
+        case 0:
+        case 1:
+        case 2:
+        case 3:
+        case 4:
+        case 5:
+        case 6:
+        case 7:
+        case 8:
+        case 9:
+            return (h + 48);
+        case 10:
+        case 11:
+        case 12:
+        case 13:
+        case 14:
+        case 15:
+            return (h + 55);
+        default:
+            return 0;
+    }
+}
+
diff --git a/bitutil.h b/bitutil.h
new file mode 100644
index 0000000..57d15e6
--- /dev/null
+++ b/bitutil.h
@@ -0,0 +1 @@
+char nibble_hex(short h);
diff --git a/cgiutil.c b/cgiutil.c
index 606a10e..043dade 100644
--- a/cgiutil.c
+++ b/cgiutil.c
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include "bitutil.h"
 
 void send_header(char key[], char val[]) {
     fputs(key, stdout);
@@ -46,31 +47,6 @@ int http_errno(int err) {
     }
 }
 
-char nibble_hex(short h) {
-    switch(h) {
-        case 0:
-        case 1:
-        case 2:
-        case 3:
-        case 4:
-        case 5:
-        case 6:
-        case 7:
-        case 8:
-        case 9:
-            return (h + 48);
-        case 10:
-        case 11:
-        case 12:
-        case 13:
-        case 14:
-        case 15:
-            return (h + 55);
-        default:
-            return 0;
-    }
-}
-
 int urlencode_realloc(char **input, int size) {
     if(*input == NULL || size <= 0) {
         return -1;
diff --git a/timeutil.c b/timeutil.c
index 3d1cec3..c695d54 100644
--- a/timeutil.c
+++ b/timeutil.c
@@ -1,28 +1,89 @@
 #define _POSIX_C_SOURCE 1
+#define _XOPEN_SOURCE 1 // for timezone
 #include <time.h>
+#include <stdlib.h>
+#include <string.h>
 #include "timeutil.h"
+#include "bitutil.h"
 
-extern long timezone;
+#include <stdio.h>
 
-char *format_string(enum time_format t, long tz) {
+char *format_string(enum time_format t) {
     switch(t) {
         case RSS_TIME_FORMAT:
             return "%a, %d %b %Y %T %z";
+        // both remaining cases still need a UTC offset
+        // part at the end which is not supported by
+        // strftime(3), so we do this ourselves in
+        // flocaltime
         case HTML_TIME_FORMAT_READABLE:
-            return tz == 0 ? "%Y-%m-%d %TZ" : "%Y-%m-%d %T%z";
+            return "%Y-%m-%d %T";
         case ATOM_TIME_FORMAT:
         default:
-            return tz == 0 ? "%Y-%m-%dT%TZ" : "%Y-%m-%dT%T%z";
+            return "%Y-%m-%dT%T";
     }
 }
 
 size_t flocaltime(char *b, enum time_format type, size_t size, const time_t *time) {
     tzset();
     struct tm *local = localtime(time);
-    char *format = format_string(type, timezone);
+    char *format = format_string(type);
 
     size_t res = strftime(b, size, format, local);
 
+    if(type == ATOM_TIME_FORMAT || type == HTML_TIME_FORMAT_READABLE) {
+        // for these formats we need to append a RFC3339 UTC offset
+        // unfortunately it is *not* exactly provided by strftime,
+        // but in hindsight it might be better to do a little string
+        // manipulation than this madness, since the libc timezone
+        // API is horrible (at least POSIX / glibc)
+        size_t offset_size = 7;
+        char offset[offset_size];
+
+        if(timezone == 0) {
+            offset[0] = 'Z';
+            offset[1] = '\0';
+        } else {
+            // for some reason timezone is seconds *west* of UTC which
+            // is inverse to how UTC offsets are denoted
+            long real_offset = (-1) * timezone;
+
+            if(daylight) {
+                struct tm *local = localtime(time);
+
+                // TODO is this correct in all cases?
+                if(local->tm_isdst == 1) {
+                    real_offset += 3600;
+                }
+            }
+
+            char sign;
+            if(real_offset > 0) {
+                sign = '+';
+            } else {
+                sign = '-';
+            }
+
+            long abso = labs(real_offset);
+            long hour = abso / 3600;
+            long minute = (abso % 3600) / 60;
+
+            fprintf(stderr, "%ld:%ld  (%ld)\n", hour, minute, real_offset);
+
+            offset[0] = sign;
+            offset[1] = nibble_hex((short) hour / 10);
+            offset[2] = nibble_hex((short) hour % 10);
+            offset[3] = ':';
+            offset[4] = nibble_hex((short) minute / 10);
+            offset[5] = nibble_hex((short) minute % 10);
+            offset[6] = '\0';
+        }
+
+        if(res > 0 && res + offset_size <= size) {
+            memcpy(b + res, offset, offset_size);
+        }
+    }
+
     // prevent any buffer overflows
     b[size - 1] = '\0';
 
diff --git a/timeutil.h b/timeutil.h
index 4659513..8ee39cc 100644
--- a/timeutil.h
+++ b/timeutil.h
@@ -8,7 +8,5 @@ enum time_format {
 // max HTML/Atom: 24 + NUL byte
 // max RSS:       31 + NUL byte
 
-char *format_string(enum time_format t, long tz);
-
 size_t flocaltime(char *b, enum time_format type, size_t size, const time_t *time);