2222#include < chrono>
2323#include < cmath>
2424#include < concepts>
25+ #include < cstdint>
26+ #include < iomanip>
2527
2628#include " iceberg/exception.h"
2729
@@ -35,6 +37,14 @@ int32_t MicrosToDays(int64_t micros_since_epoch) {
3537 return static_cast <int32_t >(days_duration.count ());
3638}
3739
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+
3848} // namespace
3949
4050// / \brief LiteralCaster handles type casting operations for Literal.
@@ -111,6 +121,7 @@ Result<Literal> LiteralCaster::CastFromInt(
111121 return Literal::Double (static_cast <double >(int_val));
112122 case TypeId::kDate :
113123 return Literal::Date (int_val);
124+ // TODO(Li Feiyang): Implement cast from Int to decimal
114125 default :
115126 return NotSupported (" Cast from Int to {} is not implemented" ,
116127 target_type->ToString ());
@@ -137,10 +148,10 @@ Result<Literal> LiteralCaster::CastFromLong(
137148 case TypeId::kDouble :
138149 return Literal::Double (static_cast <double >(long_val));
139150 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 ()) {
141152 return AboveMaxLiteral (target_type);
142153 }
143- if (long_val < static_cast < int64_t >( std::numeric_limits<int32_t >::min () )) {
154+ if (long_val < std::numeric_limits<int32_t >::min ()) {
144155 return BelowMinLiteral (target_type);
145156 }
146157 return Literal::Date (static_cast <int32_t >(long_val));
@@ -151,6 +162,7 @@ Result<Literal> LiteralCaster::CastFromLong(
151162 return Literal::Timestamp (long_val);
152163 case TypeId::kTimestampTz :
153164 return Literal::TimestampTz (long_val);
165+ // TODO(Li Feiyang): Implement cast from Long to decimal, TimestampNs and
154166 default :
155167 return NotSupported (" Cast from Long to {} is not supported" ,
156168 target_type->ToString ());
@@ -164,6 +176,7 @@ Result<Literal> LiteralCaster::CastFromFloat(
164176 switch (target_type->type_id ()) {
165177 case TypeId::kDouble :
166178 return Literal::Double (static_cast <double >(float_val));
179+ // TODO(Li Feiyang): Implement cast from Float to decimal
167180 default :
168181 return NotSupported (" Cast from Float to {} is not supported" ,
169182 target_type->ToString ());
@@ -192,30 +205,129 @@ Result<Literal> LiteralCaster::CastFromDouble(
192205
193206Result<Literal> LiteralCaster::CastFromString (
194207 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+
195212 switch (target_type->type_id ()) {
196213 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 () || 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 ()));
200226 }
201227
202228 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" );
207231
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);
212270 }
213271
272+ case TypeId::kTimestamp :
214273 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+ }
218329 }
330+ // TODO(Li Feiyang): Implement cast from String to uuid and decimal
219331
220332 default :
221333 return NotSupported (" Cast from String to {} is not supported" ,
@@ -281,14 +393,6 @@ Result<Literal> LiteralCaster::CastFromFixed(
281393 case TypeId::kBinary : {
282394 return Literal::Binary (fixed_val);
283395 }
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- }
292396 default :
293397 return NotSupported (" Cast from Fixed to {} is not supported" ,
294398 target_type->ToString ());
0 commit comments