ENH: Timestamp.replace support non-nano (#47312) · pandas-dev/pandas@87da500 (original) (raw)

`@@ -31,6 +31,7 @@ from cpython.datetime cimport (

`

31

31

`import_datetime()

`

32

32

``

33

33

`from pandas._libs.tslibs.base cimport ABCTimestamp

`

``

34

`+

from pandas._libs.tslibs.dtypes cimport periods_per_second

`

34

35

`from pandas._libs.tslibs.np_datetime cimport (

`

35

36

` NPY_DATETIMEUNIT,

`

36

37

` NPY_FR_ns,

`

`@@ -40,11 +41,14 @@ from pandas._libs.tslibs.np_datetime cimport (

`

40

41

` dtstruct_to_dt64,

`

41

42

` get_datetime64_unit,

`

42

43

` get_datetime64_value,

`

``

44

`+

get_implementation_bounds,

`

43

45

` get_unit_from_dtype,

`

44

46

` npy_datetime,

`

45

47

` npy_datetimestruct,

`

``

48

`+

npy_datetimestruct_to_datetime,

`

46

49

` pandas_datetime_to_datetimestruct,

`

47

50

` pydatetime_to_dt64,

`

``

51

`+

pydatetime_to_dtstruct,

`

48

52

` string_to_dts,

`

49

53

`)

`

50

54

``

`@@ -307,11 +311,15 @@ cdef maybe_localize_tso(_TSObject obj, tzinfo tz, NPY_DATETIMEUNIT reso):

`

307

311

`if obj.value != NPY_NAT:

`

308

312

`# check_overflows needs to run after _localize_tso

`

309

313

` check_dts_bounds(&obj.dts, reso)

`

310

``

`-

check_overflows(obj)

`

``

314

`+

check_overflows(obj, reso)

`

311

315

``

312

316

``

313

``

`-

cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,

`

314

``

`-

int32_t nanos=0):

`

``

317

`+

cdef _TSObject convert_datetime_to_tsobject(

`

``

318

`+

datetime ts,

`

``

319

`+

tzinfo tz,

`

``

320

`+

int32_t nanos=0,

`

``

321

`+

NPY_DATETIMEUNIT reso=NPY_FR_ns,

`

``

322

`+

):

`

315

323

`"""

`

316

324

`` Convert a datetime (or Timestamp) input ts, along with optional timezone

``

317

325

`` object tz to a _TSObject.

``

`@@ -327,13 +335,15 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,

`

327

335

` timezone for the timezone-aware output

`

328

336

` nanos : int32_t, default is 0

`

329

337

` nanoseconds supplement the precision of the datetime input ts

`

``

338

`+

reso : NPY_DATETIMEUNIT, default NPY_FR_ns

`

330

339

``

331

340

` Returns

`

332

341

` -------

`

333

342

` obj : _TSObject

`

334

343

`"""

`

335

344

` cdef:

`

336

345

` _TSObject obj = _TSObject()

`

``

346

`+

int64_t pps

`

337

347

``

338

348

` obj.fold = ts.fold

`

339

349

`if tz is not None:

`

`@@ -342,34 +352,35 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,

`

342

352

`if ts.tzinfo is not None:

`

343

353

`# Convert the current timezone to the passed timezone

`

344

354

` ts = ts.astimezone(tz)

`

345

``

`-

obj.value = pydatetime_to_dt64(ts, &obj.dts)

`

``

355

`+

pydatetime_to_dtstruct(ts, &obj.dts)

`

346

356

` obj.tzinfo = ts.tzinfo

`

347

357

`elif not is_utc(tz):

`

348

358

` ts = _localize_pydatetime(ts, tz)

`

349

``

`-

obj.value = pydatetime_to_dt64(ts, &obj.dts)

`

``

359

`+

pydatetime_to_dtstruct(ts, &obj.dts)

`

350

360

` obj.tzinfo = ts.tzinfo

`

351

361

`else:

`

352

362

`# UTC

`

353

``

`-

obj.value = pydatetime_to_dt64(ts, &obj.dts)

`

``

363

`+

pydatetime_to_dtstruct(ts, &obj.dts)

`

354

364

` obj.tzinfo = tz

`

355

365

`else:

`

356

``

`-

obj.value = pydatetime_to_dt64(ts, &obj.dts)

`

``

366

`+

pydatetime_to_dtstruct(ts, &obj.dts)

`

357

367

` obj.tzinfo = ts.tzinfo

`

358

368

``

359

``

`-

if obj.tzinfo is not None and not is_utc(obj.tzinfo):

`

360

``

`-

offset = get_utcoffset(obj.tzinfo, ts)

`

361

``

`-

obj.value -= int(offset.total_seconds() * 1e9)

`

362

``

-

363

369

`if isinstance(ts, ABCTimestamp):

`

364

``

`-

obj.value += ts.nanosecond

`

365

370

` obj.dts.ps = ts.nanosecond * 1000

`

366

371

``

367

372

`if nanos:

`

368

``

`-

obj.value += nanos

`

369

373

` obj.dts.ps = nanos * 1000

`

370

374

``

371

``

`-

check_dts_bounds(&obj.dts)

`

372

``

`-

check_overflows(obj)

`

``

375

`+

obj.value = npy_datetimestruct_to_datetime(reso, &obj.dts)

`

``

376

+

``

377

`+

if obj.tzinfo is not None and not is_utc(obj.tzinfo):

`

``

378

`+

offset = get_utcoffset(obj.tzinfo, ts)

`

``

379

`+

pps = periods_per_second(reso)

`

``

380

`+

obj.value -= int(offset.total_seconds() * pps)

`

``

381

+

``

382

`+

check_dts_bounds(&obj.dts, reso)

`

``

383

`+

check_overflows(obj, reso)

`

373

384

`return obj

`

374

385

``

375

386

``

`@@ -401,7 +412,7 @@ cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,

`

401

412

` obj.tzinfo = pytz.FixedOffset(tzoffset)

`

402

413

` obj.value = tz_localize_to_utc_single(value, obj.tzinfo)

`

403

414

`if tz is None:

`

404

``

`-

check_overflows(obj)

`

``

415

`+

check_overflows(obj, NPY_FR_ns)

`

405

416

`return obj

`

406

417

``

407

418

` cdef:

`

`@@ -515,13 +526,14 @@ cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, str unit,

`

515

526

`return convert_datetime_to_tsobject(dt, tz)

`

516

527

``

517

528

``

518

``

`-

cdef inline check_overflows(_TSObject obj):

`

``

529

`+

cdef inline check_overflows(_TSObject obj, NPY_DATETIMEUNIT reso=NPY_FR_ns):

`

519

530

`"""

`

520

531

` Check that we haven't silently overflowed in timezone conversion

`

521

532

``

522

533

` Parameters

`

523

534

` ----------

`

524

535

` obj : _TSObject

`

``

536

`+

reso : NPY_DATETIMEUNIT, default NPY_FR_ns

`

525

537

``

526

538

` Returns

`

527

539

` -------

`

`@@ -532,15 +544,20 @@ cdef inline check_overflows(_TSObject obj):

`

532

544

` OutOfBoundsDatetime

`

533

545

`"""

`

534

546

`# GH#12677

`

535

``

`-

if obj.dts.year == 1677:

`

``

547

`+

cdef:

`

``

548

`+

npy_datetimestruct lb, ub

`

``

549

+

``

550

`+

get_implementation_bounds(reso, &lb, &ub)

`

``

551

+

``

552

`+

if obj.dts.year == lb.year:

`

536

553

`if not (obj.value < 0):

`

537

554

`from pandas._libs.tslibs.timestamps import Timestamp

`

538

555

` fmt = (f"{obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "

`

539

556

` f"{obj.dts.hour:02d}:{obj.dts.min:02d}:{obj.dts.sec:02d}")

`

540

557

`raise OutOfBoundsDatetime(

`

541

558

` f"Converting {fmt} underflows past {Timestamp.min}"

`

542

559

` )

`

543

``

`-

elif obj.dts.year == 2262:

`

``

560

`+

elif obj.dts.year == ub.year:

`

544

561

`if not (obj.value > 0):

`

545

562

`from pandas._libs.tslibs.timestamps import Timestamp

`

546

563

` fmt = (f"{obj.dts.year}-{obj.dts.month:02d}-{obj.dts.day:02d} "

`