@@ -98,6 +98,7 @@ except NameError: # py3
9898cdef inline object create_timestamp_from_ts(
9999 int64_t value, pandas_datetimestruct dts,
100100 object tz, object freq):
101+ """ convenience routine to construct a Timestamp from its parts """
101102 cdef _Timestamp ts_base
102103 ts_base = _Timestamp.__new__ (Timestamp, dts.year, dts.month,
103104 dts.day, dts.hour, dts.min,
@@ -112,6 +113,7 @@ cdef inline object create_timestamp_from_ts(
112113cdef inline object create_datetime_from_ts(
113114 int64_t value, pandas_datetimestruct dts,
114115 object tz, object freq):
116+ """ convenience routine to construct a datetime.datetime from its parts """
115117 return datetime(dts.year, dts.month, dts.day, dts.hour,
116118 dts.min, dts.sec, dts.us, tz)
117119
@@ -378,7 +380,6 @@ class Timestamp(_Timestamp):
378380 # Mixing pydatetime positional and keyword arguments is forbidden!
379381
380382 cdef _TSObject ts
381- cdef _Timestamp ts_base
382383
383384 if offset is not None :
384385 # deprecate offset kwd in 0.19.0, GH13593
@@ -412,17 +413,7 @@ class Timestamp(_Timestamp):
412413 from pandas.tseries.frequencies import to_offset
413414 freq = to_offset(freq)
414415
415- # make datetime happy
416- ts_base = _Timestamp.__new__ (cls , ts.dts.year, ts.dts.month,
417- ts.dts.day, ts.dts.hour, ts.dts.min,
418- ts.dts.sec, ts.dts.us, ts.tzinfo)
419-
420- # fill out rest of data
421- ts_base.value = ts.value
422- ts_base.freq = freq
423- ts_base.nanosecond = ts.dts.ps / 1000
424-
425- return ts_base
416+ return create_timestamp_from_ts(ts.value, ts.dts, ts.tzinfo, freq)
426417
427418 def _round (self , freq , rounder ):
428419
@@ -660,8 +651,80 @@ class Timestamp(_Timestamp):
660651 astimezone = tz_convert
661652
662653 def replace (self , **kwds ):
663- return Timestamp(datetime.replace(self , ** kwds),
664- freq = self .freq)
654+ """
655+ implements datetime.replace, handles nanoseconds
656+
657+ Parameters
658+ ----------
659+ kwargs: key-value dict
660+
661+ accepted keywords are:
662+ year, month, day, hour, minute, second, microsecond, nanosecond, tzinfo
663+
664+ values must be integer, or for tzinfo, a tz-convertible
665+
666+ Returns
667+ -------
668+ Timestamp with fields replaced
669+ """
670+
671+ cdef:
672+ pandas_datetimestruct dts
673+ int64_t value
674+ object tzinfo, result, k, v
675+ _TSObject ts
676+
677+ # set to naive if needed
678+ tzinfo = self .tzinfo
679+ value = self .value
680+ if tzinfo is not None :
681+ value = tz_convert_single(value, ' UTC' , tzinfo)
682+
683+ # setup components
684+ pandas_datetime_to_datetimestruct(value, PANDAS_FR_ns, & dts)
685+ dts.ps = self .nanosecond * 1000
686+
687+ # replace
688+ def validate (k , v ):
689+ """ validate integers """
690+ if not isinstance (v, int ):
691+ raise ValueError (" value must be an integer, received {v} for {k}" .format(v = type (v), k = k))
692+ return v
693+
694+ for k, v in kwds.items():
695+ if k == ' year' :
696+ dts.year = validate(k, v)
697+ elif k == ' month' :
698+ dts.month = validate(k, v)
699+ elif k == ' day' :
700+ dts.day = validate(k, v)
701+ elif k == ' hour' :
702+ dts.hour = validate(k, v)
703+ elif k == ' minute' :
704+ dts.min = validate(k, v)
705+ elif k == ' second' :
706+ dts.sec = validate(k, v)
707+ elif k == ' microsecond' :
708+ dts.us = validate(k, v)
709+ elif k == ' nanosecond' :
710+ dts.ps = validate(k, v) * 1000
711+ elif k == ' tzinfo' :
712+ tzinfo = v
713+ else :
714+ raise ValueError (" invalid name {} passed" .format(k))
715+
716+ # reconstruct & check bounds
717+ value = pandas_datetimestruct_to_datetime(PANDAS_FR_ns, & dts)
718+ if value != NPY_NAT:
719+ _check_dts_bounds(& dts)
720+
721+ # set tz if needed
722+ if tzinfo is not None :
723+ value = tz_convert_single(value, tzinfo, ' UTC' )
724+
725+ result = create_timestamp_from_ts(value, dts, tzinfo, self .freq)
726+
727+ return result
665728
666729 def isoformat (self , sep = ' T' ):
667730 base = super (_Timestamp, self ).isoformat(sep = sep)
@@ -5041,7 +5104,9 @@ cpdef normalize_date(object dt):
50415104 -------
50425105 normalized : datetime.datetime or Timestamp
50435106 """
5044- if PyDateTime_Check(dt):
5107+ if is_timestamp(dt):
5108+ return dt.replace(hour = 0 , minute = 0 , second = 0 , microsecond = 0 , nanosecond = 0 )
5109+ elif PyDateTime_Check(dt):
50455110 return dt.replace(hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
50465111 elif PyDate_Check(dt):
50475112 return datetime(dt.year, dt.month, dt.day)
0 commit comments