"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const node_crypto_1 = require("node:crypto");
const DateTime_1 = require("./DateTime");
const ExifDateTime_1 = require("./ExifDateTime");
const Object_1 = require("./Object");
const _chai_spec_1 = require("./_chai.spec");
describe("ExifDateTime", () => {
    for (const ea of [
        {
            desc: "no zone suffix",
            exif: "2016:09:12 07:28:50.768120",
            exifExp: "2016:09:12 07:28:50.768",
            iso: "2016-09-12T07:28:50.768",
            d: {
                hasZone: false,
                zoneName: undefined,
                year: 2016,
                month: 9,
                day: 12,
                hour: 7,
                minute: 28,
                second: 50,
                millisecond: 768,
            },
        },
        {
            desc: "with zulu suffix",
            exif: "1999:01:02 03:04:05.67890Z",
            exifExp: "1999:01:02 03:04:05.678+00:00",
            iso: "1999-01-02T03:04:05.678Z",
            d: {
                hasZone: true,
                zoneName: "UTC",
                year: 1999,
                month: 1,
                day: 2,
                hour: 3,
                minute: 4,
                second: 5,
                millisecond: 678,
            },
        },
        {
            desc: "with timezone offset",
            exif: "2002:10:11 13:14:15.789-03:00",
            iso: "2002-10-11 13:14:15.789-03:00", // < no T!
            isoExp: "2002-10-11T13:14:15.789-03:00",
            d: {
                hasZone: true,
                zoneName: "UTC-3",
                year: 2002,
                month: 10,
                day: 11,
                hour: 13,
                minute: 14,
                second: 15,
                millisecond: 789,
            },
        },
        {
            desc: "with no tzoffset and UTC default",
            exif: "2011:01:23 18:19:20Z",
            iso: "2011-01-23T18:19:20Z",
            exifExp: "2011:01:23 18:19:20+00:00",
            d: {
                hasZone: true,
                zoneName: "UTC",
                year: 2011,
                month: 1,
                day: 23,
                hour: 18,
                minute: 19,
                second: 20,
                millisecond: undefined,
            },
        },
        {
            desc: "EXIF with ISO formatting and no tzoffset",
            exif: "2016:08:12 07:28:50.768120",
            exifExp: "2016:08:12 07:28:50.768",
            iso: "2016-08-12T07:28:50.768",
            d: {
                hasZone: false,
                zoneName: undefined,
                year: 2016,
                month: 8,
                day: 12,
                hour: 7,
                minute: 28,
                second: 50,
                millisecond: 768,
            },
        },
        {
            desc: "with tzoffset and spurious timezone",
            exif: "2015:07:17 08:46:27.123-09:00 DST",
            exifExp: "2015:07:17 08:46:27.123-09:00",
            iso: "2015-07-17T08:46:27.123-09:00",
            isoExp: "2015-07-17T08:46:27.123-09:00",
            d: {
                hasZone: true,
                zoneName: "UTC-9",
                year: 2015,
                month: 7,
                day: 17,
                hour: 8,
                minute: 46,
                second: 27,
                millisecond: 123,
            },
        },
        {
            desc: "with tzoffset, no millis, and spurious timezone",
            exif: "2014:07:17 08:46:27-07:00 DST",
            exifExp: "2014:07:17 08:46:27-07:00",
            iso: "2014-07-17T08:46:27-07:00",
            isoExp: "2014-07-17T08:46:27-07:00",
            d: {
                hasZone: true,
                zoneName: "UTC-7",
                year: 2014,
                month: 7,
                day: 17,
                hour: 8,
                minute: 46,
                second: 27,
                millisecond: undefined,
            },
        },
        {
            desc: "with tzoffset, no millis or seconds",
            exif: "2014:07:17 08:46-07:00",
            exifExp: "2014:07:17 08:46:00-07:00",
            iso: "2014-07-17T08:46-07:00",
            isoExp: "2014-07-17T08:46:00-07:00",
            d: {
                hasZone: true,
                zoneName: "UTC-7",
                year: 2014,
                month: 7,
                day: 17,
                hour: 8,
                minute: 46,
                second: 0,
                millisecond: undefined,
            },
        },
        // https://github.com/photostructure/exiftool-vendored.js/issues/318
        {
            desc: "with colon-less tzoffset (ISO 8601 basic format)",
            exif: "2025:04:27 19:47:08-0300",
            exifExp: "2025:04:27 19:47:08-03:00",
            iso: "2025-04-27T19:47:08-0300",
            isoExp: "2025-04-27T19:47:08-03:00",
            d: {
                hasZone: true,
                zoneName: "UTC-3",
                year: 2025,
                month: 4,
                day: 27,
                hour: 19,
                minute: 47,
                second: 8,
                millisecond: undefined,
            },
        },
    ]) {
        describe(ea.desc + " (iso: " + ea.iso + ")", () => {
            const fromExif = ExifDateTime_1.ExifDateTime.fromEXIF(ea.exif);
            const expected = {
                ...ea.d,
                inferredZone: false, // < because the .fromEXIF call doesn't have a default!
            };
            it("parses from EXIF", () => {
                (0, _chai_spec_1.expect)(fromExif).to.containSubset(expected);
            });
            it("parses from EXIF with zone default", () => {
                const edt = ExifDateTime_1.ExifDateTime.fromEXIF(ea.exif, "UTC+1");
                (0, _chai_spec_1.expect)(edt).to.haveOwnProperty("inferredZone", !expected.hasZone);
                (0, _chai_spec_1.expect)(edt?.zoneName).to.eql(expected.zoneName ?? "UTC+1");
            });
            const fromISO = ExifDateTime_1.ExifDateTime.fromISO(ea.iso);
            it("parses from ISO", () => {
                (0, _chai_spec_1.expect)(fromISO).to.containSubset(expected);
            });
            it("parses from ISO with zone default", () => {
                const edt = ExifDateTime_1.ExifDateTime.fromISO(ea.iso, "UTC-5");
                (0, _chai_spec_1.expect)(edt).to.haveOwnProperty("inferredZone", !expected.hasZone);
                (0, _chai_spec_1.expect)(edt?.zoneName).to.eql(expected.zoneName ?? "UTC-5");
            });
            const jsDate = new Date(ea.iso);
            it("renders correct epoch millis", () => {
                const exp = jsDate.getTime();
                (0, _chai_spec_1.expect)(fromExif?.toMillis()).to.eql(exp);
                (0, _chai_spec_1.expect)(fromISO?.toMillis()).to.eql(exp);
            });
            it("renders correct JS Date", () => {
                (0, _chai_spec_1.expect)(fromExif?.toDate()).to.eql(jsDate);
                (0, _chai_spec_1.expect)(fromISO?.toDate()).to.eql(jsDate);
            });
            it("renders correct ISO string", () => {
                const exp = ea.isoExp ?? ea.iso;
                (0, _chai_spec_1.expect)(fromExif?.toISOString()).to.eql(exp);
                (0, _chai_spec_1.expect)(fromISO?.toISOString()).to.eql(exp);
            });
            it("renders correct EXIF string", () => {
                const exp = ea.exifExp ?? ea.exif;
                (0, _chai_spec_1.expect)(fromExif?.toExifString()).to.eql(exp);
                (0, _chai_spec_1.expect)(fromISO?.toExifString()).to.eql(exp);
            });
        });
    }
    describe("parsing empty/invalid input", () => {
        const examples = [
            "",
            "     ",
            "0000",
            "1958",
            "2010_08",
            "01:08:22",
            "0000:00:00 00:00:00",
            "0000:00:00",
            "0001:01:01 00:00:00.00", // < actual value from a Fotofly image (SHAME!)
            "0001:01:01",
            "    :  :     :  :  ",
        ];
        examples.forEach((bad) => {
            it(`.fromEXIF(${bad}) => undefined`, () => {
                (0, _chai_spec_1.expect)(ExifDateTime_1.ExifDateTime.fromEXIF(bad)).to.eql(undefined);
            });
            it(`.fromISO(${bad}) => undefined`, () => {
                (0, _chai_spec_1.expect)(ExifDateTime_1.ExifDateTime.fromISO(bad)).to.eql(undefined);
            });
        });
    });
    it("parses timestamps that end in DST", () => {
        const raw = "2014:07:17 08:46:27-07:00 DST";
        const dt = ExifDateTime_1.ExifDateTime.fromEXIF(raw);
        (0, _chai_spec_1.expect)(dt.toISOString()).to.eql("2014-07-17T08:46:27-07:00");
        (0, _chai_spec_1.expect)(dt.zone).to.eql("UTC-7");
    });
    describe("POSIX locale date parsing (gnome-screenshot, etc.)", () => {
        it("parses 12-hour POSIX locale with PDT", () => {
            const raw = "Tue 17 Jun 2025 09:29:01 PM PDT";
            const dt = ExifDateTime_1.ExifDateTime.from(raw);
            (0, _chai_spec_1.expect)(dt).to.not.eql(undefined);
            (0, _chai_spec_1.expect)(dt.year).to.eql(2025);
            (0, _chai_spec_1.expect)(dt.month).to.eql(6);
            (0, _chai_spec_1.expect)(dt.day).to.eql(17);
            (0, _chai_spec_1.expect)(dt.hour).to.eql(21); // 9 PM = 21
            (0, _chai_spec_1.expect)(dt.minute).to.eql(29);
            (0, _chai_spec_1.expect)(dt.second).to.eql(1);
            (0, _chai_spec_1.expect)(dt.zone).to.eql("UTC-7");
            (0, _chai_spec_1.expect)(dt.tzoffsetMinutes).to.eql(-420);
            (0, _chai_spec_1.expect)(dt.inferredZone).to.eql(false);
        });
        it("parses 12-hour POSIX locale with PST", () => {
            const raw = "Thu 16 Jan 2025 10:00:00 AM PST";
            const dt = ExifDateTime_1.ExifDateTime.from(raw);
            (0, _chai_spec_1.expect)(dt).to.not.eql(undefined);
            (0, _chai_spec_1.expect)(dt.year).to.eql(2025);
            (0, _chai_spec_1.expect)(dt.month).to.eql(1);
            (0, _chai_spec_1.expect)(dt.day).to.eql(16);
            (0, _chai_spec_1.expect)(dt.hour).to.eql(10);
            (0, _chai_spec_1.expect)(dt.minute).to.eql(0);
            (0, _chai_spec_1.expect)(dt.second).to.eql(0);
            (0, _chai_spec_1.expect)(dt.zone).to.eql("UTC-8");
            (0, _chai_spec_1.expect)(dt.tzoffsetMinutes).to.eql(-480);
        });
        it("parses 24-hour POSIX locale with CEST", () => {
            const raw = "Tue 15 Jul 2025 14:30:00 CEST";
            const dt = ExifDateTime_1.ExifDateTime.from(raw);
            (0, _chai_spec_1.expect)(dt).to.not.eql(undefined);
            (0, _chai_spec_1.expect)(dt.year).to.eql(2025);
            (0, _chai_spec_1.expect)(dt.month).to.eql(7);
            (0, _chai_spec_1.expect)(dt.day).to.eql(15);
            (0, _chai_spec_1.expect)(dt.hour).to.eql(14);
            (0, _chai_spec_1.expect)(dt.minute).to.eql(30);
            (0, _chai_spec_1.expect)(dt.second).to.eql(0);
            (0, _chai_spec_1.expect)(dt.zone).to.eql("UTC+2");
            (0, _chai_spec_1.expect)(dt.tzoffsetMinutes).to.eql(120);
        });
        it("parses POSIX locale without weekday prefix", () => {
            const raw = "17 Jun 2025 09:29:01 PM PDT";
            const dt = ExifDateTime_1.ExifDateTime.from(raw);
            (0, _chai_spec_1.expect)(dt).to.not.eql(undefined);
            (0, _chai_spec_1.expect)(dt.year).to.eql(2025);
            (0, _chai_spec_1.expect)(dt.month).to.eql(6);
            (0, _chai_spec_1.expect)(dt.day).to.eql(17);
            (0, _chai_spec_1.expect)(dt.hour).to.eql(21);
            (0, _chai_spec_1.expect)(dt.zone).to.eql("UTC-7");
        });
    });
    it("try to repro issue #46", () => {
        const edt = new ExifDateTime_1.ExifDateTime(2019, 3, 8, 14, 24, 54, 0, -480);
        const dt = edt.toDateTime();
        (0, _chai_spec_1.expect)(dt.toISO()).to.eql("2019-03-08T14:24:54.000-08:00");
    });
    it("try to repro issue #118", () => {
        const edt = ExifDateTime_1.ExifDateTime.fromExifStrict("1970:01:01 00:00:00Z");
        (0, _chai_spec_1.expect)(edt.toMillis()).to.eql(0);
    });
    it("parses non-standard timezone offset", () => {
        // 1900-1923, Kyiv had an offset of UTC +2:02:04
        const edt = ExifDateTime_1.ExifDateTime.fromExifStrict("1904:02:03 13:14:15", "Europe/Kiev");
        (0, _chai_spec_1.expect)(edt.hasZone).to.eql(true);
        (0, _chai_spec_1.expect)(edt.isValid).to.eql(true);
        (0, _chai_spec_1.expect)(edt.toISOString()).to.eql("1904-02-03T13:14:15+02:02");
    });
    describe(".fromMillis()", () => {
        it("round-trips now()", () => {
            const e = ExifDateTime_1.ExifDateTime.now();
            (0, _chai_spec_1.expect)(e.toDate().getTime()).to.be.closeTo(Date.now(), 2_000);
        });
        it("round-trips now() and retains raw value", () => {
            const rawValue = (0, _chai_spec_1.randomChars)();
            const e = ExifDateTime_1.ExifDateTime.now({ rawValue });
            (0, _chai_spec_1.expect)(e.toDate().getTime()).to.be.closeTo(Date.now(), 2_000);
            (0, _chai_spec_1.expect)(e.rawValue).to.eql(rawValue);
        });
        it("renders pre-epoch timestamp()", () => {
            const e = ExifDateTime_1.ExifDateTime.fromMillis(-27686744322, { zone: "UTC" });
            (0, _chai_spec_1.expect)(e.toISOString()).to.eql("1969-02-14T13:14:15.678Z");
        });
        it("renders UTC TWOSday timestamp()", () => {
            const e = ExifDateTime_1.ExifDateTime.fromMillis(1643767342222, { zone: "UTC" });
            (0, _chai_spec_1.expect)(e.toISOString()).to.eql("2022-02-02T02:02:22.222Z");
        });
        it("renders PST TWOSday timestamp()", () => {
            const rawValue = (0, _chai_spec_1.randomChars)();
            const e = ExifDateTime_1.ExifDateTime.fromMillis(1643796142345, {
                zone: "America/Los_Angeles",
                rawValue,
            });
            (0, _chai_spec_1.expect)(e.toISOString()).to.eql("2022-02-02T02:02:22.345-08:00");
            (0, _chai_spec_1.expect)(e.rawValue).to.eql(rawValue);
        });
    });
    describe(".plus()", () => {
        const ts = Math.floor((0, node_crypto_1.randomInt)(0, Date.now()) / DateTime_1.MinuteMs) * DateTime_1.MinuteMs;
        it("retains values with no zone", () => {
            console.dir({ timestamp: ts });
            // we want a timestamp where adding a second won't change the minute:
            const e = ExifDateTime_1.ExifDateTime.fromMillis(ts);
            (0, _chai_spec_1.expect)(e.hasZone).to.eql(false);
            const json = e.toJSON();
            const e2 = e.plus({ seconds: 1 });
            (0, _chai_spec_1.expect)(e2.hasZone).to.eql(false);
            (0, _chai_spec_1.expect)(e2.toJSON()).to.containSubset((0, Object_1.omit)(json, "second", "millisecond", "rawValue"));
        });
        it("retains values with zone", () => {
            const zoneStr = "UTC+6";
            // we want a timestamp where adding a second won't change the minute:
            const e = ExifDateTime_1.ExifDateTime.fromMillis(ts).setZone(zoneStr);
            (0, _chai_spec_1.expect)(e.hasZone).to.eql(true);
            const json = e.toJSON();
            const e2 = e.plus({ seconds: 1 });
            (0, _chai_spec_1.expect)(e2.hasZone).to.eql(true);
            (0, _chai_spec_1.expect)(e2.zone).to.eql(zoneStr);
            (0, _chai_spec_1.expect)(e2.toJSON()).to.containSubset((0, Object_1.omit)(json, "second", "millisecond", "rawValue"));
        });
    });
});
//# sourceMappingURL=ExifDateTime.spec.js.map