| 1 | //===----------------------------------------------------------------------===// |
| 2 | // DuckDB |
| 3 | // |
| 4 | // duckdb/function/scalar/strftime.hpp |
| 5 | // |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #pragma once |
| 10 | |
| 11 | #include "duckdb/function/scalar_function.hpp" |
| 12 | #include "duckdb/common/vector.hpp" |
| 13 | |
| 14 | #include <algorithm> |
| 15 | |
| 16 | namespace duckdb { |
| 17 | |
| 18 | enum class StrTimeSpecifier : uint8_t { |
| 19 | ABBREVIATED_WEEKDAY_NAME = 0, // %a - Abbreviated weekday name. (Sun, Mon, ...) |
| 20 | FULL_WEEKDAY_NAME = 1, // %A Full weekday name. (Sunday, Monday, ...) |
| 21 | WEEKDAY_DECIMAL = 2, // %w - Weekday as a decimal number. (0, 1, ..., 6) |
| 22 | DAY_OF_MONTH_PADDED = 3, // %d - Day of the month as a zero-padded decimal. (01, 02, ..., 31) |
| 23 | DAY_OF_MONTH = 4, // %-d - Day of the month as a decimal number. (1, 2, ..., 30) |
| 24 | ABBREVIATED_MONTH_NAME = 5, // %b - Abbreviated month name. (Jan, Feb, ..., Dec) |
| 25 | FULL_MONTH_NAME = 6, // %B - Full month name. (January, February, ...) |
| 26 | MONTH_DECIMAL_PADDED = 7, // %m - Month as a zero-padded decimal number. (01, 02, ..., 12) |
| 27 | MONTH_DECIMAL = 8, // %-m - Month as a decimal number. (1, 2, ..., 12) |
| 28 | YEAR_WITHOUT_CENTURY_PADDED = 9, // %y - Year without century as a zero-padded decimal number. (00, 01, ..., 99) |
| 29 | YEAR_WITHOUT_CENTURY = 10, // %-y - Year without century as a decimal number. (0, 1, ..., 99) |
| 30 | YEAR_DECIMAL = 11, // %Y - Year with century as a decimal number. (2013, 2019 etc.) |
| 31 | HOUR_24_PADDED = 12, // %H - Hour (24-hour clock) as a zero-padded decimal number. (00, 01, ..., 23) |
| 32 | HOUR_24_DECIMAL = 13, // %-H - Hour (24-hour clock) as a decimal number. (0, 1, ..., 23) |
| 33 | HOUR_12_PADDED = 14, // %I - Hour (12-hour clock) as a zero-padded decimal number. (01, 02, ..., 12) |
| 34 | HOUR_12_DECIMAL = 15, // %-I - Hour (12-hour clock) as a decimal number. (1, 2, ... 12) |
| 35 | AM_PM = 16, // %p - Locale’s AM or PM. (AM, PM) |
| 36 | MINUTE_PADDED = 17, // %M - Minute as a zero-padded decimal number. (00, 01, ..., 59) |
| 37 | MINUTE_DECIMAL = 18, // %-M - Minute as a decimal number. (0, 1, ..., 59) |
| 38 | SECOND_PADDED = 19, // %S - Second as a zero-padded decimal number. (00, 01, ..., 59) |
| 39 | SECOND_DECIMAL = 20, // %-S - Second as a decimal number. (0, 1, ..., 59) |
| 40 | MICROSECOND_PADDED = 21, // %f - Microsecond as a decimal number, zero-padded on the left. (000000 - 999999) |
| 41 | MILLISECOND_PADDED = 22, // %g - Millisecond as a decimal number, zero-padded on the left. (000 - 999) |
| 42 | UTC_OFFSET = 23, // %z - UTC offset in the form +HHMM or -HHMM. ( ) |
| 43 | TZ_NAME = 24, // %Z - Time zone name. ( ) |
| 44 | DAY_OF_YEAR_PADDED = 25, // %j - Day of the year as a zero-padded decimal number. (001, 002, ..., 366) |
| 45 | DAY_OF_YEAR_DECIMAL = 26, // %-j - Day of the year as a decimal number. (1, 2, ..., 366) |
| 46 | WEEK_NUMBER_PADDED_SUN_FIRST = |
| 47 | 27, // %U - Week number of the year (Sunday as the first day of the week). All days in a new year preceding the |
| 48 | // first Sunday are considered to be in week 0. (00, 01, ..., 53) |
| 49 | WEEK_NUMBER_PADDED_MON_FIRST = |
| 50 | 28, // %W - Week number of the year (Monday as the first day of the week). All days in a new year preceding the |
| 51 | // first Monday are considered to be in week 0. (00, 01, ..., 53) |
| 52 | LOCALE_APPROPRIATE_DATE_AND_TIME = |
| 53 | 29, // %c - Locale’s appropriate date and time representation. (Mon Sep 30 07:06:05 2013) |
| 54 | LOCALE_APPROPRIATE_DATE = 30, // %x - Locale’s appropriate date representation. (09/30/13) |
| 55 | LOCALE_APPROPRIATE_TIME = 31 // %X - Locale’s appropriate time representation. (07:06:05) |
| 56 | }; |
| 57 | |
| 58 | struct StrTimeFormat { |
| 59 | public: |
| 60 | virtual ~StrTimeFormat() { |
| 61 | } |
| 62 | |
| 63 | DUCKDB_API static string ParseFormatSpecifier(const string &format_string, StrTimeFormat &format); |
| 64 | |
| 65 | inline bool HasFormatSpecifier(StrTimeSpecifier s) const { |
| 66 | return std::find(first: specifiers.begin(), last: specifiers.end(), val: s) != specifiers.end(); |
| 67 | } |
| 68 | |
| 69 | //! The full format specifier, for error messages |
| 70 | string format_specifier; |
| 71 | |
| 72 | protected: |
| 73 | //! The format specifiers |
| 74 | vector<StrTimeSpecifier> specifiers; |
| 75 | //! The literals that appear in between the format specifiers |
| 76 | //! The following must hold: literals.size() = specifiers.size() + 1 |
| 77 | //! Format is literals[0], specifiers[0], literals[1], ..., specifiers[n - 1], literals[n] |
| 78 | vector<string> literals; |
| 79 | //! The constant size that appears in the format string |
| 80 | idx_t constant_size = 0; |
| 81 | //! The max numeric width of the specifier (if it is parsed as a number), or -1 if it is not a number |
| 82 | vector<int> numeric_width; |
| 83 | |
| 84 | protected: |
| 85 | void AddLiteral(string literal); |
| 86 | DUCKDB_API virtual void AddFormatSpecifier(string preceding_literal, StrTimeSpecifier specifier); |
| 87 | }; |
| 88 | |
| 89 | struct StrfTimeFormat : public StrTimeFormat { |
| 90 | DUCKDB_API idx_t GetLength(date_t date, dtime_t time, int32_t utc_offset, const char *tz_name); |
| 91 | |
| 92 | DUCKDB_API void FormatString(date_t date, int32_t data[8], const char *tz_name, char *target); |
| 93 | void FormatString(date_t date, dtime_t time, char *target); |
| 94 | |
| 95 | DUCKDB_API static string Format(timestamp_t timestamp, const string &format); |
| 96 | |
| 97 | DUCKDB_API void ConvertDateVector(Vector &input, Vector &result, idx_t count); |
| 98 | DUCKDB_API void ConvertTimestampVector(Vector &input, Vector &result, idx_t count); |
| 99 | |
| 100 | protected: |
| 101 | //! The variable-length specifiers. To determine total string size, these need to be checked. |
| 102 | vector<StrTimeSpecifier> var_length_specifiers; |
| 103 | //! Whether or not the current specifier is a special "date" specifier (i.e. one that requires a date_t object to |
| 104 | //! generate) |
| 105 | vector<bool> is_date_specifier; |
| 106 | |
| 107 | protected: |
| 108 | DUCKDB_API void AddFormatSpecifier(string preceding_literal, StrTimeSpecifier specifier) override; |
| 109 | static idx_t GetSpecifierLength(StrTimeSpecifier specifier, date_t date, dtime_t time, int32_t utc_offset, |
| 110 | const char *tz_name); |
| 111 | char *WriteString(char *target, const string_t &str); |
| 112 | char *Write2(char *target, uint8_t value); |
| 113 | char *WritePadded2(char *target, uint32_t value); |
| 114 | char *WritePadded3(char *target, uint32_t value); |
| 115 | char *WritePadded(char *target, uint32_t value, size_t padding); |
| 116 | bool IsDateSpecifier(StrTimeSpecifier specifier); |
| 117 | char *WriteDateSpecifier(StrTimeSpecifier specifier, date_t date, char *target); |
| 118 | char *WriteStandardSpecifier(StrTimeSpecifier specifier, int32_t data[], const char *tz_name, size_t tz_len, |
| 119 | char *target); |
| 120 | }; |
| 121 | |
| 122 | struct StrpTimeFormat : public StrTimeFormat { |
| 123 | public: |
| 124 | //! Type-safe parsing argument |
| 125 | struct ParseResult { |
| 126 | int32_t data[8]; // year, month, day, hour, min, sec, µs, offset |
| 127 | string tz; |
| 128 | string error_message; |
| 129 | idx_t error_position = DConstants::INVALID_INDEX; |
| 130 | |
| 131 | date_t ToDate(); |
| 132 | timestamp_t ToTimestamp(); |
| 133 | |
| 134 | bool TryToDate(date_t &result); |
| 135 | bool TryToTimestamp(timestamp_t &result); |
| 136 | |
| 137 | DUCKDB_API string FormatError(string_t input, const string &format_specifier); |
| 138 | }; |
| 139 | |
| 140 | public: |
| 141 | DUCKDB_API static ParseResult Parse(const string &format, const string &text); |
| 142 | |
| 143 | DUCKDB_API bool Parse(string_t str, ParseResult &result); |
| 144 | |
| 145 | DUCKDB_API bool TryParseDate(string_t str, date_t &result, string &error_message); |
| 146 | DUCKDB_API bool TryParseTimestamp(string_t str, timestamp_t &result, string &error_message); |
| 147 | |
| 148 | date_t ParseDate(string_t str); |
| 149 | timestamp_t ParseTimestamp(string_t str); |
| 150 | |
| 151 | protected: |
| 152 | static string FormatStrpTimeError(const string &input, idx_t position); |
| 153 | DUCKDB_API void AddFormatSpecifier(string preceding_literal, StrTimeSpecifier specifier) override; |
| 154 | int NumericSpecifierWidth(StrTimeSpecifier specifier); |
| 155 | int32_t TryParseCollection(const char *data, idx_t &pos, idx_t size, const string_t collection[], |
| 156 | idx_t collection_count); |
| 157 | }; |
| 158 | |
| 159 | } // namespace duckdb |
| 160 | |