PERF: cython-optimized datetime constructor
State: closed

Calling the datetime constructor goes through python space, so the C code generated by `dt = datetime(obj.dts.year, obj.dts.month, obj.dts.day, obj.dts.hour, obj.dts.min, obj.dts.sec, obj.dts.us, obj.tzinfo)` in master is:

```
  __pyx_t_1 = __Pyx_PyInt_From_npy_int64(__pyx_v_obj->dts.year); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 435, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_3 = __Pyx_PyInt_From_npy_int32(__pyx_v_obj->dts.month); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 435, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_7 = __Pyx_PyInt_From_npy_int32(__pyx_v_obj->dts.day); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 435, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_7);
  __pyx_t_4 = __Pyx_PyInt_From_npy_int32(__pyx_v_obj->dts.hour); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 436, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_2 = __Pyx_PyInt_From_npy_int32(__pyx_v_obj->dts.min); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 436, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_5 = __Pyx_PyInt_From_npy_int32(__pyx_v_obj->dts.sec); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 436, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_11 = __Pyx_PyInt_From_npy_int32(__pyx_v_obj->dts.us); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 437, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_11);
  __pyx_t_12 = PyTuple_New(8); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 435, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_12);
  __Pyx_GIVEREF(__pyx_t_1);
  PyTuple_SET_ITEM(__pyx_t_12, 0, __pyx_t_1);
  __Pyx_GIVEREF(__pyx_t_3);
  PyTuple_SET_ITEM(__pyx_t_12, 1, __pyx_t_3);
  __Pyx_GIVEREF(__pyx_t_7);
  PyTuple_SET_ITEM(__pyx_t_12, 2, __pyx_t_7);
  __Pyx_GIVEREF(__pyx_t_4);
  PyTuple_SET_ITEM(__pyx_t_12, 3, __pyx_t_4);
  __Pyx_GIVEREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_12, 4, __pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_5);
  PyTuple_SET_ITEM(__pyx_t_12, 5, __pyx_t_5);
  __Pyx_GIVEREF(__pyx_t_11);
  PyTuple_SET_ITEM(__pyx_t_12, 6, __pyx_t_11);
  __Pyx_INCREF(__pyx_v_obj->tzinfo);
  __Pyx_GIVEREF(__pyx_v_obj->tzinfo);
  PyTuple_SET_ITEM(__pyx_t_12, 7, __pyx_v_obj->tzinfo);
  __pyx_t_1 = 0;
  __pyx_t_3 = 0;
  __pyx_t_7 = 0;
  __pyx_t_4 = 0;
  __pyx_t_2 = 0;
  __pyx_t_5 = 0;
  __pyx_t_11 = 0;
  __pyx_t_11 = __Pyx_PyObject_Call(((PyObject *)__pyx_ptype_7cpython_8datetime_datetime), __pyx_t_12, NULL); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 435, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_11);
  __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0;
  __pyx_v_dt = ((PyDateTime_DateTime *)__pyx_t_11);
  __pyx_t_11 = 0;
```


Using `datetime_new` we instead get:
```
  __pyx_t_1 = __pyx_v_obj->tzinfo;
  __Pyx_INCREF(__pyx_t_1);
  __pyx_t_3 = __pyx_f_7cpython_8datetime_datetime_new(__pyx_v_obj->dts.year, __pyx_v_obj->dts.month, __pyx_v_obj->dts.day, __pyx_v_obj->dts.hour, __pyx_v_obj->dts.min, __pyx_v_obj->dts.sec, __pyx_v_obj->dts.us, __pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 437, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  if (!(likely(((__pyx_t_3) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_3, __pyx_ptype_7cpython_8datetime_datetime))))) __PYX_ERR(0, 437, __pyx_L1_error)
  __pyx_v_dt = ((PyDateTime_DateTime *)__pyx_t_3);
  __pyx_t_3 = 0;
```

Still need to run asv to see if this actually matters.

Labels:

Comments:
nothing obvious in asv.  i think im going to close this and suggest in cython making this substitution automatically