|
669 | 669 | end |
670 | 670 | end |
671 | 671 | end |
| 672 | + |
| 673 | +@testitem "DateTime64 to Date/DateTime" begin |
| 674 | + using Dates |
| 675 | + using PythonCall: NumpyDates |
| 676 | + |
| 677 | + # Cases: (value, unit_symbol_or_tuple, expected_DateTime, expected_Date) |
| 678 | + cases = [ |
| 679 | + # Day counts (since 1970-01-01) |
| 680 | + (10_956, :D, DateTime(1999, 12, 31, 0, 0, 0), Date(1999, 12, 31)), |
| 681 | + (0, :D, DateTime(1970, 1, 1, 0, 0, 0), Date(1970, 1, 1)), |
| 682 | + (1, :D, DateTime(1970, 1, 2, 0, 0, 0), Date(1970, 1, 2)), |
| 683 | + |
| 684 | + # Seconds since epoch |
| 685 | + (946_684_799, :s, DateTime(1999, 12, 31, 23, 59, 59), Date(1999, 12, 31)), |
| 686 | + (0, :s, DateTime(1970, 1, 1, 0, 0, 0), Date(1970, 1, 1)), |
| 687 | + (3_600, :s, DateTime(1970, 1, 1, 1, 0, 0), Date(1970, 1, 1)), |
| 688 | + |
| 689 | + # Hours/minutes (epoch-based) |
| 690 | + (24, :h, DateTime(1970, 1, 2, 0, 0, 0), Date(1970, 1, 2)), |
| 691 | + (60, :m, DateTime(1970, 1, 1, 1, 0, 0), Date(1970, 1, 1)), |
| 692 | + |
| 693 | + # Years/months (calendar truncation semantics from 1970-01-01) |
| 694 | + (30, :Y, DateTime(2000, 1, 1, 0, 0, 0), Date(2000, 1, 1)), |
| 695 | + (361, :M, DateTime(2000, 2, 1, 0, 0, 0), Date(2000, 2, 1)), |
| 696 | + (359, :M, DateTime(1999, 12, 1, 0, 0, 0), Date(1999, 12, 1)), |
| 697 | + |
| 698 | + # Weeks |
| 699 | + (1, :W, DateTime(1970, 1, 8, 0, 0, 0), Date(1970, 1, 8)), |
| 700 | + (-1, :W, DateTime(1969, 12, 25, 0, 0, 0), Date(1969, 12, 25)), |
| 701 | + |
| 702 | + # Sub-nanosecond units: floored to nanoseconds |
| 703 | + (1_500, :ps, DateTime(1970, 1, 1, 0, 0, 0) + Nanosecond(1), Date(1970, 1, 1)), |
| 704 | + (1_500_000, :fs, DateTime(1970, 1, 1, 0, 0, 0) + Nanosecond(1), Date(1970, 1, 1)), |
| 705 | + ( |
| 706 | + 1_500_000_000, |
| 707 | + :as, |
| 708 | + DateTime(1970, 1, 1, 0, 0, 0) + Nanosecond(1), |
| 709 | + Date(1970, 1, 1), |
| 710 | + ), |
| 711 | + |
| 712 | + # Multiplier tuple unit: value * multiplier is applied before adding |
| 713 | + (1_800, (:s, 2), DateTime(1970, 1, 1, 1, 0, 0), Date(1970, 1, 1)), |
| 714 | + ] |
| 715 | + |
| 716 | + @testset "$v $u" for (v, u, expdt, expd) in cases |
| 717 | + # 1) DateTime64(value, unit) |
| 718 | + x = NumpyDates.DateTime64(v, u) |
| 719 | + @test Dates.DateTime(x) == expdt |
| 720 | + @test Dates.Date(x) == expd |
| 721 | + |
| 722 | + # 2) InlineDateTime64 typed |
| 723 | + Uconst = u isa Tuple ? (NumpyDates.Unit(u[1]), Int(u[2])) : NumpyDates.Unit(u) |
| 724 | + y = NumpyDates.InlineDateTime64{Uconst}(v) |
| 725 | + @test Dates.DateTime(y) == expdt |
| 726 | + @test Dates.Date(y) == expd |
| 727 | + |
| 728 | + # 3) InlineDateTime64 dynamic |
| 729 | + z = NumpyDates.InlineDateTime64(v, u) |
| 730 | + @test Dates.DateTime(z) == expdt |
| 731 | + @test Dates.Date(z) == expd |
| 732 | + end |
| 733 | + |
| 734 | + # NaT conversion errors |
| 735 | + for u in (:Y, :M, :W, :D, :h, :m, :s, :ms, :us, :ns, :ps, :fs, :as) |
| 736 | + nat1 = NumpyDates.DateTime64("NaT", u) |
| 737 | + @test_throws Exception Dates.DateTime(nat1) |
| 738 | + @test_throws Exception Dates.Date(nat1) |
| 739 | + |
| 740 | + Uconst = NumpyDates.Unit(u) |
| 741 | + nat2 = NumpyDates.InlineDateTime64{Uconst}("NaT") |
| 742 | + @test_throws Exception Dates.DateTime(nat2) |
| 743 | + @test_throws Exception Dates.Date(nat2) |
| 744 | + |
| 745 | + nat3 = NumpyDates.InlineDateTime64("NaT", u) |
| 746 | + @test_throws Exception Dates.DateTime(nat3) |
| 747 | + @test_throws Exception Dates.Date(nat3) |
| 748 | + end |
| 749 | +end |
0 commit comments