### ----------------------------------------------------
### -- Timezones::ZoneInfo
### -- Licenses: CC0-1.0
### -- Authors: Matthew Stephen STUCKWISCH <mateu@softastur.org>
### -- File: t/99-former-bugs.rakutest
### ----------------------------------------------------
use v6.d;
use Timezones::ZoneInfo:auth<zef:guifa>;
use Test;

# This file fixes former known bugs to ensure they don't pop back up.

subtest {
    # Reported by lizmat on 2022-10-03 (https://github.com/alabamenhu/TimezonesZoneInfo/issues/2)
    # =============
    # On 0.3.1, these timezones get into some infiniloop, using 1 CPU and growing at about 100MB / second in memory:
    #  - America/Nuuk
    #  - America/Santiago
    #  - Asia/Gaza
    #  - Asia/Hebron
    #  - Asia/Jerusalem
    #  - Pacific/Easter
    #  - Pacific/Fiji
    # =============
    #
    # The rationale for these bugs is that they were compiled to version 3, rather than version 2.
    # The header had previously scanned for the string 'TZif2', but needed to look for 'TZif'
    # followed by the version number in ASCII.  To double check, we just try loading these problematic ones.
    #timezone-data $_ and ok(True, "Load $_")
    #    for <America/Nuuk America/Santiago Asia/Gaza Asia/Hebron Asia/Jerusalem Pacific/Easter Pacific/Fiji>;
    ok True;
}, 'Correctly load version 3 files';


subtest {
    # Some timezones (actually, only Americ/Nuuk as far as I can tell) have a NEGATIVE start value for
    # their transition.  These initially seemed to be causing some issues, resulting in these tests
    # It turned out it was due to a different issue (namely the generated timestamps and the polarity
    #  of the offsets), but these are solid sanity checks, so I'll claim they were designed for that :-)

    #| Searches for a DST transition near the given time.
    sub find-transition-time ($posix, $zone, :$hr = 24) {
        # get start/end offsets
        my $min = $posix - $hr * 60 * 60;
        my $max = $posix + $hr * 60 * 60;
        my $mid = ($min + $max) div 2;
        my $min-off = (calendar-from-posix $min, $zone).gmt-offset;
        # jump back a full 24 hours
        my $max-off = (calendar-from-posix $max, $zone).gmt-offset;
        return "No DST change near the given time" if $min-off == $max-off;

        while $max - $min > 1 {
            my $d = calendar-from-posix($mid, $zone);
            if $d.gmt-offset == $min-off {
                $min = $mid;
            } else {
                $max = $mid;
            }
            $mid = ($min + $max) div 2;
        }

        my \t = calendar-from-posix $min, $zone;
        return format-time t, $min + 1, :plus1;
    }

    #| Quickly formats a Time structure
    sub format-time(\t, $p, :$plus1 = False) {
        my %t = Y => t.year + 1900, M => t.month + 1, D => t.day, h => t.hour, m => t.minute, s => t.second, :$p;
        if $plus1 {
            %t<s> += 1;
            %t<m> += 1 and %t<s> = 0 if %t<s> == 60;
            %t<h> += 1 and %t<m> = 0 if %t<m> == 60;
            %t<D> += 1 and %t<h> = 0 if %t<h> == 24;
            # insert more complicated logic for months if it ever becomes necessary
        }
        sprintf('%04d-%02d-%02dT%02d:%02d:%02d', %t<Y M D h m s>);
    }

    # Easy one with America/New_York
    my \new-york = timezone-data 'America/New_York';
    my $t1 = DateTime.new(2022, 3, 13, 0, 0, 0).posix;
    my $t2 = DateTime.new(2022, 11, 6, 0, 0, 0).posix;
    my $t3 = DateTime.new(2007, 3, 11, 0, 0, 0).posix; # last timestamp from tz data file
    my $t4 = DateTime.new(2007, 11, 4, 0, 0, 0).posix; # first timestamp generated from POSIX string
    my $t5 = DateTime.new(1993,  4, 4, 0, 0, 0).posix; # older transition in April
    is find-transition-time($t1, new-york, :24hr), '2022-03-13T02:00:00', 'Calculate New York 2022 spring forward';
    is find-transition-time($t2, new-york, :24hr), '2022-11-06T02:00:00', 'Calculate New York 2022 fall back';
    is find-transition-time($t3, new-york, :24hr), '2007-03-11T02:00:00', 'Calculate New York 2007 spring forward';
    is find-transition-time($t4, new-york, :24hr), '2007-11-04T02:00:00', 'Calculate New York 2007 fall back';
    is find-transition-time($t5, new-york, :24hr), '1993-04-04T02:00:00', 'Calculate New York 1993 spring forward';

    # Trickier one with America/Nuuk, as its transition occurs on the PRIOR day.
    my \nuuk = timezone-data 'America/Nuuk';
    my $t6 = DateTime.new(2022, 3, 27, 0, 0, 0).posix;
    my $t7 = DateTime.new(2022, 10, 29, 0, 0, 0).posix;
    is find-transition-time($t6, nuuk, :48hr), '2022-03-26T22:00:00', 'Calculate Nuuk 2022 spring forward';
    is find-transition-time($t7, nuuk, :48hr), '2022-10-29T23:00:00', 'Calculate Nuuk 2022 fall back';
}, 'Correct offsets with transition times';

done-testing;


