jstedfast / gmime

A C/C++ MIME creation and parser library with support for S/MIME, PGP, and Unix mbox spools.
GNU Lesser General Public License v2.1
111 stars 36 forks source link

g_mime_message_set_date() and g_mime_message_get_date() and g_mime_utils_header_decode_date() fail for TAI timezone #134

Closed riddicc closed 1 year ago

riddicc commented 1 year ago

Hi!

When I use this timezone file, and when I send an email @1658946095 (date -d@1658946095 = "Wed Jul 27 18:21:45 TAI 2022"), balsa says, that the timestamp is "2022-07-27T18:22:12TAI" (format: %FT%T%Z).

balsa uses g_mime_message_set_date/g_mime_message_get_date/g_mime_utils_header_decode_date().

It seems like these functions do not behave like date(1), because: They just ignore the fractions of a minute (% 60) in tm_gmtoff.

Can you somehow fix it? I do not know why these functions do that... I just wanted to avoid those annoying leap seconds (once I thought that the crystal in my self-tinkered USB sensor phalanx is broken, when i woke up in the morning...)...

PS: Here is my bug report in the balsa issues list...

PPS: exim and s-mailx already switch to UTC and use "-0000" as timezone offset (the minus denotes a deviating time zone in accordance with RFC5322, if i understood correctly).

PPPS: fetchmail did it, too...

Thx.

Bye

Arne

jstedfast commented 1 year ago

I would appreciate if you could test this for me. I wrote this patch from my Windows development machine which makes it non-trivial to test this.

riddicc commented 1 year ago

@jstedfast thanks for that patch... i have some comments: line 236: "%c%05d" seems to be too long... it should be "%c%04d"... right? line 232: and i do not see, how day,month,year,hour,min,sec change, when u only change tz_offset... maybe "g_date_time_add_seconds" could be used to subtract (or add) the old value of tz_offset? tz_offset = 0; sign = '-'; } return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d %c%05d",

i need to setup a build environment... is this the right way to go: git clone https://github.com/jstedfast/gmime.git ?

riddicc commented 1 year ago

@jstedfast I found another error in ur patch (%G_TIME_SPAN_SECOND would find sub second fractions...)... But it still does not work... It seems like g_date_time_new_from_unix_local has a problem with my system time, too (it seems like it acts like my box does not know of leap seconds...)...

--- ./gmime/gmime-utils.c,orig  2022-11-14 09:13:19.527160752 +0000
+++ ./gmime/gmime-utils.c   2022-11-14 10:23:08.191705502 +0000
@@ -217,8 +217,9 @@
    min = g_date_time_get_minute (date);
    sec = g_date_time_get_second (date);
    tz = g_date_time_get_utc_offset (date);
+printf("ARNE g_mime_utils_header_format_date: time:%jd min:%u sec:%u tz:%ld/%ld\n",time(0),min,sec,tz,G_TIME_SPAN_MINUTE);

-   if (tz % G_TIME_SPAN_SECOND == 0) {
+   if (tz % G_TIME_SPAN_MINUTE == 0) {
        if (tz < 0) {
            sign = '-';
            tz *= -1;
@@ -229,11 +230,21 @@
        tz_offset = 100 * (tz / G_TIME_SPAN_HOUR);
        tz_offset += (tz % G_TIME_SPAN_HOUR) / G_TIME_SPAN_MINUTE;
    } else {
+       GDateTime * utcdate = g_date_time_to_utc(date);
+       wday = g_date_time_get_day_of_week (utcdate);
+       year = g_date_time_get_year (utcdate);
+       month = g_date_time_get_month (utcdate);
+       day = g_date_time_get_day_of_month (utcdate);
+       hour = g_date_time_get_hour (utcdate);
+       min = g_date_time_get_minute (utcdate);
+       sec = g_date_time_get_second (utcdate);
+       g_date_time_unref(utcdate);
+printf("ARNE g_mime_utils_header_format_date: fracMIN min:%u sec:%u\n",min,sec);
        tz_offset = 0;
        sign = '-';
    }

-   return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d %c%05d",
+   return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d %c%04d",
                tm_days[wday % 7], day, tm_months[month - 1],
                year, hour, min, sec, sign, tz_offset);
 }

this is the output of my test:

> balsa
ARNE g_mime_utils_header_format_date: time:1668421497 min:25 sec:34 tz:37000000/60000000
ARNE g_mime_utils_header_format_date: fracMIN min:24 sec:57
>  date -d@1668421497
Mon Nov 14 10:25:07 TAI 2022
>  TZ=UTC date -d@1668421497
Mon Nov 14 10:24:30 UTC 2022
jstedfast commented 1 year ago

Thanks, I've made the corrections and folded them into my original patch (with a force push)..

jstedfast commented 1 year ago

Interesting... C#/.NET does not allow timezones with non-0 seconds:

var date = new DateTimeOffset (2022, 11, 14, 10, 25, 07, new TimeSpan (0, 0, 37));
var timestamp = DateUtils.FormatDate (date);

Assert.AreEqual ("Mon, 14 Nov 2022 10:24:30 -0000", timestamp);

Result:

Message: 
System.ArgumentException : Offset must be specified in whole minutes. (Parameter 'offset')

  Stack Trace: 
DateTimeOffset.ValidateOffset(TimeSpan offset)
DateTimeOffset.ctor(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second, TimeSpan offset)
DateParserTests.TestFormatDateWithNonStandardTimezone() line 164
riddicc commented 1 year ago

@jstedfast how do g_date_time_get_second and g_date_time_new_from_unix_local work? I mean: Do they use the system routines? or do they try to circumvent the zoneinfo files?

jstedfast commented 1 year ago

You'd have to ask the GLib developers. I'm not sure how the internals of those functions work.

riddicc commented 1 year ago

@jstedfast Uhm... Glib intentionally circumvents the system time routines and ignores POSIX...

can gmime take care of this, since it is talking to POSIX compliant systems?

I would recommend to use gmtime(3) and localtime(3)...

jstedfast commented 1 year ago

Are things still not working? I don't understand why I need to bypass Glib's time routines now.

riddicc commented 1 year ago

@jstedfast it seems like GLib still uses the original meaning of "seconds since 1970"... GLib acts like there are no leap seconds...

So gmime would have to use functions like strptime(3) and strftime(3) to be POSIX and RFC5322 compliant, i guess...

But maybe GLib changes its mind... i cant say that... I am ok with my balsa patch...

I just thought, that gmime or GLib might want to be leap seconds aware... 🫣

riddicc commented 1 year ago

it seems like GLib wants to circumvent the system routines with their own custom routines... i wonder if javascript also uses GLib, because: It has at least a similar problem... πŸ˜€

what about a RFC5322-compliance check, when gmime tries to make a human-readable timestamp?