@@ -130,7 +130,7 @@ JavascriptInterop::ConvertFromV8(Handle<Value> iValue, ConvertedObjects &already
130
130
if (iValue->IsArray ())
131
131
return ConvertArrayFromV8 (iValue, already_converted);
132
132
if (iValue->IsDate ())
133
- return ConvertDateFromV8 (iValue);
133
+ return ConvertDateFromV8 (iValue. As <Date>() );
134
134
if (iValue->IsRegExp ())
135
135
return ConvertRegexFromV8 (iValue);
136
136
if (iValue->IsFunction ())
@@ -199,8 +199,8 @@ JavascriptInterop::ConvertToV8(System::Object^ iObject)
199
199
return v8::Number::New (isolate, safe_cast<float >(iObject));
200
200
if (type == System::Decimal::typeid )
201
201
return v8::Number::New (isolate, (double )safe_cast<System::Decimal>(iObject));
202
- if (type == System::DateTime::typeid )
203
- return v8::Date::New (isolate-> GetCurrentContext (), SystemInterop::ConvertFromSystemDateTime ( safe_cast<System::DateTime^>(iObject))). ToLocalChecked ( );
202
+ if (type == System::DateTime::typeid )
203
+ return ConvertDateTimeToV8 ( safe_cast<System::DateTime^>(iObject));
204
204
}
205
205
}
206
206
if (type == System::String::typeid )
@@ -357,13 +357,73 @@ JavascriptInterop::ConvertObjectFromV8(Handle<Object> iObject, ConvertedObjects
357
357
358
358
// //////////////////////////////////////////////////////////////////////////////////////////////////
359
359
360
- System::DateTime^
361
- JavascriptInterop::ConvertDateFromV8 (Handle<Value> iValue)
360
+ /*
361
+ * We need to be very careful with date conversions because C# does not use the IANA timezone database
362
+ * to determine offsets from UTC / daylight savings time. However, V8 does use the IANA timezone
363
+ * database which lead to discrepancies in the date conversion.
364
+ *
365
+ * Example:
366
+ * Germany did not observe daylight savings time from 1950-1979 and therefore observed UTC+1
367
+ * throughout the whole year. However, C# thinks Germany did observe daylight savings time
368
+ * and assumes incorrectly that Germany observed UTC+2 during the summer time.
369
+ *
370
+ * V8
371
+ * new Date(1978, 5, 15) // "Thu Jun 15 1978 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit)"
372
+ *
373
+ * C#
374
+ * If we get the ticks since 1970-01-01 from V8 to construct a UTC DateTime object we get
375
+ * "1978-06-14 23:00:00" which is correct. A subsequent call to .ToLocalTime() on that date object
376
+ * yields "1978-06-15 01:00:00" which is wrong; it should be "1978-06-15 00:00:00"!
377
+ *
378
+ * The same problem exists in the other direction.
379
+ *
380
+ * We therefore construct date objects directly from the date components we get from V8 and vice versa.
381
+ */
382
+
383
+ double GetDateComponent (Isolate* isolate, Handle<Date> date, const char * component)
362
384
{
363
- System::DateTime^ startDate = gcnew System::DateTime (1970 , 1 , 1 , 0 , 0 , 0 , 0 , System::DateTimeKind::Utc);
364
- double milliseconds = iValue->NumberValue (JavascriptContext::GetCurrentIsolate ()->GetCurrentContext ()).ToChecked ();
365
- System::TimeSpan^ timespan = System::TimeSpan::FromMilliseconds (milliseconds);
366
- return System::DateTime (timespan->Ticks + startDate->Ticks ).ToLocalTime ();
385
+ auto getComponent = date->Get (isolate->GetCurrentContext (), String::NewFromUtf8 (isolate, component)).ToLocalChecked ().As <Function>();
386
+ auto componentValue = getComponent->Call (date, 0 , nullptr );
387
+ return componentValue->NumberValue (isolate->GetCurrentContext ()).ToChecked ();
388
+ }
389
+
390
+ void SetDateComponent (Isolate* isolate, Handle<Date> date, const char * component, double value)
391
+ {
392
+ auto getComponent = date->Get (isolate->GetCurrentContext (), String::NewFromUtf8 (isolate, component)).ToLocalChecked ().As <Function>();
393
+ Handle<Value> parameters[] = { JavascriptInterop::ConvertToV8 (value) };
394
+ getComponent->Call (date, 1 , parameters);
395
+ }
396
+
397
+ System::DateTime^ JavascriptInterop::ConvertDateFromV8(Handle<Date> date)
398
+ {
399
+ auto isolate = JavascriptContext::GetCurrentIsolate ();
400
+ auto year = GetDateComponent (isolate, date, " getFullYear" );
401
+ auto month = GetDateComponent (isolate, date, " getMonth" ) + 1 ;
402
+ auto day = GetDateComponent (isolate, date, " getDate" );
403
+ auto hour = GetDateComponent (isolate, date, " getHours" );
404
+ auto minute = GetDateComponent (isolate, date, " getMinutes" );
405
+ auto second = GetDateComponent (isolate, date, " getSeconds" );
406
+ auto millisecond = GetDateComponent (isolate, date, " getMilliseconds" );
407
+ return gcnew System::DateTime (year, month, day, hour, minute, second, millisecond, System::DateTimeKind::Local);
408
+ }
409
+
410
+ Handle<Date> JavascriptInterop::ConvertDateTimeToV8 (System::DateTime^ dateTime)
411
+ {
412
+ auto isolate = JavascriptContext::GetCurrentIsolate ();
413
+ EscapableHandleScope handleScope (isolate);
414
+
415
+ auto date = v8::Date::New (isolate->GetCurrentContext (), SystemInterop::ConvertFromSystemDateTime (dateTime)).ToLocalChecked ().As <Date>();
416
+ if (dateTime->Kind == System::DateTimeKind::Utc)
417
+ return handleScope.Escape (date);
418
+
419
+ SetDateComponent (isolate, date, " setFullYear" , dateTime->Year );
420
+ SetDateComponent (isolate, date, " setMonth" , dateTime->Month - 1 );
421
+ SetDateComponent (isolate, date, " setDate" , dateTime->Day );
422
+ SetDateComponent (isolate, date, " setHours" , dateTime->Hour );
423
+ SetDateComponent (isolate, date, " setMinutes" , dateTime->Minute );
424
+ SetDateComponent (isolate, date, " setSeconds" , dateTime->Second );
425
+ SetDateComponent (isolate, date, " setMilliseconds" , dateTime->Millisecond );
426
+ return handleScope.Escape (date);
367
427
}
368
428
369
429
// //////////////////////////////////////////////////////////////////////////////////////////////////
0 commit comments