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
16namespace duckdb {
17
18enum 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
58struct StrTimeFormat {
59public:
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
72protected:
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
84protected:
85 void AddLiteral(string literal);
86 DUCKDB_API virtual void AddFormatSpecifier(string preceding_literal, StrTimeSpecifier specifier);
87};
88
89struct 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
100protected:
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
107protected:
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
122struct StrpTimeFormat : public StrTimeFormat {
123public:
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
140public:
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
151protected:
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