Bagaimana cara memodelkan tanggal parsial dalam Python? Seperti tahun yang tidak dikenal, atau hari yang tidak diketahui dalam sebulan?

11

Saya ingin dapat menangkap fakta seperti Bob was born in 2000dan Bill's birthday is May 7th.

Dalam kedua contoh tersebut, kita hanya mengetahui sebagian dari tanggal lahir orang tersebut. Dalam satu kasus kita hanya tahu tahun; dalam kasus lain kita tahu bulan dan hari, tetapi bukan tahun.

Bagaimana cara saya menangkap informasi ini?

Beberapa contoh cara kerjanya:

Bayangkan pustaka seperti datetime yang memungkinkan None di bidang untuk mewakili yang tidak diketahui. Saya mungkin memiliki kode seperti berikut ini:

date_a = date(2000, 5, None)
date_b = date(2000, 6, None)
difference = date_b - date_a
assert difference.min.days == 1
assert difference.max.days == 60  # Or something close to 60.
assert equal(date_a, date_b) == False

date_c = date(2000, 5, None)
assert equal(date_a, date_c) == Maybe

Ini hanyalah sebuah contoh bagaimana perilaku itu. Saya tidak selalu menginginkan perilaku yang tepat ini.

Buttons840
sumber
Secara umum, cara Anda menangani hal-hal seperti ini adalah menggunakan, misalnya, tahun 0001 di .NET untuk tanggal yang tidak memiliki tahun, dan 1 Januari selama bertahun-tahun tanpa bulan dan hari.
Robert Harvey
Saya mengedit pertanyaan Anda untuk menghapus permintaan untuk perpustakaan. Pertanyaan semacam itu di luar topik di situs ini.
@ RobertTarvey Saya tidak bisa menggunakan saran Anda. Jika kita melihat bahwa Bob lahir 1 Januari 2000, kita tidak tahu persis apa artinya ini. Kita tidak tahu apakah dia dilahirkan pada hari pertama tahun 2000, atau apakah dia dilahirkan pada hari apa pun tahun 2000. Kita perlu tahu bedanya.
Buttons840
@ RobertTarvey Saya tahu itu biasa, tetapi saya telah melihat banyak kegagalan buruk karena pilihan nilai sinyal yang buruk. (Plus, saya tidak berpikir itu menjawab pertanyaan karena OP perlu berurusan dengan hanya beberapa tanggal yang tidak diketahui. Pengaturan ke 1 Januari dalam kasus seperti itu tidak memungkinkan Anda untuk membedakan tanggal 1 Januari nyata dari yang tidak diketahui.
Gort the Robot
5
@ Buttons840: Maka Anda harus menulis kelas yang merangkum perilaku yang Anda inginkan. Anda harus membungkus kelas tanggal yang ada dan menambahkan perilaku yang Anda inginkan.
Robert Harvey

Jawaban:

3

Pertama-tama, begitu Anda mulai mendekomposisi tanggal ke dalam komponen penyusunnya, mereka bukan lagi tanggal.

Dengan cara yang sama bahwa tidak mungkin untuk menghapus fungsionalitas melalui subclass tanpa melanggar OOP, tidak mungkin untuk mencampur tanggal dan fraksi tanggal tanpa menyebabkan kebingungan (atau lebih buruk) membuatnya kompatibel seperti pada contoh kode Anda tanpa merusak sesuatu yang lain.

Jika Anda ingin menangkap satu tahun, apa yang salah dengan objek yang berisi bilangan bulat sederhana? Jika Anda ingin mengabadikan satu bulan dan satu hari, mengapa tidak mencatat satu bulan penghitungan dan satu hari bilangan bulat? Mungkin bahkan menyimpannya secara internal di objek tanggal sehingga Anda mendapatkan batas pemeriksaan yang tepat (mis. 31 Februari tidak masuk akal). Namun, tampilkan antarmuka yang berbeda.

Mengapa Anda ingin membandingkan tanggal dengan satu tahun untuk melihat apakah mereka sama, lebih besar, atau lebih kecil? Tidak masuk akal: tidak ada informasi yang cukup untuk membuat perbandingan itu. Namun, ada perbandingan lain yang mungkin masuk akal (ini adalah pseudocode):

Year y = Year(2015)
Date d = Date(2015, 01, 01)
assert y.contains(d) == True

sumber
2

Komentar kedua Robert Harvey berisi jawaban yang benar, tetapi izinkan saya sedikit mengembangkannya.

Tahun kelahiran orang dan tanggal lahir orang adalah entitas yang sama sekali berbeda, jadi Anda tidak perlu (dan sebenarnya Anda tidak boleh) menggunakan mekanisme yang sama untuk keduanya.

Untuk tanggal lahir, Anda dapat merancang BirthDatetipe data (atau mungkin YearlyRecurringDatemeskipun saya tidak dapat membuat nama yang layak saat ini) yang hanya akan berisi datetahun yang konstan, seperti tahun 2000 berdasarkan konvensi. Tahun 2000 adalah pilihan yang baik karena itu adalah lompatan, sehingga tidak akan mengecewakan orang-orang yang berulang tahun pada tanggal 28 Februari.

Selama bertahun-tahun kelahiran, Anda dapat merancang sebuah BirthYeartipe data (atau mungkin ApproximateDatetipe data) yang akan berisi date, dan indikator presisi: Year, Month, Full.

Manfaat dari pendekatan ini adalah bahwa di jantung hal Anda masih mempertahankan datesehingga Anda masih dapat melakukan aritmatika tanggal.

Mike Nakis
sumber
1

Saya percaya apa yang Anda gambarkan akan menjadi pengganti drop-in untuk datetimemodul yang mengimplementasikan datetime.datetimeatribut (tahun, bulan, dll) sebagai nilai dengan pengukuran ketidakpastian (bukan hanya nilai).

Paket Python ada untuk membantu dengan angka yang tidak pasti (misalnya: paket ketidakpastian ), dan mungkin tidak akan terlalu sulit untuk membuat garpu datetimeyang menggunakan ketidakpastian pada setiap atribut. Saya juga ingin melihatnya dan bahkan mungkin menggunakannya untuk itu. Argumen pasti bisa dibuat untuk dimasukkannya udatetimeke dalam paket ketidakpastian yang terkait sebelumnya.

Contoh Anda akan menjadi seperti:

bob_bday = udatetime(2000, (6,6))  # 2000-06 +/- 6mo
>>> 2000-??-?? T??:??:??
bil_bday = udatetime((1970, 50), 3, 7)  # assume bill is ~40 +/- 40 
>>> [1970+/-40]-03-07 T??:??:??

"Nilai sinyal" memiliki banyak masalah, tetapi di samping itu Anda dapat mewakili hal-hal dengan ketidakpastian yang tidak dapat diperoleh nilai sinyal:

# ali was born in spring
ali_bday = udatetime((), (4.5, 1.5))
>>> [1970+/-40]-[4.5+/-1.5]-?? T??:??:??

Pertimbangan lain adalah bahwa untuk lebih tepatnya ketidakpastian di sini sebenarnya harus bertipe timedelta. Saya meninggalkannya sebagai latihan bagi pembaca untuk mencari konstruktor yang ringkas dan lengkap untuk udatetimemenggunakan timedeltaketidakpastian.

Jadi pada akhirnya saya akan mengatakan bahwa apa yang Anda gambarkan adalah "mudah" dimodelkan dengan ketidakpastian, tetapi implementasi a udatetimesecara praktis cukup sulit. Sebagian besar akan mengambil rute "mudah" dan memecah datetime menjadi komponen dan melacak ketidakpastian pada mereka secara independen, tetapi jika Anda merasa ambisius uncertaintiespaket (atau yang lain) mungkin tertarik pada permintaan tarik udatetime.

7yl4r
sumber
0

Mengapa tidak membuat kelas "periode" yang mengimplementasikan dari ke struktur.

"Bob lahir tahun 2000" ->

period {
   from  {
      yy = 2000;
      mm = 01;
      dd = 01; 
   }
   to {
     yy = 2000;
     mm = 12;
     dd = 31;
   }
   fuzz = 365;
}

Anda kemudian dapat menerapkan berbagai metode pencarian seperti dengan mengurutkan dari dari tanggal. Atribut fuzz memberikan indikasi yang berguna tentang seberapa akurat tanggal sehingga Anda dapat menentukan fuzz == 1 untuk pencocokan tepat, atau fuzz == 31 untuk dalam waktu sekitar satu bulan atau lebih.

James Anderson
sumber