/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    ql_time.qpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include <qore/Qore.h>
#include "qore/intern/ql_time.h"
#include "qore/intern/QC_TimeZone.h"
#include "qore/intern/qore_date_private.h"
#include "qore/intern/QoreHashNodeIntern.h"

#include <cstdio>
#include <ctime>
#include <sys/time.h>

static int qd_get_digit(unsigned& rv, const char*& p, unsigned factor) {
    if (*p < '0' || *p > '9') {
        throw QoreStandardException("INVALID-DATE", "invalid digit '%c' found in date/time input", *p);
    }

    rv += (*p - '0') * factor;
    ++p;
    return 0;
}

// returns 0 if invalid data is encountered
static unsigned parse_int_2(const char *p) {
    unsigned rv = 0;
    if (qd_get_digit(rv, p, 10))
        return 0;
    if (qd_get_digit(rv, p, 1))
        return 0;
    return rv;
}

// returns 0 if invalid data is encountered
static unsigned parse_int_3(const char *p) {
   unsigned rv = 0;
   if (qd_get_digit(rv, p, 100))
      return 0;
   if (qd_get_digit(rv, p, 10))
      return 0;
   if (qd_get_digit(rv, p, 1))
      return 0;
   return rv;
}

// returns 0 if invalid data is encountered
static unsigned parse_int_4(const char *p) {
   unsigned rv = 0;
   if (qd_get_digit(rv, p, 1000))
      return 0;
   if (qd_get_digit(rv, p, 100))
      return 0;
   if (qd_get_digit(rv, p, 10))
      return 0;
   if (qd_get_digit(rv, p, 1))
      return 0;
   return rv;
}

// returns 0 if invalid data is encountered
static unsigned parse_int_6(const char *p) {
   unsigned rv = 0;
   if (qd_get_digit(rv, p, 100000))
      return 0;
   if (qd_get_digit(rv, p, 10000))
      return 0;
   if (qd_get_digit(rv, p, 1000))
      return 0;
   if (qd_get_digit(rv, p, 100))
      return 0;
   if (qd_get_digit(rv, p, 10))
      return 0;
   if (qd_get_digit(rv, p, 1))
      return 0;
   return rv;
}

QoreHashNode* date_info(const DateTime& d) {
    qore_tm info;
    d.getInfo(info);

    QoreHashNode* h = new QoreHashNode(hashdeclDateTimeInfo, nullptr);
    qore_hash_private* ph = qore_hash_private::get(*h);
    ph->setKeyValueIntern("relative", d.isRelative());
    ph->setKeyValueIntern("year", info.year);
    ph->setKeyValueIntern("month", info.month);
    ph->setKeyValueIntern("day", info.day);
    ph->setKeyValueIntern("hour", info.hour);
    ph->setKeyValueIntern("minute", info.minute);
    ph->setKeyValueIntern("second", info.second);
    ph->setKeyValueIntern("microsecond", info.us);

    if (d.isAbsolute()) {
        ph->setKeyValueIntern("dow", d.getDayOfWeek());
        ph->setKeyValueIntern("doy", d.getDayNumber());
        ph->setKeyValueIntern("utc_secs_east", info.utc_secs_east);
        ph->setKeyValueIntern("dst", info.dst);
        ph->setKeyValueIntern("zone_name", new QoreStringNode(info.zone_name));
        ph->setKeyValueIntern("zone", new QoreObject(QC_TIMEZONE, 0, new TimeZoneData(info.zone)));
    }

    return h;
}

DateTimeNode* make_date_with_mask(const AbstractQoreZoneInfo* tz, const QoreString& dtstr, const QoreString& mask, ExceptionSink* xsink) {
    qore_tm dt;
    dt.clear();
    dt.month = 1;
    dt.day = 1;

    const char* d = dtstr.c_str();
    const char* de = d + dtstr.strlen();

    const char* s = mask.c_str();
    const char* se = s + mask.strlen();

    try {
        while (*s) {
            // break if we run out of data in either the mask or the date string
            if (s >= se)
                break;
            switch (*s) {
                case 'Y':
                    if (s[1] != 'Y') {
                        throw QoreStandardException("INVALID-DATE", "'Y' has to be used as 'YY' or 'YYYY'");
                    }
                    if (s[2] == 'Y' && s[3] == 'Y') {
                        if ((de - d) < 4) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for year mask");
                        }

                        dt.year = parse_int_4(d);
                        s += 3;
                        d += 3;
                    } else {
                        if ((de - d) < 2) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for year mask");
                        }

                        ++s;
                        // obtain the current century
                        {
                            DateTime tmpdt(q_epoch());
                            dt.year = parse_int_2(d) + (tmpdt.getYear() / 100 * 100);
                        }
                        ++d;
                    }
                    break;

                case 'M':
                    if ((de - d) < 2) {
                        throw QoreStandardException("INVALID-DATE", "no more input to process for month mask");
                    }
                    if (s[1] == 'M') {
                        dt.month = parse_int_2(d);
                        s++;
                        d++;
                        break;
                    }
                    // 'M' is not supported because there is no clear way how to get eg. 1 or 11
                    if ((s[1] == 'o' || s[1] == 'O') && (s[2] == 'n' || s[2] == 'N')) {
                        QoreString str(d, 3);
                        dt.month = str.strlen() == 3 ? qore_date_info::getMonthIxFromAbbr(str.getBuffer()) : -1;
                        if (dt.month < 0 || dt.month > 11) {
                            throw QoreStandardException("INVALID-DATE", "Invalid 'Mon' string: '%s'", !str.empty() ? str.getBuffer() : "<none>");
                        }

                        ++dt.month;
                        s += 2;
                        d += 2;
                        break;
                    }
                    break;

                case 'm':
                    if (s[1] == 'm') {
                        if ((de - d) < 2) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for minute mask");
                        }
                        dt.minute = parse_int_2(d);
                        s++;
                        d++;
                        break;
                    }
                    if (s[1] == 's') {
                        if ((de - d) < 2) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for millisecond mask");
                        }
                        dt.us = parse_int_3(d) * 1000;
                        ++s;
                        d += 2;
                        break;
                    }
                    break;

                case 'D':
                case 'd':
                    if (s[1] == 'D' || s[1] == 'd') {
                        if ((de - d) < 2) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for day mask");
                        }
                        dt.day = parse_int_2(d);
                        s++;
                        d++;
                    }
                    break;

                case 'H':
                case 'h':
                    if (s[1] == 'H' || s[1] == 'h') {
                        if ((de - d) < 2) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for hour mask");
                        }
                        dt.hour = parse_int_2(d);
                        s++;
                        d++;
                    }
                    break;

                case 's':
                    if (s[1] == 's' && s[2] == 's') {
                        if (s[3] == 's' && s[4] == 's' && s[5] == 's') {
                            if ((de - d) < 6) {
                                throw QoreStandardException("INVALID-DATE", "no more input to process for second mask");
                            }
                            dt.us = parse_int_6(d);
                            s += 5;
                            d += 5;
                        } else {
                            if ((de - d) < 3) {
                                throw QoreStandardException("INVALID-DATE", "no more input to process for second mask");
                            }
                            dt.us = parse_int_3(d) * 1000;
                            s += 2;
                            d += 2;
                        }
                    }
                    break;

                case 'S':
                    if (s[1] == 'S') {
                        if ((de - d) < 2) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for second mask");
                        }
                        dt.second = parse_int_2(d);
                        s++;
                        d++;
                    }
                    break;

                case 'u':
                    if (s[1] == 's') {
                        if ((de - d) < 6) {
                            throw QoreStandardException("INVALID-DATE", "no more input to process for microsecond mask");
                        }
                        dt.us = parse_int_6(d);
                        ++s;
                        d += 5;
                        break;
                    }
            }
            ++s;
            ++d;
        }
    } catch (AbstractException& e) {
        e.convert(xsink);
    }

    if (*xsink)
        return nullptr;

    SimpleRefHolder<DateTimeNode> rv(DateTimeNode::makeAbsolute(tz, dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.us, xsink));
    return *xsink ? nullptr : rv.release();
}

//! date/time information hash as returned by @ref Qore::date_info() "date_info()" and <date>::info()
/** @since %Qore 0.8.13
*/
hashdecl DateTimeInfo {
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") @ref True if the date is a relative date, @ref False if it is absolute
    bool relative;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The year value of the date
    int year;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The month value of the date
    int month;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The day value of the date (day of the month for absolute dates)
    int day;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The hour value of the date
    int hour;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The minute value of the date
    int minute;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The second value of the date
    int second;
    //! (@ref absolute_dates "absolute" and @ref relative_dates "relative") The microsecond value of the date
    int microsecond;
    //! (@ref absolute_dates "absolute" Only) The day of the week, where 0=Sunday, 1=Monday, ... 6=Saturday
    int dow;
    //! (@ref absolute_dates "absolute" Only) The ordinal day number in the year
    int doy;
    //! (@ref absolute_dates "absolute" Only) Offset from UTC in seconds east; if the time zone is west of UTC then the value will be negative
    int utc_secs_east;
    //! (@ref absolute_dates "absolute" Only) A flag if daylight savings time is in effect
    bool dst;
    //! (@ref absolute_dates "absolute" Only) The time zone for the time
    Qore::TimeZone zone;
    //! (@ref absolute_dates "absolute" Only) The name of the time zone for the given time (ex: "CEST" for Central European Summer Time for a time during summer time or "CET" for Central European Time for the same time zone while daylight savings time is not active)
    string zone_name;
}

//! ISO week information as returned by @ref Qore::get_iso_week_hash() "get_iso_week_hash()" and <date>::isoWeekHash()
/** @since %Qore 0.8.13
*/
hashdecl IsoWeekInfo {
    //! the ISO year value (not always equal to the actual calendar year)
    int year;
    //! the ISO week number
    int week;
    //! the ISO day value from 1 (Monday) to 7 (Sunday)
    int day;
}

/** @defgroup date_and_time_functions Date and Time Functions
    @section date_formatting Date Formatting Codes
    |!Format Code|!Description
    |\c YY|last two digits of year
    |\c YYYY|four-digit year
    |\c M|non zero-padded month number (1-12)
    |\c MM|zero-padded two-digit month number (01-12)
    |\c Month|long month string (ex: \c "January")
    |\c MONTH|long month string capitalized (ex: \c "JANUARY")
    |\c Mon|abbreviated month (ex: \c "Jan")
    |\c MON|abbreviated month, capitalized (ex: \c "JAN")
    |\c D|non zero-padded day number (1 - 31)
    |\c DD|zero-padded day number (01 - 31)
    |\c Day|long day of week string (ex: \c "Monday")
    |\c DAY|long day of week string, capitalized (ex: \c "MONDAY")
    |\c Dn|the ordinal day number in the year without zero padding (\c "1" - \c "366" for leap years; ex: \c "85")
    |\c DN|the day number in the year with zero padding to 3 digits (\c "001" - \c "366" for leap years; ex: \c "085")
    |\c Dy|abbreviated day of week string (ex: \c "Mon")
    |\c DY|abbreviated day of week string capitalized (ex: \c "MON")
    |\c H|non zero-padded hour number (0 - 23)
    |\c HH|zero-padded hour number (00 - 23)
    |\c h|non zero-padded hour number, 12-hour clock (1 - 12)
    |\c hh|zero-padded hour number, 12-hour clock (01 - 12)
    |\c I|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week string</a> for the date (ex: \c "2018-W12-5")
    |\c Id|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week day number</a> in the week (\c "1" - \c "7")
    |\c ID|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week day number</a> in the week (\c "1" - \c "7")
    |\c IF|the value in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO-8601</a> format for both @ref relative_dates "relative" (ex: \c "P2Y1M3DT5H7M9.002S") and @ref absolute_dates "absolute" dates (ex: \c "2018-03-23T10:43:12.067628+01:00")
    |\c Iw|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week number</a> for the year without zero padding (\c "1" - \c "53")
    |\c IW|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week number</a> for the year with zero padding to 2 digits (\c "01" - \c "53")
    |\c Iy|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week year number</a> without zero padding (ex: \c "2018")
    |\c IY|the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 week year number</a> with zero padding to 4 digits (ex: \c "2018")
    |\c m|non zero-padded minute number (0 - 59)
    |\c mm|zero-padded minute number (00 - 59)
    |\c S|non zero-padded second number (0 - 59)
    |\c SS|zero-padded second number (00 - 59)
    |\c P|\c "AM" or \c "PM" (upper-case)
    |\c p|\c "am" or \c "pm" (lower-case)
    |\c u|non zero-padded millisecond number (0 - 999)
    |\c uu or \c ms|zero-padded millisecond number (000 - 999)
    |\c x|non zero-padded microsecond number (0 - 999999)
    |\c xx or \c us|zero-padded microsecond number (000000 - 999999)
    |\c y|microseconds, with trailing zeros removed (suitable for use after the '.')
    |\c z|local time zone name (ex: \c "EST") if available, otherwise the UTC offset (ex: \c "+01:00")
    |\c Z|time zone UTC offset like +HH:mm[:SS] (ex: \c "+01:00"), seconds are only included if non-zero

    @since
    - \c us was added in %Qore 0.8.7
    - \c Dn, \c DN, \c I, \c Id, \c ID, \c IF, \c Iw, \c IW, \c Iy, \c IY format codes were added in %Qore 0.9

    @bug the \c "u" and \c "uu" format codes work with milliseconds and not microseconds since they were implemented before
    qore supported microsecond time resolution; this cannot be changed without breaking backwards-compatibility

    @section date_mask Date Mask Format
    The date mask (as used in @ref Qore::date(string, string) and @ref Qore::TimeZone::date(string, string)) is used to specify
    the string format of a date/time value for parsing to a @ref date "date/time value".

    The fields in the mask string are as follows:
    - \c YY: The last 2 digits of the year; the current century is assumed
    - \c YYYY: A 4-digit year value (ex: 1956, 0870)
    - \c MM: A 2-digit month number (ex: 01, 11)
    - \c Mon or \c MON: A 3-character English month name abbreviation (parsing is case-insensitive: ex: \c "Apr" or \c "APR" or \c "apr" for April)
    - \c DD: A 2-digit day number (ex: 29, 04)
    - \c HH: A 2-digit hour number (ex: 23, 04)
    - \c mm: A 2-digit minute number (ex: 11, 04)
    - \c ms: A 3-digit millisecond number (ex: 511, 042)
    - \c SS: A 2-digit second number (ex: 11, 04)
    - \c sss: A 3-digit millisecond number (ex: 511, 042)
    - \c ssssss: A 6-digit microsecond number (ex: 240511, 005142)
    - \c us: A 6-digit microsecond number (ex: 240511, 005142)

    @bug currently only English month abbreviations are accepted in the date/time mask
*/
///@{

//! Returns the current date and time with a resolution to the millisecond
/**
    @return the current date and time with a resolution to the millisecond

    @par Example:
    @code{.py}
date d = now_ms();
    @endcode

    @see
    - now() For a similar function returning the current date and time in the local time zone with coarser granularity, when resolution only to the second is needed
    - now_us() for a similar function returning the current date and time with a resolution to the microsecond
    - now_utc();

    @note There is no performance penalty for using now_ms() and now_us() versus now(); now() and now_ms() are kept for backwards compatibility
*/
date now_ms() [flags=CONSTANT] {
   int us;
   int64 seconds = q_epoch_us(us);

   return DateTimeNode::makeAbsolute(currentTZ(), seconds, (us / 1000) * 1000);
}

//! Returns the current date and time with a resolution to the microsecond
/**
    @return the current date and time with a resolution to the microsecond

    @par Example:
    @code{.py}
date d = now_us();
    @endcode

    @see
    - now() For a similar function returning the current date and time in the local time zone with coarser granularity, when resolution only to the second is needed
    - now_ms() for a similar function returning the current date and time in the local time zone with coarser granularity, when resolution only to the millisecond is needed
    - now_utc();

    @note There is no performance penalty for using now_ms() and now_us() versus now(); now() and now_ms() are kept for backwards compatibility
*/
date now_us() [flags=CONSTANT] {
   int us;
   int64 seconds = q_epoch_us(us);

   return DateTimeNode::makeAbsolute(currentTZ(), seconds, us);
}

//! Returns the current UTC date and time with a resolution to the microsecond
/**
    @return the current UTC date and time with a resolution to the microsecond

    @par Example:
    @code{.py}
date d = now_utc();
    @endcode

    @see now_us() for a similar function that returns the current date and time in the local time zone
*/
date now_utc() [flags=CONSTANT] {
   int us;
   int64 seconds = q_epoch_us(us);
   return DateTimeNode::makeAbsolute(NULL, seconds, us);
}

//! Returns a formatted string for a date argument passed
/**
    @param format a string giving the format for the date; see @ref date_formatting for more information about this string
    @param dt the date to use for the string output

    @return a formatted string for a date argument passed

    @par Example:
    @code{.py}
string str = format_date("Day, Mon D, YYYY-MM-DD HH:mm:SS", 2004-02-01T12:30:00);
# returns "Sunday, Feb 1, 2004-02-01 12:30:00"
    @endcode

    @bug there is no locale support; day and month names and abbreviations are only returned in English
*/
string format_date(string format, date dt) [flags=CONSTANT] {
   QoreStringNode *rv = new QoreStringNode;
   dt->format(*rv, format->getBuffer());
   return rv;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing format_date() [flags=RUNTIME_NOOP] {
}

//! Returns the current date and time with a resolution to the second
/**
    @return the current date and time with a resolution to the second

    @par Example:
    @code{.py}
date d = now();
    @endcode

    @see
    - now_ms()
    - now_us()
    - now_utc()

    @note this function is equivalent to localtime()
*/
date now() [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(currentTZ(), (int64)time(0), 0);
}

//! Returns the current date and time with a resolution to the second
/**
    @return the current date and time with a resolution to the second

    @par Example:
    @code{.py}
date d = localtime();
    @endcode

    @see gmtime()

    @note this variant of localtime() is equivalent to now()
*/
date localtime() [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(currentTZ(), (int64)time(0), 0);
}

//! Returns the date and time in the local time zone corresponding to the integer arguments passed, which are interpreted as the number of seconds and microseconds since Jan 1, 1970, 00:00:00 Z (UTC)
/**
    @param secs the number of seconds and microseconds since Jan 1, 1970, 00:00:00 Z (UTC)
    @param us a microsecond offset for the time returned

    @return the date and time in the local time zone corresponding to the arguments passed

    @par Example:
    @code{.py}
date dt = localtime(10);
    @endcode

    @see gmtime(softint, softint)
*/
date localtime(softint secs, softint us = 0) [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(currentTZ(), secs, (int)us);
}

//! Returns the date and time in the local time zone corresponding to the date argument passed
/**
    @param dt a date to process; if the date passed is in the local time zone, then the same value is returned

    @return the date and time in the local time zone corresponding to the date argument passed

    @par Example:
    @code{.py}
date dt = localtime(2012-01-19T15:00:00-07:00);
    @endcode

    @see gmtime(date)
*/
date localtime(date dt) [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(currentTZ(), dt->getEpochSecondsUTC(), dt->getMicrosecond());
}

//! Returns the current UTC (GMT) time with a resolution of a second
/**
    @return the current UTC (GMT) time with a resolution of a second

    @par Example:
    @code{.py}
date utc = gmtime();
    @endcode

    @see
    - now_utc()
    - localtime()
*/
date gmtime() [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(NULL, (int64)time(0), 0);
}

//! Returns a date/time value in UTC (GMT) from arguments giving the number of seconds and microseconds since Jan 1, 1970, 00:00:00 Z (UTC)
/**
    @param secs the number of seconds and microseconds since Jan 1, 1970, 00:00:00 Z (UTC)
    @param us a microsecond offset for the time returned

    @return a date/time value in UTC (GMT) from arguments giving the number of seconds and microseconds since Jan 1, 1970, 00:00:00 Z (UTC)

    @par Example:
    @code{.py}
date dt = gmtime(10);
    @endcode

    @see localtime(softint, softint)
*/
date gmtime(softint secs, softint us = 0) [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(NULL, secs, (int)us);
}

//! Returns the date and time in UTC (GMT) corresponding to the date argument passed
/**
    @param dt a date to process; if the date passed is already in UTC, then the same value is returned

    @return the date and time in UTC (GMT) corresponding to the date argument passed

    @par Example:
    @code{.py}
date dt = gmtime(2012-01-19T15:00:00-07:00);
    @endcode

    @see localtime(date)
*/
date gmtime(date dt) [flags=CONSTANT] {
   return DateTimeNode::makeAbsolute(NULL, dt->getEpochSecondsUTC(), dt->getMicrosecond());
}

//! Returns the number of seconds since January 1, 1970 00:00:00 in the local time zone for the given date
/**
    @param dt The date to process

    @return the number of seconds since January 1, 1970 00:00:00 in the local time zone for the given date

    @par Example:
    @code{.py}
int secs = timegm(dt);
    @endcode

    @see get_epoch_seconds(date)
*/
int timegm(date dt) [flags=CONSTANT] {
   return dt->getEpochSeconds();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing timegm() [flags=RUNTIME_NOOP] {
}

//! Returns the number of seconds of the date and time in local time passed since Jan 1, 1970, 00:00:00 Z (UTC); negative values are returned for dates before the epoch
/**
    @param dt The date to process

    @return the number of seconds of the date and time in local time passed since Jan 1, 1970, 00:00:00 Z (UTC); negative values are returned for dates before the epoch

    @par Example:
    @code{.py}
int i = get_epoch_seconds(2012-01-19T08:02:15+01:00);
    @endcode

    @see timegm(date)

    @note This function is equivalent to mktime(date)
*/
int get_epoch_seconds(date dt) [flags=CONSTANT] {
   return dt->getEpochSecondsUTC();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_epoch_seconds() [flags=RUNTIME_NOOP] {
}

//! Returns the number of seconds of the date and time in local time passed since Jan 1, 1970, 00:00:00 Z (UTC); negative values are returned for dates before the epoch
/**
    @param dt The date to process

    @return the number of seconds of the date and time in local time passed since Jan 1, 1970, 00:00:00 Z (UTC); negative values are returned for dates before the epoch

    @par Example:
    @code{.py}
int i = mktime(2012-01-19T08:02:15+01:00);
    @endcode

    @see timegm(date)

    @note This function is equivalent to get_epoch_seconds(date)
*/
int mktime(date dt) [flags=CONSTANT] {
   return dt->getEpochSecondsUTC();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing mktime() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in years based on the integer argument passed to be used in date arithmetic
/**
    @param years the number of years to return

    @return a @ref relative_dates "relative date/time value" in years based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date y = years(100);
    @endcode

    @see
    - months(softint)
    - days(softint)
    - hours(softint)
    - minutes(softint)
    - seconds(softint)
    - milliseconds(softint)
    - microseconds(softint)
*/
date years(softint years) [flags=CONSTANT] {
   return new DateTimeNode((int)years, 0, 0, 0, 0, 0, 0, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing years() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in months based on the integer argument passed to be used in date arithmetic
/**
    @param months the number of months to return

    @return a @ref relative_dates "relative date/time value" in months based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = months(100);
    @endcode

    @see
    - years(softint)
    - days(softint)
    - hours(softint)
    - minutes(softint)
    - seconds(softint)
    - milliseconds(softint)
    - microseconds(softint)
*/
date months(softint months) [flags=CONSTANT] {
   return new DateTimeNode(0, (int)months, 0, 0, 0, 0, 0, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing months() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in days based on the integer argument passed to be used in date arithmetic
/**
    @param days the number of days to return

    @return a @ref relative_dates "relative date/time value" in days based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = days(100);
    @endcode

    @see
    - years(softint)
    - months(softint)
    - hours(softint)
    - minutes(softint)
    - seconds(softint)
    - milliseconds(softint)
    - microseconds(softint)
*/
date days(softint days) [flags=CONSTANT] {
   return new DateTimeNode(0, 0, (int)days, 0, 0, 0, 0, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing days() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in hours based on the integer argument passed to be used in date arithmetic
/**
    @param hours the number of hours to return

    @return a @ref relative_dates "relative date/time value" in hours based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = hours(100);
    @endcode

    @see
    - years(softint)
    - months(softint)
    - hours(softint)
    - minutes(softint)
    - seconds(softint)
    - milliseconds(softint)
    - microseconds(softint)
*/
date hours(softint hours) [flags=CONSTANT] {
   return new DateTimeNode(0, 0, 0, (int)hours, 0, 0, 0, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing hours() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in minutes based on the integer argument passed to be used in date arithmetic
/**
    @param minutes the number of minutes to return

    @return a @ref relative_dates "relative date/time value" in minutes based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = minutes(100);
    @endcode

    @see
    - years(softint)
    - months(softint)
    - days(softint)
    - hours(softint)
    - seconds(softint)
    - milliseconds(softint)
    - microseconds(softint)
*/
date minutes(softint minutes) [flags=CONSTANT] {
   return new DateTimeNode(0, 0, 0, 0, (int)minutes, 0, 0, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing minutes() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in seconds based on the integer argument passed to be used in date arithmetic
/**
    @param seconds the number of seconds to return

    @return a @ref relative_dates "relative date/time value" in seconds based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = seconds(100);
    @endcode

    @see
    - years(softint)
    - months(softint)
    - days(softint)
    - hours(softint)
    - minutes(softint)
    - milliseconds(softint)
    - microseconds(softint)
*/
date seconds(softint seconds) [flags=CONSTANT] {
   return new DateTimeNode(0, 0, 0, 0, 0, (int)seconds, 0, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing seconds() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in milliseconds based on the integer argument passed to be used in date arithmetic
/**
    @param ms the number of milliseconds to return

    @return a @ref relative_dates "relative date/time value" in milliseconds based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = milliseconds(100);
    @endcode

    @see
    - years(softint)
    - months(softint)
    - days(softint)
    - hours(softint)
    - minutes(softint)
    - seconds(softint)
    - microseconds(softint)
*/
date milliseconds(softint ms) [flags=CONSTANT] {
   int64 secs = ms / 1000;
   ms -= (secs * 1000);
   int64 minutes = secs / 60;
   secs -= (minutes * 60);
   int64 hours = minutes / 60;
   minutes -= (hours * 60);

   return new DateTimeNode(0, 0, 0, (int)hours, (int)minutes, (int)secs, (int)ms, true);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing milliseconds() [flags=RUNTIME_NOOP] {
}

//! Returns a @ref relative_dates "relative date/time value" in microseconds based on the integer argument passed to be used in date arithmetic
/**
    @param us the number of microseconds to return

    @return a @ref relative_dates "relative date/time value" in microseconds based on the integer argument passed to be used in date arithmetic

    @par Example:
    @code{.py}
date rd = microseconds(100);
    @endcode

    @see
    - years(softint)
    - months(softint)
    - days(softint)
    - hours(softint)
    - minutes(softint)
    - seconds(softint)
    - milliseconds(softint)
*/
date microseconds(softint us) [flags=CONSTANT] {
   int64 secs = us / 10000000;
   us -= (secs * 10000000);
   int64 minutes = secs / 60;
   secs -= (minutes * 60);
   int64 hours = minutes / 60;
   minutes -= (hours * 60);

   return DateTimeNode::makeRelative(0, 0, 0, hours, minutes, secs, (int)us);
}

//! Returns an integer corresponding to the literal year value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the year value in the date

    @par Example:
    @code{.py}
int n = get_years(dt);
    @endcode

    @note equivalent to pseudo-method <date>::years()
*/
int get_years(date dt) [flags=CONSTANT] {
   return dt->getYear();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_years() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal month value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the month value in the date

    @par Example:
    @code{.py}
int n = get_months(dt);
    @endcode

    @note equivalent to pseudo-method <date>::months()
*/
int get_months(date dt) [flags=CONSTANT] {
   return dt->getMonth();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_months() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal day value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the day value in the date

    @par Example:
    @code{.py}
int n = get_days(dt);
    @endcode

    @note equivalent to pseudo-method <date>::days()
*/
int get_days(date dt) [flags=CONSTANT] {
   return dt->getDay();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_days() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal hour value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the hour value in the date

    @par Example:
    @code{.py}
int n = get_hours(dt);
    @endcode

    @note equivalent to pseudo-method <date>::hours()
*/
int get_hours(date dt) [flags=CONSTANT] {
   return dt->getHour();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_hours() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal minute value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the minute value in the date

    @par Example:
    @code{.py}
int n = get_minutes(dt);
    @endcode

    @note equivalent to pseudo-method <date>::minutes()
*/
int get_minutes(date dt) [flags=CONSTANT] {
   return dt->getMinute();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_minutes() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal second value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the literal second value in the date (does not calculate a duration)

    @par Example:
    @code{.py}
int n = get_seconds(dt);
    @endcode

    @note
    - equivalent to pseudo-method <date>::seconds()
    - to get the number of seconds of duration in a date/time value, use @ref get_duration_seconds() instead
*/
int get_seconds(date dt) [flags=CONSTANT] {
   return dt->getSecond();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_seconds() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal millisecond value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the literal millisecond value in the date (does not calculate a duration)

    @par Example:
    @code{.py}
int n = get_milliseconds(dt);
    @endcode

    @note
    - equivalent to pseudo-method <date>::milliseconds()
    - to get the number of milliseconds of duration in a date/time value, use @ref get_duration_milliseconds() instead
*/
int get_milliseconds(date dt) [flags=CONSTANT] {
   return dt->getMillisecond();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_milliseconds() [flags=RUNTIME_NOOP] {
}

//! Returns an integer corresponding to the literal microsecond value in the date (does not calculate a duration)
/**
    @param dt the date value; can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date

    @return an integer corresponding to the literal microsecond value in the date (does not calculate a duration)

    @par Example:
    @code{.py}
int n = get_microseconds(dt);
    @endcode

    @note
    - equivalent to pseudo-method <date>::microseconds()
    - to get the number of microseconds of duration in a date/time value, use @ref get_duration_microseconds() instead
*/
int get_microseconds(date dt) [flags=CONSTANT] {
   return dt->getMicrosecond();
}

//! Returns midnight on the date passed (strips the time component on the new value)
/**
    @param dt the date to process

    @return midnight on the date passed (strips the time component on the new value)

    @par Example:
    @code{.py}
date midnight = get_midnight(dt);
    @endcode

    @note equivalent to pseudo-method <date>::midnight()
*/
date get_midnight(date dt) [flags=CONSTANT] {
   DateTimeNode *rv = new DateTimeNode(*dt);
   rv->setTime(0, 0, 0);
   return rv;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing get_midnight() [flags=RUNTIME_NOOP] {
}

//! Returns an integer representing the ordinal day number in the year (corresponding to the <a href="http://en.wikipedia.org/wiki/ISO_8601#Ordinal_dates">ISO-8601 day number</a>) for the @ref absolute_dates "absolute date" value passed
/**
    @param dt an @ref absolute_dates "absolute date" value to get the ordinal day number

    @return an integer representing the ordinal day number in the year (corresponding to the <a href="http://en.wikipedia.org/wiki/ISO_8601#Ordinal_dates">ISO-8601 day number</a>) for the @ref absolute_dates "absolute date" value passed; if a @ref relative_dates "relative date" value is passed, then this function will return 0

    @deprecated use get_day_number(); camel-case function names were deprecated in %Qore 0.8.12
*/
int getDayNumber(date dt) [flags=CONSTANT,DEPRECATED] {
   return dt->getDayNumber();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getDayNumber() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns an integer representing the ordinal day number in the year (corresponding to the <a href="http://en.wikipedia.org/wiki/ISO_8601#Ordinal_dates">ISO-8601 day number</a>) for the @ref absolute_dates "absolute date" value passed
/**
    @param dt an @ref absolute_dates "absolute date" value to get the ordinal day number

    @return an integer representing the ordinal day number in the year (corresponding to the <a href="http://en.wikipedia.org/wiki/ISO_8601#Ordinal_dates">ISO-8601 day number</a>) for the @ref absolute_dates "absolute date" value passed; if a @ref relative_dates "relative date" value is passed, then this function will return 0

    @par Example:
    @code{.py}
int dn = get_day_number(dt);
    @endcode

    @note equivalent to <date>::dayNumber()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getDayNumber()
*/
int get_day_number(date dt) [flags=CONSTANT] {
   return dt->getDayNumber();
}

//! Returns an integer representing the day of the week for the @ref absolute_dates "absolute date" value passed (0=Sunday, 6=Saturday)
/**
    @param dt an @ref absolute_dates "absolute date" value to get the number for the day of the week

    @return an integer representing the day of the week for the @ref absolute_dates "absolute date" value passed (0=Sunday, 6=Saturday); if a @ref relative_dates "relative date" value is passed, then this function will return 0

    @see get_iso_day_of_week()

    @deprecated use get_day_of_week(); camel-case function names were deprecated in %Qore 0.8.12
*/
int getDayOfWeek(date dt) [flags=CONSTANT,DEPRECATED] {
   return dt->getDayOfWeek();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getDayOfWeek() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns an integer representing the day of the week for the @ref absolute_dates "absolute date" value passed (0=Sunday, 6=Saturday)
/**
    @param dt an @ref absolute_dates "absolute date" value to get the number for the day of the week

    @return an integer representing the day of the week for the @ref absolute_dates "absolute date" value passed (0=Sunday, 6=Saturday); if a @ref relative_dates "relative date" value is passed, then this function will return 0

    @par Example:
    @code{.py}
int dn = get_day_of_week(dt);
    @endcode

    @note equivalent to <date>::dayOfWeek()

    @see get_iso_day_of_week()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getDayOfWeek()
*/
int get_day_of_week(date dt) [flags=CONSTANT] {
   return dt->getDayOfWeek();
}

//! Returns an integer representing the ISO-8601 day of the week for the @ref absolute_dates "absolute date" value passed (1=Monday, 7=Sunday)
/**
    @param dt an @ref absolute_dates "absolute date" value to get the number for the day of the week

    @return an integer representing the day of the week for the @ref absolute_dates "absolute date" value passed (1=Monday, 7=Sunday); if a @ref relative_dates "relative date" value is passed, then this function will return 7

    @see get_day_of_week()

    @deprecated use get_iso_day_of_week(); camel-case function names were deprecated in %Qore 0.8.12
*/
int getISODayOfWeek(date dt) [flags=CONSTANT,DEPRECATED] {
   int d = dt->getDayOfWeek();
   return (!d ? 7 : d);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getISODayOfWeek() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns an integer representing the ISO-8601 day of the week for the @ref absolute_dates "absolute date" value passed (1=Monday, 7=Sunday)
/**
    @param dt an @ref absolute_dates "absolute date" value to get the number for the day of the week

    @return an integer representing the day of the week for the @ref absolute_dates "absolute date" value passed (1=Monday, 7=Sunday); if a @ref relative_dates "relative date" value is passed, then this function will return 7

    @par Example:
    @code{.py}
int dn = get_iso_day_of_week(dt);
    @endcode

    @note equivalent to <date>::isoDayOfWeek()

    @see get_day_of_week()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getISODayOfWeek()
*/
int get_iso_day_of_week(date dt) [flags=CONSTANT] {
   int d = dt->getDayOfWeek();
   return (!d ? 7 : d);
}

//! Returns an @ref IsoWeekInfo "IsoWeekInfo" hash representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (hash keys: \c "year", \c "week", \c "day")
/** @note that the ISO-8601 year does not always correspond with the calendar year at the end and the beginning of every year (for example 2006-01-01 in ISO-8601 calendar week format is: year=2005, week=52, day=7)

    @param dt the date to get information for

    @return an @ref IsoWeekInfo "IsoWeekInfo" hash representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (hash keys: \c "year", \c "week", \c "day"); if a @ref relative_dates "relative date" value is passed, then this function will return year=1970, week=1, day=1

    @see get_iso_week_string()

    @deprecated use get_iso_week_hash(); camel-case function names were deprecated in %Qore 0.8.12
*/
hash<IsoWeekInfo> getISOWeekHash(date dt) [flags=CONSTANT,DEPRECATED] {
   int year, week, day;
   dt->getISOWeek(year, week, day);

   QoreHashNode* h = new QoreHashNode(hashdeclIsoWeekInfo, xsink);
   qore_hash_private* ph = qore_hash_private::get(*h);
   ph->setKeyValueIntern("year", year);
   ph->setKeyValueIntern("week", week);
   ph->setKeyValueIntern("day", day);

   return h;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getISOWeekHash() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns an @ref IsoWeekInfo "IsoWeekInfo" hash representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (hash keys: \c "year", \c "week", \c "day")
/** @note that the ISO-8601 year does not always correspond with the calendar year at the end and the beginning of every year (for example 2006-01-01 in ISO-8601 calendar week format is: year=2005, week=52, day=7)

    @param dt the date to get information for

    @return an @ref IsoWeekInfo "IsoWeekInfo" hash representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (hash keys: \c "year", \c "week", \c "day"); if a @ref relative_dates "relative date" value is passed, then this function will return year=1970, week=1, day=1

    @par Example:
    @code{.py}
hash<IsoWeekInfo> h = get_iso_week_hash(2007-05-15); # returns year=2007, week=20, day=2
    @endcode

    @note equivalent to <date>::isoWeekHash()

    @see get_iso_week_string()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getISOWeekHash()
*/
hash<IsoWeekInfo> get_iso_week_hash(date dt) [flags=CONSTANT] {
    int year, week, day;
    dt->getISOWeek(year, week, day);

    QoreHashNode* h = new QoreHashNode(hashdeclIsoWeekInfo, xsink);
    qore_hash_private* ph = qore_hash_private::get(*h);
    ph->setKeyValueIntern("year", year);
    ph->setKeyValueIntern("week", week);
    ph->setKeyValueIntern("day", day);

    return h;
}

//! Returns a string representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (ex: 2006-01-01 = "2005-W52-7")
/**
    @param dt the date to get information for

    @return a string representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (ex: 2006-01-01 = "2005-W52-7"); if a @ref relative_dates "relative date" value is passed, then this function will return \c "1970-W01-1"

    @par Example:
    @code{.py}
string str = getISOWeekString(2007-05-15); # returns "2007-W20-2"
    @endcode

    @see get_iso_week_hash()

    @deprecated use get_iso_week_string(); camel-case function names were deprecated in %Qore 0.8.12
*/
string getISOWeekString(date dt) [flags=CONSTANT,DEPRECATED] {
   int year, week, day;
   dt->getISOWeek(year, week, day);
   QoreStringNode *str = new QoreStringNode();
   str->sprintf("%04d-W%02d-%d", year, week, day);
   return str;
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing getISOWeekString() [flags=RUNTIME_NOOP,DEPRECATED] {
}

//! Returns a string representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (ex: 2006-01-01 = "2005-W52-7")
/**
    @param dt the date to get information for

    @return a string representing the ISO-8601 calendar week information for the @ref absolute_dates "absolute date" passed (ex: 2006-01-01 = "2005-W52-7"); if a @ref relative_dates "relative date" value is passed, then this function will return \c "1970-W01-1"

    @par Example:
    @code{.py}
string str = get_iso_week_string(2007-05-15); # returns "2007-W20-2"
    @endcode

    @note equivalent to <date>::isoWeekString()

    @see get_iso_week_hash()

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getISOWeekString()
*/
string get_iso_week_string(date dt) [flags=CONSTANT] {
   int year, week, day;
   dt->getISOWeek(year, week, day);
   QoreStringNode *str = new QoreStringNode();
   str->sprintf("%04d-W%02d-%d", year, week, day);
   return str;
}

//! Retuns an @ref absolute_dates "absolute date value" for the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 calendar week information</a> passed (year, week number, optional: weekday, where 1=Monday, 7=Sunday) in the current time zone
/** Throws an exception if the arguments are invalid

    @param year the ISO-8601 year (which for some special days does not always correspond to the actual calendar year)
    @param week the ISO-8601 week number; if this argument is invalid, an \c ISO-8601-INVALID-WEEK exception is thrown
    @param day the day of the week, where 1=Monday and 7=Sunday; if this argument is invalid, an \c ISO-8601-INVALID-DAY exception is thrown

    @return an @ref absolute_dates "absolute date value" for the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 calendar week information</a> passed (year, week number, optional: weekday, where 1=Monday, 7=Sunday) in the current time zone

    @throw ISO-8601-INVALID-WEEK The week number is not valid for the given year
    @throw ISO-8601-INVALID-DAY The day number is not between 1 (Monday) and 7 (Sunday) inclusive

    @deprecated use get_date_from_iso_week(); camel-case function names were deprecated in %Qore 0.8.12
*/
date getDateFromISOWeek(softint year, softint week, softint day = 1) [flags=RET_VALUE_ONLY,DEPRECATED] {
   return DateTimeNode::getDateFromISOWeek(year, week, day, xsink);
}

//! Retuns an @ref absolute_dates "absolute date value" for the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 calendar week information</a> passed (year, week number, optional: weekday, where 1=Monday, 7=Sunday) in the current time zone
/** Throws an exception if the arguments are invalid

    @param year the ISO-8601 year (which for some special days does not always correspond to the actual calendar year)
    @param week the ISO-8601 week number; if this argument is invalid, an \c ISO-8601-INVALID-WEEK exception is thrown
    @param day the day of the week, where 1=Monday and 7=Sunday; if this argument is invalid, an \c ISO-8601-INVALID-DAY exception is thrown

    @return an @ref absolute_dates "absolute date value" for the <a href="http://en.wikipedia.org/wiki/ISO_week_date">ISO-8601 calendar week information</a> passed (year, week number, optional: weekday, where 1=Monday, 7=Sunday) in the current time zone

    @par Example:
    @code{.py}
date d = getDateFromISOWeek(2007, 3); # returns 2007-01-15
    @endcode

    @throw ISO-8601-INVALID-WEEK The week number is not valid for the given year
    @throw ISO-8601-INVALID-DAY The day number is not between 1 (Monday) and 7 (Sunday) inclusive

    @since %Qore 0.8.12 as a replacement for deprecated camel-case getDateFromISOWeek()
*/
date get_date_from_iso_week(softint year, softint week, softint day = 1) [flags=RET_VALUE_ONLY] {
   return DateTimeNode::getDateFromISOWeek(year, week, day, xsink);
}

//! Returns an integer representing the system time in milliseconds (1/1000 second intervals since Jan 1, 1970 00:00)
/**
    @return an integer representing the system time in milliseconds (1/1000 second intervals since Jan 1, 1970 00:00)

    @par Example:
    @code{.py}
int ms = clock_getmillis();
    @endcode

    @see
    - clock_getnanos()
    - clock_getmicros()
*/
int clock_getmillis() [flags=CONSTANT] {
   return q_clock_getmillis();
}

//! Returns an integer representing the system time in nanoseconds (1/1000000000 second intervals) since Jan 1, 1970 00:00:00Z
/**
    @return an integer representing the system time in nanoseconds (1/1000000000 second intervals) since Jan 1, 1970 00:00:00Z

    @par Example:
    @code{.py}
int ns = clock_getnanos();
    @endcode

    @see
    - clock_getmillis()
    - clock_getmicros()
*/
int clock_getnanos() [flags=CONSTANT] {
   return q_clock_getnanos();
}

//! Returns an integer representing the system time in microseconds (1/1000000 second intervals) since Jan 1, 1970 00:00:00Z
/**
    @return an integer representing the system time in microseconds (1/1000000 second intervals) since Jan 1, 1970 00:00:00Z

    @par Example:
    @code{.py}
int us = clock_getmicros();
    @endcode

    @see
    - clock_getmillis()
    - clock_getmicros()
*/
int clock_getmicros() [flags=CONSTANT] {
   return q_clock_getmicros();
}

//! Converts an integer argument representing the offset in milliseconds from January 1, 1970 in the local time zone to a date in the local time zone
/** @param ms an integer argument representing the offset in milliseconds from January 1, 1970 in the local time zone

    @return a date in the local time zone corresponding to the argument passed which is an offset in milliseconds from January 1, 1970 in the local time zone

    @par Example:
    @code{.py}
date date = date_ms(1); # returns 1970-01-01T00:00:00.001 in the local time zone
    @endcode

    @see date_us(softint)
*/
date date_ms(softint ms) [flags=CONSTANT] {
   return new DateTimeNode(ms / 1000, (int)(ms % 1000));
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
nothing date_ms() [flags=RUNTIME_NOOP] {
}

//! Converts an integer argument representing the offset in microseconds from January 1, 1970 in the local time zone to a date in the local time zone
/** @param us an integer argument representing the offset in microseconds from January 1, 1970 in the local time zone

    @return a date in the local time zone corresponding to the argument passed which is an offset in microseconds from January 1, 1970 in the local time zone

    @par Example:
    @code{.py}
date date = date_us(1); # returns 1970-01-01T00:00:00.000001 in the local time zone
    @endcode

    @see date_ms(softint)
*/
date date_us(softint us) [flags=CONSTANT] {
   return DateTimeNode::makeAbsoluteLocal(currentTZ(), us / 1000000, (int)(us % 1000000));
}

//! Returns an integer value representing the the number of seconds of duration in the value of the date passed (can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date)
/** The duration in seconds is calculated and any fractional seconds are truncated (no rounding is performed)

    @param dt a @ref relative_dates "relative" or @ref absolute_dates "absolute" date argument

    @return an integer value representing the the number of seconds of duration in the value of the date passed; if the argument is a @ref relative_dates "relative date", the value is normalized to seconds and the integer seconds value is returned, if the argument is an @ref absolute_dates "absolute date", the duration in seconds is calculated from the present time; so if the present time is sent as an argument, 0 is returned, if a future date is used, the number of seconds from the present time to the future date is returned, if an @ref absolute_dates "absolute date" in the past is passed as an argument, also 0 is returned (the function does not calculate second differences for @ref absolute_dates "absolute dates" in the past (for this use @ref date_time_arithmetic instead); this function can only return a negative value if passed a relative date/time value

    @par Example:
    @code{.py}
int us = get_duration_seconds(PT2M15S3u); # returns 135
    @endcode

    @note
    - equivalent to <date>::durationSeconds()
    - to get a similar floating-point value with microseconds providing the digits to the right of the decimal place, use @ref get_duration_seconds_f()
    - to get the literal seconds integer value from a date/time value without calculating a duration, use @ref get_seconds(date)

    @see
    - get_duration_seconds_f()
    - get_duration_milliseconds()
    - get_duration_microseconds()
*/
int get_duration_seconds(date dt) [flags=CONSTANT] {
   return dt->getRelativeSeconds();
}

//! Returns an integer value representing the the number of milliseconds of duration in the value of the date passed (can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date)
/** The duration in milliseconds is calculated and any fractional milliseconds are truncated (no rounding is performed)

    @param dt a @ref relative_dates "relative" or @ref absolute_dates "absolute" date argument

    @return an integer value representing the the number of milliseconds of duration in the value of the date passed; if the argument is a @ref relative_dates "relative date", the value is normalized to milliseconds and the integer milliseconds value is returned, if the argument is an @ref absolute_dates "absolute date", the duration in milliseconds is calculated from the present time; so if the present time is sent as an argument, 0 is returned, if a future date is used, the number of milliseconds from the present time to the future date is returned, if an @ref absolute_dates "absolute date" in the past is passed as an argument, also 0 is returned (the function does not calculate millisecond differences for @ref absolute_dates "absolute dates" in the past (for this use @ref date_time_arithmetic instead); this function can only return a negative value if passed a relative date/time value

    @par Example:
    @code{.py}
int ms = get_duration_milliseconds(PT2M15S3u); # returns 135000
    @endcode

    @note
    - equivalent to <date>::durationMilliseconds()
    - to get the literal milliseconds integer value from a date/time value without calculating a duration, use @ref get_milliseconds(date)

    @see
    - get_duration_seconds_f()
    - get_duration_seconds()
    - get_duration_microseconds()
*/
int get_duration_milliseconds(date dt) [flags=CONSTANT] {
   return dt->getRelativeMilliseconds();
}

//! Returns an integer value representing the the number of microseconds of duration in the value of the date passed (can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date)
/**
    @param dt a @ref relative_dates "relative" or @ref absolute_dates "absolute" date argument

    @return an integer value representing the the number of microseconds of duration in the value of the date passed; if the argument is a @ref relative_dates "relative date", the value is normalized to microseconds and the integer microseconds value is returned, if the argument is an @ref absolute_dates "absolute date", the duration in microseconds is calculated from the present time; so if the present time is sent as an argument, 0 is returned, if a future date is used, the number of microseconds from the present time to the future date is returned, if an @ref absolute_dates "absolute date" in the past is passed as an argument, also 0 is returned (the function does not calculate microsecond differences for @ref absolute_dates "absolute dates" in the past (for this use @ref date_time_arithmetic instead); this function can only return a negative value if passed a relative date/time value

    @par Example:
    @code{.py}
int us = get_duration_microseconds(PT2M15S3u); # returns 135000003
    @endcode

    @note
    - equivalent to <date>::durationMicroseconds()
    - to get the literal microseconds integer value from a date/time value without calculating a duration, use @ref get_microseconds(date)

    @see
    - get_duration_seconds_f()
    - get_duration_seconds()
    - get_duration_milliseconds()
*/
int get_duration_microseconds(date dt) [flags=CONSTANT] {
   return dt->getRelativeMicroseconds();
}

//! Returns a floating-point value representing the the number of seconds of duration in the value of the date passed (can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date)
/** The duration in seconds is calculated and any fractional seconds are returned as digits to the right of the decimal point.

    @param dt a @ref relative_dates "relative" or @ref absolute_dates "absolute" date argument

    @return a floating-point value representing the the number of seconds of duration in the value of the date passed; if the argument is a @ref relative_dates "relative date", the value is normalized to seconds and the floating-point seconds value is returned with microseconds providing the digits to the right of the decimal place, if the argument is an @ref absolute_dates "absolute date", the duration in seconds is calculated from the present time; so if the present time is sent as an argument, 0 is returned, if a future date is used, the number of seconds from the present time to the future date is returned, if an @ref absolute_dates "absolute date" in the past is passed as an argument, also 0 is returned (the function does not calculate second differences for @ref absolute_dates "absolute dates" in the past (for this use @ref date_time_arithmetic instead); this function can only return a negative value if passed a relative date/time value

    @par Example:
    @code{.py}
float dt = get_duration_seconds_f(PT2M15S3u); # returns 135.000003
    @endcode

    @note
    - equivalent to <date>::durationSecondsFloat()
    - to get the literal seconds integer value from a date/time value without calculating a duration, use @ref get_seconds(date)

    @see
    - get_duration_seconds()
    - get_duration_milliseconds()
    - get_duration_microseconds()

    @since %Qore 0.8.12
*/
float get_duration_seconds_f(date dt) [flags=CONSTANT] {
   return dt->getRelativeSecondsDouble();
}

//! Returns a @ref DateTimeInfo hash for the given date argument (can be either a @ref relative_dates "relative" or @ref absolute_dates "absolute" date)
/**
    @param dt the date to return information for

    @return a @ref DateTimeInfo hash for the given date argument

    @par Example:
    @code{.py}
hash<DateTimeInfo> h = date_info(date);
    @endcode

    @note equivalent to <date>::info()
*/
hash<DateTimeInfo> date_info(date dt) [flags=CONSTANT] {
   return date_info(*dt);
}

//! Returns a @ref DateTimeInfo hash for the current date and time
/**
    @return a @ref DateTimeInfo hash for the current date and time

    @par Example:
    @code{.py}
hash<DateTimeInfo> h = date_info();
    @endcode
*/
hash<DateTimeInfo> date_info() [flags=CONSTANT] {
   int us;
   int64 seconds = q_epoch_us(us);
   DateTime d;
   d.setDate(currentTZ(), seconds, us);
   return date_info(d);
}

//! Returns @ref True if the argument is an @ref relative_dates "relative date/time value", @ref False if not
/**
    @param dt the date to check

    @return @ref True if the argument is an @ref relative_dates "relative date/time value", @ref False if not

    @par Example:
    @code{.py}
bool b = is_date_relative(dt)
    @endcode

    @see is_date_absolute()
*/
bool is_date_relative(date dt) [flags=CONSTANT] {
   return dt->isRelative();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
bool is_date_relative() [flags=NOOP] {
   return false;
}

//! Returns @ref True if the argument is an @ref absolute_dates "absolute date/time value", @ref False if not
/**
    @param dt the date to check

    @return @ref True if the argument is an @ref absolute_dates "absolute date/time value", @ref False if not

    @par Example:
    @code{.py}
bool b = is_date_absolute(dt)
    @endcode

    @see is_date_relative()
*/
bool is_date_absolute(date dt) [flags=CONSTANT] {
   return dt->isAbsolute();
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
bool is_date_absolute() [flags=NOOP] {
   return false;
}

//! Returns the date passed
/** This function is included because the date() function is used to convert values to dates (similar to a C/C++ cast)

    @param dt the date to return

    @return the date passed
 */
date date(date dt) [flags=CONSTANT] {
  return dt->refSelf();
}

//! Converts the argument to a date and returns the date
/**
    @param dtstr string a string to bo converted literally to an @ref absolute_dates "absolute date/time value"; if no time zone information is present, the local time zone is assumed

    @return the @ref absolute_dates "absolute date/time value" corresponding to the string argument passed

    @par Example:
    @code{.py}
date d = date("2001-01-01T15:35:23"); # returns 2001-01-01 15:35:23 Mon +01:00 (CET)
date d = date("20010101 15:35:23Z"); # returns 2001-01-01 15:35:23 Mon Z (UTC)
date d = date("20010101 153523Z"); # returns 2001-01-01 15:35:23 Mon Z (UTC)
date d = date("20010101 153523-02"); # returns 2001-01-01 15:35:23 Mon -02:00 (-02:00)
date d = date("20010101 153523-02:00:00"); # returns 2001-01-01 15:35:23 Mon -02:00 (-02:00)
date d = date("2001-01-01-153523-020000"); # returns 2001-01-01 15:35:23 Mon -02:00 (-02:00)
date d = date("20010101-153523"); # returns 2001-01-01 15:35:23 Mon +01:00 (CET)
    @endcode

    @throw INVALID-DATE thrown if the date is invalid; invalid or missing date components

    @see date(string, string)

    @since %Qore 0.8.12.4 \c INVALID-DATE exceptions are thrown if the date is invalid
 */
date date(string dtstr) [flags=CONSTANT] {
   DateTimeNodeValueHelper date(dtstr, xsink);
   return *xsink ? 0 : date.getReferencedValue();
}

//! The argument is assumed to be the number of seconds and fractions of a second
/**
    @param f the number of seconds and fractions of a second

    @return a @ref relative_dates "relative date/time value" corresponding to the number of seconds and fractions of a second in the argument

    @par Example:
    @code{.py}
date d = date(f);
    @endcode

    @since %Qore 0.8.13 the return value is a @ref relative_dates "relative date/time value" in seconds
 */
date date(softfloat f) [flags=CONSTANT] {
   return DateTimeNode::makeRelativeFromSeconds((int64)f, (int)((f - (double)((int)f)) * 1000000));
}

//! The argument is assumed to be the number of seconds
/**
    @param i the number of seconds

    @return a @ref relative_dates "relative date/time value" corresponding to the number of seconds

    @par Example:
    @code{.py}
date d = date(i);
    @endcode

    @since %Qore 0.8.13 the return value is a @ref relative_dates "relative date/time value" in seconds
 */
date date(int i) [flags=CONSTANT] {
   return DateTimeNode::makeRelativeFromSeconds(i);
}

//! This function just returns 1970-01-01Z
/** This function is included because the date() function is used to convert values to dates (similar to a C/C++ cast)

    @return 1970-01-01Z
 */
date date() [flags=CONSTANT] {
   return zero_date();
}

//! This function just returns 1970-01-01Z
/** This function is included because the date() function is used to convert values to dates (similar to a C/C++ cast)

    @return 1970-01-01Z
 */
date date(null[doc] null) [flags=CONSTANT] {
   return zero_date();
}

//! Returns the @ref date "date/time" value corresponding to parsing a string argument according to a @ref date_mask "format mask"
/**
    @param dtstr a string giving a date
    @param mask the mask for the date value; see @ref date_mask for information on the format of the @ref date_mask "format mask"

    @return the @ref date "date/time" value corresponding to parsing the \a dtstr string argument according to \a mask serving as a @ref date_mask "format mask"

    @par Example:
    @code{.py}
date dt = date("20100401 234520 230394", "YYYYMMDD HHmmSS ssssss"); # returns 2010-04-01T23:45:20.230394
    @endcode

    @throw INVALID-DATE invalid mask specification

    @see @ref Qore::TimeZone::date(string, string) for a method that allows the date to be parsed in a particular @ref time_zones "time zone"
*/
date date(string dtstr, string mask) [flags=RET_VALUE_ONLY] {
   return make_date_with_mask(currentTZ(), *dtstr, *mask, xsink);
}
///@}
