@@ -261,7 +261,9 @@ def get_month(self):
261261
262262 month = property (get_month , set_month )
263263
264- def __init__ (self , year = None , month = None , day = None , ** kwargs ):
264+ def __init__ (
265+ self , year = None , month = None , day = None , significant_digits = None , ** kwargs
266+ ):
265267 for param in ("date" , "lower" , "upper" ):
266268 if param in kwargs :
267269 self .__init__ (** kwargs [param ])
@@ -270,13 +272,18 @@ def __init__(self, year=None, month=None, day=None, **kwargs):
270272 self .year = year # Year is required, but sometimes passed in as a 'date' dict.
271273 self .month = month
272274 self .day = day
275+ self .significant_digits = (
276+ int (significant_digits ) if significant_digits else None
277+ )
273278
274279 def __str__ (self ):
275280 r = self .year
276281 if self .month :
277282 r += f"-{ self .month } "
278283 if self .day :
279284 r += f"-{ self .day } "
285+ if self .significant_digits :
286+ r += f"S{ self .significant_digits } "
280287 return r
281288
282289 def isoformat (self , default = date .max ):
@@ -286,6 +293,36 @@ def isoformat(self, default=date.max):
286293 int (self .day or default .day ),
287294 )
288295
296+ def lower_fuzzy (self ):
297+ if not hasattr (self , "significant_digits" ) or not self .significant_digits :
298+ return apply_delta (
299+ sub , self .lower_strict (), self ._get_fuzzy_padding (EARLIEST )
300+ )
301+ else :
302+ total_digits = len (self .year )
303+ insignificant_digits = total_digits - self .significant_digits
304+ lower_year = (
305+ int (self .year )
306+ // (10 ** insignificant_digits )
307+ * (10 ** insignificant_digits )
308+ )
309+ return struct_time ([lower_year , 1 , 1 ] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS )
310+
311+ def upper_fuzzy (self ):
312+ if not hasattr (self , "significant_digits" ) or not self .significant_digits :
313+ return apply_delta (
314+ add , self .upper_strict (), self ._get_fuzzy_padding (LATEST )
315+ )
316+ else :
317+ total_digits = len (self .year )
318+ insignificant_digits = total_digits - self .significant_digits
319+ upper_year = (int (self .year ) // (10 ** insignificant_digits ) + 1 ) * (
320+ 10 ** insignificant_digits
321+ ) - 1
322+ return struct_time (
323+ [upper_year , 12 , 31 ] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS
324+ )
325+
289326 def _precise_year (self , lean ):
290327 # Replace any ambiguous characters in the year string with 0s or 9s
291328 if lean == EARLIEST :
@@ -337,6 +374,9 @@ def precision(self):
337374 return PRECISION_MONTH
338375 return PRECISION_YEAR
339376
377+ def estimated (self ):
378+ return self ._precise_year (EARLIEST )
379+
340380
341381class DateAndTime (EDTFObject ):
342382 def __init__ (self , date , time ):
@@ -537,11 +577,17 @@ def _get_fuzzy_padding(self, lean):
537577
538578
539579class LongYear (EDTFObject ):
540- def __init__ (self , year ):
580+ def __init__ (self , year , significant_digits = None ):
541581 self .year = year
582+ self .significant_digits = (
583+ int (significant_digits ) if significant_digits else None
584+ )
542585
543586 def __str__ (self ):
544- return f"Y{ self .year } "
587+ if self .significant_digits :
588+ return f"Y{ self .year } S{ self .significant_digits } "
589+ else :
590+ return f"Y{ self .year } "
545591
546592 def _precise_year (self ):
547593 return int (self .year )
@@ -553,6 +599,45 @@ def _strict_date(self, lean):
553599 else :
554600 return struct_time ([py , 12 , 31 ] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS )
555601
602+ def estimated (self ):
603+ return self ._precise_year ()
604+
605+ def lower_fuzzy (self ):
606+ full_year = self ._precise_year ()
607+ strict_val = self .lower_strict ()
608+ if not self .significant_digits :
609+ return apply_delta (sub , strict_val , self ._get_fuzzy_padding (EARLIEST ))
610+ else :
611+ insignificant_digits = len (str (full_year )) - int (self .significant_digits )
612+ if insignificant_digits <= 0 :
613+ return apply_delta (sub , strict_val , self ._get_fuzzy_padding (EARLIEST ))
614+ padding_value = 10 ** insignificant_digits
615+ sig_digits = full_year // padding_value
616+ lower_year = sig_digits * padding_value
617+ return apply_delta (
618+ sub ,
619+ struct_time ([lower_year , 1 , 1 ] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS ),
620+ self ._get_fuzzy_padding (EARLIEST ),
621+ )
622+
623+ def upper_fuzzy (self ):
624+ full_year = self ._precise_year ()
625+ strict_val = self .upper_strict ()
626+ if not self .significant_digits :
627+ return apply_delta (add , strict_val , self ._get_fuzzy_padding (LATEST ))
628+ else :
629+ insignificant_digits = len (str (full_year )) - self .significant_digits
630+ if insignificant_digits <= 0 :
631+ return apply_delta (add , strict_val , self ._get_fuzzy_padding (LATEST ))
632+ padding_value = 10 ** insignificant_digits
633+ sig_digits = full_year // padding_value
634+ upper_year = (sig_digits + 1 ) * padding_value - 1
635+ return apply_delta (
636+ add ,
637+ struct_time ([upper_year , 12 , 31 ] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS ),
638+ self ._get_fuzzy_padding (LATEST ),
639+ )
640+
556641
557642class Season (Date ):
558643 def __init__ (self , year , season , ** kwargs ):
@@ -806,10 +891,6 @@ def _strict_date(self, lean):
806891 return min ([x ._strict_date (lean ) for x in self .objects ])
807892
808893
809- class MaskedPrecision (Date ):
810- pass
811-
812-
813894class Level2Interval (Level1Interval ):
814895 def __init__ (self , lower , upper ):
815896 # Check whether incoming lower/upper values are single-item lists, and
@@ -831,18 +912,23 @@ class Level2Season(Season):
831912
832913
833914class ExponentialYear (LongYear ):
834- def __init__ (self , base , exponent , precision = None ):
915+ def __init__ (self , base , exponent , significant_digits = None ):
835916 self .base = base
836917 self .exponent = exponent
837- self .precision = precision
918+ self .significant_digits = (
919+ int (significant_digits ) if significant_digits else None
920+ )
838921
839922 def _precise_year (self ):
840923 return int (self .base ) * 10 ** int (self .exponent )
841924
842925 def get_year (self ):
843- if self .precision :
844- return f"{ self .base } E{ self .exponent } S{ self .precision } "
926+ if self .significant_digits :
927+ return f"{ self .base } E{ self .exponent } S{ self .significant_digits } "
845928 else :
846929 return f"{ self .base } E{ self .exponent } "
847930
848931 year = property (get_year )
932+
933+ def estimated (self ):
934+ return self ._precise_year ()
0 commit comments