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 | |