"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get DATE_OBJECT_SCHEMA () {
        return DATE_OBJECT_SCHEMA;
    },
    get formatFhirDate () {
        return formatFhirDate;
    },
    get parseDateTime () {
        return parseDateTime;
    }
});
const _datefns = require("date-fns");
const _datefnstz = require("date-fns-tz");
const _lodash = require("lodash");
const _yup = require("yup");
const _constants = require("@tamanu/constants");
const _dateTime = require("@tamanu/utils/dateTime");
const _errors = require("./errors");
function extractTz(str) {
    // eslint-disable-next-line no-unused-vars
    const [_date, time] = str.split('T');
    const plus = time.lastIndexOf('+');
    if (plus !== -1) return time.slice(plus);
    const minus = time.lastIndexOf('-');
    if (minus !== -1) return time.slice(minus);
    return null;
}
function normalizeTz(tz, date) {
    const offset = (0, _datefnstz.getTimezoneOffset)(tz, date) / 1000;
    if (isNaN(offset)) return null;
    const sign = offset < 0 ? '-' : '+';
    const hours = Math.floor(Math.abs(offset) / 3600);
    const minutes = Math.floor(Math.abs(offset) % 3600 / 60);
    const seconds = Math.floor(Math.abs(offset) % 60);
    let offsetSuffix = `${sign}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
    if (seconds !== 0) {
        offsetSuffix += `:${seconds.toString().padStart(2, '0')}`;
    }
    return offsetSuffix;
}
function dateParts(combinedDate, withTz, str, form) {
    let date = combinedDate;
    let tz = null;
    if (form.endsWith('X') && str.endsWith('Z')) {
        tz = '+00:00';
    } else if (form.endsWith('XXX')) {
        const tzm = extractTz(str);
        if (tzm) tz = tzm;
    } else if (form.endsWith('X')) {
        const tzh = extractTz(str);
        if (tzh) tz = `${tzh}:00`;
    } else if (withTz) {
        // no timezone in the format, use provided timezone
        date = (0, _datefnstz.zonedTimeToUtc)(combinedDate, withTz);
        tz = normalizeTz(tz, date);
    }
    // else: no timezone in the format, using system timezone to parse and no tz in the output
    return {
        plain: date,
        sql: (0, _datefns.formatISO9075)(date),
        sqlDate: (0, _datefns.format)(date, 'yyyy-MM-dd'),
        iso: (0, _datefns.formatRFC3339)(date),
        year: (0, _datefns.getYear)(date),
        month: (0, _datefns.getMonth)(date),
        day: (0, _datefns.getDate)(date),
        hour: (0, _datefns.getHours)(date),
        minute: (0, _datefns.getMinutes)(date),
        second: (0, _datefns.getSeconds)(date),
        tz
    };
}
const DATE_OBJECT_SCHEMA = (0, _yup.object)({
    precision: (0, _yup.string)().oneOf(Object.values(_constants.FHIR_DATETIME_PRECISION)).required(),
    plain: (0, _yup.date)().required(),
    sql: (0, _yup.string)().required(),
    iso: (0, _yup.string)().required(),
    value: (0, _yup.object)({
        year: (0, _yup.number)().required(),
        month: (0, _yup.number)().optional(),
        day: (0, _yup.number)().optional(),
        hour: (0, _yup.number)().optional(),
        minute: (0, _yup.number)().optional(),
        second: (0, _yup.number)().optional(),
        tz: (0, _yup.string)().optional()
    })
}).noUnknown();
const COMMONS = [
    'plain',
    'sql',
    'iso'
];
const FORMS = {
    "yyyy-MM-dd'T'HH:mm:ssXXX": [
        _constants.FHIR_DATETIME_PRECISION.SECONDS_WITH_TIMEZONE,
        [
            'year',
            'month',
            'day',
            'hour',
            'minute',
            'second',
            'tz'
        ]
    ],
    "yyyy-MM-dd'T'HH:mm:ssX": [
        _constants.FHIR_DATETIME_PRECISION.SECONDS_WITH_TIMEZONE,
        [
            'year',
            'month',
            'day',
            'hour',
            'minute',
            'second',
            'tz'
        ]
    ],
    "yyyy-MM-dd'T'HH:mmXXX": [
        _constants.FHIR_DATETIME_PRECISION.MINUTES_WITH_TIMEZONE,
        [
            'year',
            'month',
            'day',
            'hour',
            'minute',
            'tz'
        ]
    ],
    "yyyy-MM-dd'T'HH:mmX": [
        _constants.FHIR_DATETIME_PRECISION.MINUTES_WITH_TIMEZONE,
        [
            'year',
            'month',
            'day',
            'hour',
            'minute',
            'tz'
        ]
    ],
    "yyyy-MM-dd'T'HHXXX": [
        _constants.FHIR_DATETIME_PRECISION.HOURS_WITH_TIMEZONE,
        [
            'year',
            'month',
            'day',
            'hour',
            'tz'
        ]
    ],
    "yyyy-MM-dd'T'HHX": [
        _constants.FHIR_DATETIME_PRECISION.HOURS_WITH_TIMEZONE,
        [
            'year',
            'month',
            'day',
            'hour',
            'tz'
        ]
    ],
    "yyyy-MM-dd'T'HH:mm:ss": [
        _constants.FHIR_DATETIME_PRECISION.SECONDS,
        [
            'year',
            'month',
            'day',
            'hour',
            'minute',
            'second'
        ]
    ],
    "yyyy-MM-dd'T'HH:mm": [
        _constants.FHIR_DATETIME_PRECISION.MINUTES,
        [
            'year',
            'month',
            'day',
            'hour',
            'minute'
        ]
    ],
    "yyyy-MM-dd'T'HH": [
        _constants.FHIR_DATETIME_PRECISION.HOURS,
        [
            'year',
            'month',
            'day',
            'hour'
        ]
    ],
    'yyyy-MM-dd': [
        _constants.FHIR_DATETIME_PRECISION.DAYS,
        [
            'year',
            'month',
            'day'
        ]
    ],
    'yyyy-MM': [
        _constants.FHIR_DATETIME_PRECISION.MONTHS,
        [
            'year',
            'month'
        ]
    ],
    yyyy: [
        _constants.FHIR_DATETIME_PRECISION.YEARS,
        [
            'year'
        ]
    ]
};
function parseDateTime(str, { ref = new Date(), withTz = null } = {}) {
    for (const [form, [precision, extract]] of Object.entries(FORMS)){
        const date = (0, _datefns.parse)(str, form, ref);
        if ((0, _datefns.isValid)(date)) {
            const parts = dateParts(date, withTz, str, form);
            return {
                // note this is the input precision; when using withTz, the value
                // will include a tz even if the precision isn't *_WITH_TIMEZONE.
                precision,
                ...(0, _lodash.pick)(parts, COMMONS),
                value: (0, _lodash.pick)(parts, extract)
            };
        }
    }
    return false;
}
function formatFhirDate(date, precision = _constants.FHIR_DATETIME_PRECISION.SECONDS_WITH_TIMEZONE) {
    if (date === null || date === undefined) return date;
    const actual = (0, _dateTime.parseDate)(date);
    switch(precision){
        case _constants.FHIR_DATETIME_PRECISION.SECONDS_WITH_TIMEZONE:
            return (0, _datefns.format)(actual, "yyyy-MM-dd'T'HH:mm:ssXXX");
        case _constants.FHIR_DATETIME_PRECISION.DAYS:
            return (0, _datefns.format)(actual, 'yyyy-MM-dd');
        case _constants.FHIR_DATETIME_PRECISION.MONTHS:
            return (0, _datefns.format)(actual, 'yyyy-MM');
        case _constants.FHIR_DATETIME_PRECISION.YEARS:
            return (0, _datefns.format)(actual, 'yyyy');
        case _constants.FHIR_DATETIME_PRECISION.MINUTES_WITH_TIMEZONE:
        case _constants.FHIR_DATETIME_PRECISION.HOURS_WITH_TIMEZONE:
        case _constants.FHIR_DATETIME_PRECISION.SECONDS:
        case _constants.FHIR_DATETIME_PRECISION.MINUTES:
        case _constants.FHIR_DATETIME_PRECISION.HOURS:
            // not allowed under FHIR
            throw new _errors.Exception('cannot format to that precision level');
        default:
            throw new _errors.Exception(`unknown datetime precision: ${precision}`);
    }
}

//# sourceMappingURL=datetime.js.map