@@ -541,7 +541,148 @@ def precision(self):
541541
542542
543543class Unspecified (Date ):
544- pass
544+ def __init__ (
545+ self ,
546+ year = None ,
547+ month = None ,
548+ day = None ,
549+ significant_digits = None ,
550+ ua = None ,
551+ ** kwargs ,
552+ ):
553+ super ().__init__ (
554+ year = year ,
555+ month = month ,
556+ day = day ,
557+ significant_digits = significant_digits ,
558+ ** kwargs ,
559+ )
560+ self .ua = ua
561+ self .negative = self .year .startswith ("-" )
562+
563+ def __str__ (self ):
564+ base = super ().__str__ ()
565+ if self .ua :
566+ base += str (self .ua )
567+ return base
568+
569+ def _get_fuzzy_padding (self , lean ):
570+ if not self .ua :
571+ return relativedelta ()
572+ multiplier = self .ua ._get_multiplier ()
573+ padding = relativedelta ()
574+
575+ if self .year :
576+ years_padding = self ._years_padding (multiplier )
577+ padding += years_padding
578+ if self .month :
579+ padding += relativedelta (
580+ months = int (multiplier * appsettings .PADDING_MONTH_PRECISION .months )
581+ )
582+ if self .day :
583+ padding += relativedelta (
584+ days = int (multiplier * appsettings .PADDING_DAY_PRECISION .days )
585+ )
586+ return padding
587+
588+ def _years_padding (self , multiplier ):
589+ """Calculate year padding based on the precision."""
590+ precision_settings = {
591+ PRECISION_MILLENIUM : appsettings .PADDING_MILLENNIUM_PRECISION .years ,
592+ PRECISION_CENTURY : appsettings .PADDING_CENTURY_PRECISION .years ,
593+ PRECISION_DECADE : appsettings .PADDING_DECADE_PRECISION .years ,
594+ PRECISION_YEAR : appsettings .PADDING_YEAR_PRECISION .years ,
595+ }
596+ years = precision_settings .get (self .precision , 0 )
597+ return relativedelta (years = int (multiplier * years ))
598+
599+ def lower_fuzzy (self ):
600+ strict_val = (
601+ self .lower_strict ()
602+ ) # negative handled in the lower_strict() override
603+ adjusted = apply_delta (sub , strict_val , self ._get_fuzzy_padding (EARLIEST ))
604+ return adjusted
605+
606+ def upper_fuzzy (self ):
607+ strict_val = (
608+ self .upper_strict ()
609+ ) # negative handled in the upper_strict() override
610+
611+ adjusted = apply_delta (add , strict_val , self ._get_fuzzy_padding (LATEST ))
612+ return adjusted
613+
614+ def lower_strict (self ):
615+ if self .negative :
616+ strict_val = self ._strict_date (
617+ lean = LATEST
618+ ) # gets the year right, but need to adjust day and month
619+ if self .precision in (
620+ PRECISION_YEAR ,
621+ PRECISION_DECADE ,
622+ PRECISION_CENTURY ,
623+ PRECISION_MILLENIUM ,
624+ ):
625+ return struct_time (
626+ (strict_val .tm_year , 1 , 1 )
627+ + tuple (TIME_EMPTY_TIME )
628+ + tuple (TIME_EMPTY_EXTRAS )
629+ )
630+ elif self .precision == PRECISION_MONTH :
631+ return struct_time (
632+ (strict_val .tm_year , strict_val .tm_mon , 1 )
633+ + tuple (TIME_EMPTY_TIME )
634+ + tuple (TIME_EMPTY_EXTRAS )
635+ )
636+ else :
637+ return strict_val
638+ else :
639+ return self ._strict_date (lean = EARLIEST )
640+
641+ def upper_strict (self ):
642+ if self .negative :
643+ strict_val = self ._strict_date (lean = EARLIEST )
644+ if self .precision in (
645+ PRECISION_YEAR ,
646+ PRECISION_DECADE ,
647+ PRECISION_CENTURY ,
648+ PRECISION_MILLENIUM ,
649+ ):
650+ return struct_time (
651+ (strict_val .tm_year , 12 , 31 )
652+ + tuple (TIME_EMPTY_TIME )
653+ + tuple (TIME_EMPTY_EXTRAS )
654+ )
655+ elif self .precision == PRECISION_MONTH :
656+ days_in_month = calendar .monthrange (
657+ strict_val .tm_year , strict_val .tm_mon
658+ )[1 ]
659+ return struct_time (
660+ (strict_val .tm_year , strict_val .tm_mon , days_in_month )
661+ + tuple (TIME_EMPTY_TIME )
662+ + tuple (TIME_EMPTY_EXTRAS )
663+ )
664+ else :
665+ return strict_val
666+ else :
667+ return self ._strict_date (lean = LATEST )
668+
669+ @property
670+ def precision (self ):
671+ if self .day :
672+ return PRECISION_DAY
673+ if self .month :
674+ return PRECISION_MONTH
675+ if self .year :
676+ year_no_symbol = self .year .lstrip ("-" )
677+ if year_no_symbol .isdigit ():
678+ return PRECISION_YEAR
679+ if len (year_no_symbol ) == 4 and year_no_symbol .endswith ("XXX" ):
680+ return PRECISION_MILLENIUM
681+ if len (year_no_symbol ) == 4 and year_no_symbol .endswith ("XX" ):
682+ return PRECISION_CENTURY
683+ if len (year_no_symbol ) == 4 and year_no_symbol .endswith ("X" ):
684+ return PRECISION_DECADE
685+ raise ValueError (f"Unspecified date { self } has no precision" )
545686
546687
547688class Level1Interval (Interval ):
0 commit comments