22
22
#include < chrono>
23
23
#include < cmath>
24
24
#include < concepts>
25
+ #include < cstdint>
26
+ #include < iomanip>
25
27
26
28
#include " iceberg/exception.h"
27
29
@@ -35,6 +37,14 @@ int32_t MicrosToDays(int64_t micros_since_epoch) {
35
37
return static_cast <int32_t >(days_duration.count ());
36
38
}
37
39
40
+ time_t timegm_custom (std::tm* tm) {
41
+ #if defined(_WIN32)
42
+ return _mkgmtime (tm);
43
+ #else
44
+ return timegm (tm);
45
+ #endif
46
+ }
47
+
38
48
} // namespace
39
49
40
50
// / \brief LiteralCaster handles type casting operations for Literal.
@@ -111,6 +121,7 @@ Result<Literal> LiteralCaster::CastFromInt(
111
121
return Literal::Double (static_cast <double >(int_val));
112
122
case TypeId::kDate :
113
123
return Literal::Date (int_val);
124
+ // TODO(Li Feiyang): Implement cast from Int to decimal
114
125
default :
115
126
return NotSupported (" Cast from Int to {} is not implemented" ,
116
127
target_type->ToString ());
@@ -137,10 +148,10 @@ Result<Literal> LiteralCaster::CastFromLong(
137
148
case TypeId::kDouble :
138
149
return Literal::Double (static_cast <double >(long_val));
139
150
case TypeId::kDate : {
140
- if (long_val > static_cast < int64_t >( std::numeric_limits<int32_t >::max () )) {
151
+ if (long_val > std::numeric_limits<int32_t >::max ()) {
141
152
return AboveMaxLiteral (target_type);
142
153
}
143
- if (long_val < static_cast < int64_t >( std::numeric_limits<int32_t >::min () )) {
154
+ if (long_val < std::numeric_limits<int32_t >::min ()) {
144
155
return BelowMinLiteral (target_type);
145
156
}
146
157
return Literal::Date (static_cast <int32_t >(long_val));
@@ -151,6 +162,7 @@ Result<Literal> LiteralCaster::CastFromLong(
151
162
return Literal::Timestamp (long_val);
152
163
case TypeId::kTimestampTz :
153
164
return Literal::TimestampTz (long_val);
165
+ // TODO(Li Feiyang): Implement cast from Long to decimal, TimestampNs and
154
166
default :
155
167
return NotSupported (" Cast from Long to {} is not supported" ,
156
168
target_type->ToString ());
@@ -164,6 +176,7 @@ Result<Literal> LiteralCaster::CastFromFloat(
164
176
switch (target_type->type_id ()) {
165
177
case TypeId::kDouble :
166
178
return Literal::Double (static_cast <double >(float_val));
179
+ // TODO(Li Feiyang): Implement cast from Float to decimal
167
180
default :
168
181
return NotSupported (" Cast from Float to {} is not supported" ,
169
182
target_type->ToString ());
@@ -192,30 +205,129 @@ Result<Literal> LiteralCaster::CastFromDouble(
192
205
193
206
Result<Literal> LiteralCaster::CastFromString (
194
207
const Literal& literal, const std::shared_ptr<PrimitiveType>& target_type) {
208
+ const auto & str_val = std::get<std::string>(literal.value_ );
209
+ std::istringstream in{str_val};
210
+ std::tm tm = {};
211
+
195
212
switch (target_type->type_id ()) {
196
213
case TypeId::kDate : {
197
- // TODO(Li Feiyang): Implement parsing for "YYYY-MM-DD" using std::chrono::parse
198
- // once it becomes available in the target libc++.
199
- return NotImplemented (" Cast from String to Date is not yet implemented." );
214
+ // Parse "YYYY-MM-DD" into days since 1970-01-01 epoch.
215
+ in >> std::get_time (&tm, " %Y-%m-%d" );
216
+
217
+ if (in.fail () || tm.tm_mday == 0 || in.peek () != EOF) {
218
+ return NotSupported (" Failed to parse '{}' as a valid Date (expected YYYY-MM-DD)" ,
219
+ str_val);
220
+ }
221
+
222
+ auto time_point = std::chrono::system_clock::from_time_t (timegm_custom (&tm));
223
+ auto days_since_epoch = std::chrono::floor<std::chrono::days>(time_point);
224
+ return Literal::Date (
225
+ static_cast <int32_t >(days_since_epoch.time_since_epoch ().count ()));
200
226
}
201
227
202
228
case TypeId::kTime : {
203
- // TODO(Li Feiyang): Implement parsing for "HH:MM:SS.ffffff" using
204
- // std::chrono::parse once it becomes available in the target libc++.
205
- return NotImplemented (" Cast from String to Time is not yet implemented." );
206
- }
229
+ // Parse "HH:MM:SS.ffffff" into microseconds since midnight.
230
+ in >> std::get_time (&tm, " %H:%M:%S" );
207
231
208
- case TypeId::kTimestamp : {
209
- // TODO(Li Feiyang): Implement parsing for "YYYY-MM-DDTHH:MM:SS.ffffff" using
210
- // std::chrono::parse once it becomes available in the target libc++.
211
- return NotImplemented (" Cast from String to Timestamp is not yet implemented." );
232
+ if (in.fail ()) {
233
+ return NotSupported (
234
+ " Failed to parse '{}' as a valid Time (expected HH:MM:SS.ffffff)" , str_val);
235
+ }
236
+
237
+ int64_t total_micros =
238
+ (tm.tm_hour * 3600LL + tm.tm_min * 60LL + tm.tm_sec ) * 1000000LL ;
239
+
240
+ if (in.peek () == ' .' ) {
241
+ in.ignore ();
242
+ std::string fractional_str;
243
+ char c;
244
+ while (in.get (c) && isdigit (c)) {
245
+ fractional_str += c;
246
+ }
247
+ if (in) {
248
+ in.unget ();
249
+ }
250
+
251
+ if (fractional_str.length () > 6 ) {
252
+ fractional_str.resize (6 );
253
+ }
254
+ try {
255
+ if (!fractional_str.empty ()) {
256
+ fractional_str.append (6 - fractional_str.length (), ' 0' );
257
+ total_micros += std::stoll (fractional_str);
258
+ }
259
+ } catch (const std::exception&) {
260
+ return NotSupported (" Failed to parse fractional part of Time '{}'" , str_val);
261
+ }
262
+ }
263
+
264
+ if (in.peek () != EOF) {
265
+ return NotSupported (" Unconsumed characters found after parsing Time '{}'" ,
266
+ str_val);
267
+ }
268
+
269
+ return Literal::Time (total_micros);
212
270
}
213
271
272
+ case TypeId::kTimestamp :
214
273
case TypeId::kTimestampTz : {
215
- // TODO(Li Feiyang): Implement parsing for "YYYY-MM-DDTHH:MM:SS.ffffffZ" using
216
- // std::chrono::parse once it becomes available in the target libc++.
217
- return NotImplemented (" Cast from String to TimestampTz is not yet implemented." );
274
+ // Parse "YYYY-MM-DDTHH:MM:SS.ffffff" and optional 'Z'
275
+ in >> std::get_time (&tm, " %Y-%m-%dT%H:%M:%S" );
276
+
277
+ if (in.fail ()) {
278
+ return NotSupported (
279
+ " Failed to parse '{}' as a valid Timestamp (expected YYYY-MM-DDTHH:MM:SS...)" ,
280
+ str_val);
281
+ }
282
+
283
+ auto seconds_since_epoch = timegm_custom (&tm);
284
+ int64_t total_micros = seconds_since_epoch * 1000000LL ;
285
+
286
+ if (in.peek () == ' .' ) {
287
+ in.ignore ();
288
+ std::string fractional_str;
289
+ char c;
290
+ while (in.get (c) && isdigit (c)) {
291
+ fractional_str += c;
292
+ }
293
+ if (in) {
294
+ in.unget ();
295
+ }
296
+
297
+ if (fractional_str.length () > 6 ) {
298
+ fractional_str.resize (6 );
299
+ }
300
+ try {
301
+ if (!fractional_str.empty ()) {
302
+ fractional_str.append (6 - fractional_str.length (), ' 0' );
303
+ total_micros += std::stoll (fractional_str);
304
+ }
305
+ } catch (const std::exception&) {
306
+ return NotSupported (" Failed to parse fractional part of Timestamp '{}'" ,
307
+ str_val);
308
+ }
309
+ }
310
+
311
+ if (target_type->type_id () == TypeId::kTimestampTz ) {
312
+ // NOTE: This implementation DOES NOT support timezone offsets like
313
+ // '+08:00' or '-07:00'. It only supports the UTC designator 'Z'.
314
+ if (in.peek () == ' Z' ) {
315
+ in.ignore (); // Consume 'Z'
316
+ }
317
+ }
318
+
319
+ if (in.peek () != EOF) {
320
+ return NotSupported (" Unconsumed characters found after parsing Timestamp '{}'" ,
321
+ str_val);
322
+ }
323
+
324
+ if (target_type->type_id () == TypeId::kTimestamp ) {
325
+ return Literal::Timestamp (total_micros);
326
+ } else {
327
+ return Literal::TimestampTz (total_micros);
328
+ }
218
329
}
330
+ // TODO(Li Feiyang): Implement cast from String to uuid and decimal
219
331
220
332
default :
221
333
return NotSupported (" Cast from String to {} is not supported" ,
@@ -243,12 +355,10 @@ Result<Literal> LiteralCaster::CastFromTimestampTz(
243
355
auto micros = std::get<int64_t >(literal.value_ );
244
356
245
357
switch (target_type->type_id ()) {
246
- case TypeId::kDate : {
358
+ case TypeId::kDate :
247
359
return Literal::Date (MicrosToDays (micros));
248
- }
249
- case TypeId::kTimestamp : {
360
+ case TypeId::kTimestamp :
250
361
return Literal::Timestamp (micros);
251
- }
252
362
default :
253
363
return NotSupported (" Cast from TimestampTz to {} is not supported" ,
254
364
target_type->ToString ());
@@ -278,17 +388,8 @@ Result<Literal> LiteralCaster::CastFromFixed(
278
388
const auto & fixed_val = std::get<std::vector<uint8_t >>(literal.value_ );
279
389
280
390
switch (target_type->type_id ()) {
281
- case TypeId::kBinary : {
391
+ case TypeId::kBinary :
282
392
return Literal::Binary (fixed_val);
283
- }
284
- case TypeId::kFixed : {
285
- auto target_fixed_type = std::dynamic_pointer_cast<FixedType>(target_type);
286
- if (fixed_val.size () == target_fixed_type->length ()) {
287
- return literal;
288
- }
289
- return NotSupported (" Cannot cast Fixed({}) to Fixed({}) due to mismatched lengths" ,
290
- fixed_val.size (), target_fixed_type->length ());
291
- }
292
393
default :
293
394
return NotSupported (" Cast from Fixed to {} is not supported" ,
294
395
target_type->ToString ());
0 commit comments