VerityPy 1.1
Python library for Verity data profiling, quality control, remediation
datefuncs.py
Go to the documentation of this file.
1#!/usr/bin/env python
2"""
3Date Functions
4
5various functions to process and provide dates
6"""
7
8__all__ = ['get_current_iso_datetime',
9 'is_iso_date_str',
10 'convert_excel_date_to_iso',
11 'is_year_leap',
12 'convert_date_to_iso',
13 'is_date_format'
14 ]
15
16__version__ = '1.0'
17__author__ = 'Geoffrey Malafsky'
18__email__ = 'gmalafsky@technikinterlytics.com'
19__date__ = '20240725'
20
21
22import math
23from time import *
24from . import numfuncs
25
26
27def get_current_iso_datetime(inctime:bool=False, tz:str="") -> str:
28 """
29 Get current date or datetime in ISO format
30 but without delimiters like 20240409 or 20240409T090245
31 inctime: bool whether to include time part with T prefix
32 tz: optional number hours timezone offset from UTC (e.g. Dallas TX is -5, Kolkata India is +5.5, New York NY is -4),
33 otherwise the computer's time zone is used so depends on server settings.
34 Returns string starting with notok: if error
35 """
36
37 result:str=""
38 dval:float=-99.0
39 dtime_secs_since_epoch:float=-999999
40 time_obj:struct_time
41 try:
42 dtime_secs_since_epoch= time()
43 if len(tz)>0 and numfuncs.is_real(tz):
44 dval= dtime_secs_since_epoch + round(float(tz),1) * 3600 # number seconds for offset hours
45 else:
46 dval= dtime_secs_since_epoch # uses localtime
47 time_obj= localtime(dval)
48 if inctime:
49 result= strftime("%Y%m%dT%H%M%S", time_obj)
50 else:
51 result= strftime("%Y%m%d", time_obj)
52 except (RuntimeError, ValueError, OSError) as err:
53 result="notok:" + str(err)
54 return result
55
56def is_iso_date_str(strin:str,timereq:bool=False) -> str:
57 """
58 Determines if string is an ISO DateTime
59
60 timereq: bool if time part is required. This is demarcated by either
61 T or a space after the date part as with 20240420T0730 or 20240420 0730
62
63 All delimiters will be removed (- /) in date part, (:) in time part.
64 Returns string as either false or true:<newdatetime>, or starts
65 with notok: if error
66 """
67
68 result:str=""
69 nyr:int=0
70 nmo:int=0
71 ndy:int=0
72 nhr:int=-1
73 nmin:int=0
74 nsec:int=0
75 n1:int=-1
76 timepart:str=""
77 datepart:str=""
78 tz:str=""
79 delim:str=""
80 txt1:str=""
81 txt2:str=""
82 txt3:str=""
83 txt4:str=""
84 fmterr:bool=False
85 dayspermonth:list=[]
86 try:
87 if len(strin)==0:
88 return "false"
89 strin=strin.lower()
90 if "t" in strin:
91 n1=strin.find("t")
92 elif timereq and " " in strin:
93 n1=strin.find(" ")
94 if n1>0:
95 timepart= strin[n1+1:]
96 datepart= strin[:n1]
97 else:
98 datepart=strin
99 dayspermonth.append(31)
100 dayspermonth.append(28)
101 dayspermonth.append(31)
102 dayspermonth.append(30)
103 dayspermonth.append(31)
104 dayspermonth.append(30)
105 dayspermonth.append(31)
106 dayspermonth.append(31)
107 dayspermonth.append(30)
108 dayspermonth.append(31)
109 dayspermonth.append(30)
110 dayspermonth.append(31)
111
112 if timereq and len(timepart)==0:
113 fmterr=True
114 if not fmterr:
115 if "-" in datepart:
116 delim="-"
117 n1=datepart.find(delim)
118 txt1= datepart[:n1]
119 txt2=datepart[n1+1:]
120 n1=txt2.find(delim)
121 if n1>-1:
122 txt3=txt2[n1+1:]
123 txt2=txt2[:n1]
124 elif len(datepart)>=4:
125 txt1=datepart[:4]
126 if len(datepart)>=6:
127 txt2=datepart[4:6]
128 if len(datepart)>=8:
129 txt3=datepart[6:]
130 if len(txt1)==4 and numfuncs.is_int(txt1):
131 nyr=int(txt1)
132 if nyr<0:
133 fmterr=True
134 else:
135 fmterr=True
136 if not fmterr and len(txt2)==2 and numfuncs.is_int(txt2):
137 nmo=int(txt2)
138 if nmo<=0 or nmo>=13:
139 fmterr=True
140 else:
141 fmterr=True
142 if not fmterr and len(txt3)==2 and numfuncs.is_int(txt3):
143 ndy=int(txt3)
144 if ndy<=0 or ndy>=31:
145 fmterr=True
146 else:
147 fmterr=True
148 if not fmterr and timereq:
149 if len(timepart)>0:
150 txt1=""
151 txt2=""
152 txt3=""
153 txt4=""
154 if timepart.endswith("z"):
155 tz="Z"
156 timepart=timepart[:len(timepart)-1]
157 elif "+" in timepart:
158 n1=timepart.find("+")
159 tz=timepart[n1:]
160 timepart=timepart[:n1].strip()
161 elif "-" in timepart:
162 n1=timepart.find("-")
163 tz=timepart[n1:]
164 timepart=timepart[:n1].strip()
165 if ":" in timepart:
166 delim=":"
167 n1=timepart.find(delim)
168 txt1=timepart[:n1]
169 txt2=timepart[n1+1:]
170 if delim in txt2:
171 n1=txt2.find(delim)
172 txt3=txt2[n1+1:]
173 txt2=txt2[:n1]
174 elif len(timepart)>2:
175 txt1=timepart[:2]
176 if len(timepart)>=4:
177 txt2=timepart[2:4]
178 if len(timepart)==6 or ("." in timepart and len(timepart)>7):
179 txt3=timepart[4:]
180 if "." in txt3:
181 n1= txt3.find(".")
182 txt4=txt3[(n1+1):]
183 txt3=txt3[:n1]
184
185 if len(txt1)!=2 or not numfuncs.is_int(txt1):
186 fmterr=True
187 else:
188 nhr=int(txt1)
189 if len(txt2)>0:
190 if len(txt2)!=2 or not numfuncs.is_int(txt2):
191 fmterr=True
192 else:
193 nmin=int(txt2)
194 if len(txt3)>0:
195 if len(txt3)!=2 or not numfuncs.is_int(txt3):
196 fmterr=True
197 else:
198 nsec=int(txt3)
199 if len(txt4)>0 and not numfuncs.is_int(txt4):
200 fmterr=True
201 else:
202 fmterr=True
203
204 if not fmterr:
205 if nyr<=0:
206 fmterr=True
207 elif 0> nmo >12:
208 fmterr=True
209 elif 0> ndy > 31:
210 fmterr=True
211 else:
212 if nmo==2:
213 n1= 29 if is_year_leap(nyr) else 28
214 else:
215 n1= dayspermonth[nmo-1]
216 if ndy>n1:
217 fmterr= True
218
219 if not fmterr:
220 txt1=str(nyr)
221 if len(txt1)==3:
222 txt1="0" + txt1
223 elif len(txt1)==2:
224 txt1="00" + txt1
225 elif len(txt1)==1:
226 txt1="000" + txt1
227 txt2=str(nmo)
228 if len(txt2)==1:
229 txt2 = "0" + txt2
230 txt3=str(ndy)
231 if len(txt3)==1:
232 txt3 = "0" + txt3
233 result= txt1 + txt2 + txt3
234 if timereq:
235 result += "T"
236 txt1= str(nhr)
237 if len(txt1)==1:
238 txt1 = "0" + txt1
239 txt2=str(nmin)
240 if len(txt2)==1:
241 txt2 = "0" + txt2
242 txt3=str(nsec)
243 if len(txt3)==1:
244 txt3 = "0" + txt3
245 result += txt1 + txt2 + txt3
246 result = "true:" + result
247 if len(tz)>0:
248 result += tz
249 else:
250 result="false"
251 except (RuntimeError,ValueError,OSError) as err:
252 result= "notok:" + str(err)
253 return result
254
255def convert_excel_date_to_iso(strin:str) -> str:
256 """
257 Excel numeric date to ISO format
258
259 Converts a date in numeric excel format into ISO8601 yyyyMMdd format.
260 Fractional days are removed. Jan 1 1900 is 1. Anything less than 1 is error.
261 Example: 44106 = 20201002, 45393 = 20240411, 21012=19570711
262 Return result as yyyyMMdd. Starts with notok: if there is an error
263 """
264
265
266 result:str=""
267 str1:str=""
268 ddays_400:int=0
269 ddays_100:int=0
270 ddays_4:int=0
271 ddays_1:int=0
272 curyr:int=0
273 curmn:int=0
274 curday:int=0
275 dayspermonth:list=[]
276 dval_days:float=0
277 nyrs_400:int=0
278 nyrs_100:int=0
279 nyrs_4:int=0
280 nyrs_1:int=0
281 isleap:bool=False
282 ndays:int=0
283 ndaysprev:int=0
284 addleap:bool=True
285 try:
286 dayspermonth.append(31)
287 dayspermonth.append(28)
288 dayspermonth.append(31)
289 dayspermonth.append(30)
290 dayspermonth.append(31)
291 dayspermonth.append(30)
292 dayspermonth.append(31)
293 dayspermonth.append(31)
294 dayspermonth.append(30)
295 dayspermonth.append(31)
296 dayspermonth.append(30)
297 dayspermonth.append(31)
298 dayspermonth.append(29) #leap years
299
300 ddays_1=365
301 ddays_4= 365*4
302 if addleap:
303 # 1 leap year
304 ddays_4 += 1
305 ddays_100= 365*100
306 if addleap:
307 # 24 leap years since number 100 is not
308 ddays_100 += 24
309 ddays_400= ddays_100*4
310 if addleap:
311 # 400th year is leap
312 ddays_400 += 1
313 str1=strin
314 if "." in str1:
315 str1=str1[:str1.find(".")]
316 dval_days= int(str1)
317 if dval_days<1:
318 raise ValueError("min number to supply is 1 not: " + str1)
319 dval_days -= 1 #since Excel starts with day 1
320
321 if dval_days>ddays_400:
322 nyrs_400= math.floor(dval_days/ddays_400)
323 dval_days -= nyrs_400 * ddays_400
324 if dval_days> ddays_100:
325 nyrs_100= math.floor(dval_days/ddays_100)
326 dval_days -= nyrs_100 * ddays_100
327 if dval_days > ddays_4:
328 nyrs_4= math.floor(dval_days/ddays_4)
329 dval_days -= nyrs_4 * ddays_4
330 if dval_days > ddays_1:
331 nyrs_1= math.floor(dval_days/ddays_1)
332 dval_days -= nyrs_1 * ddays_1
333
334 curyr= 1900 + 400*nyrs_400 + 100*nyrs_100 + 4*nyrs_4 + nyrs_1
335 isleap= is_year_leap(curyr)
336
337 if dval_days>0:
338 for i in range(12):
339 if i>0:
340 ndaysprev=ndays
341 if isleap and i==1:
342 ndays += dayspermonth[12]
343 else:
344 ndays += dayspermonth[i]
345 if ndays>=dval_days:
346 curmn= i+1
347 curday=dval_days- ndaysprev
348 break
349 curmn= max(curmn,1)
350 curday= max(curday,1)
351 result= str(curyr)
352 str1= str(curmn)
353 if len(str1)==1:
354 str1="0" + str1
355 result += str1
356 str1= str(curday)
357 if len(str1)==1:
358 str1="0" + str1
359 result += str1
360 except (RuntimeError,ValueError,OSError) as err:
361 result="notok:" + str(err)
362 return result
363
364def is_year_leap(nyear:int) -> bool:
365 """
366 Is Year a Leap Year
367
368 uses rule that every 4th year is leap except when
369 multiple of 100, but when multiple of 400 it is leap
370 nyear: integer year to evaluate
371 returns: bool
372 """
373
374 isleap=False
375 try:
376 if nyear>=400 and nyear%400==0:
377 isleap=True
378 elif nyear>=100 and nyear%100==0:
379 isleap=False
380 elif nyear>=4 and nyear%4==0:
381 isleap=True
382 except (RuntimeError,ValueError,OSError):
383 pass
384 return isleap
385
386def convert_date_to_iso(datein:str,formatin:str,detectfmt:bool=False) -> str:
387 """
388 Converts a datetime into ISO8601 format based on specified format.
389 Time portions should be removed prior to sending into this method.
390 Four delimiters in date part will be automatically detected: ( - / _ . )
391
392 datein: incoming date string not in ISO8601 and without time part.
393 formatin: required unless detectfmt is True
394 mmddyy, mmdyy, mdyy, mmddyyyy, mmdyyyy, mdyyyy,
395 ddmmyy, ddmyy, dmyy, ddmmyyyy, ddmyyyy, dmyyyy,
396 yymmdd, yymmd, yymd, yyyymmdd, yyyymmd, yyyymd,
397 yyyyddd (3 digit day number within year),
398 yyyyMMMdd, ddMMMyyyy (MMM = 3 letter month title like 'JAN'),
399 'MONTHdd,yyyy', 'ddMONTH,yyyy', yyyyMONTHdd, ddMONTHyyyy, yyMONTHdd, ddMONTHyy (MONTH full title),
400 *dmmyyyy, mm*dyyyy, *mddyyyy, dd*myyyy (*= can be 1 or 2 characters)
401
402 With these formats, incoming string must have all characters required so for mmddyyyy there must be
403 8 characters meaning 1122011 fails but 01122011 is good.
404 Leading title of day is removed like for Wednesday, March 14, 2001 which will be changed to
405 March 14, 2001 and then will match formatin of MONTHdd,yyyy since spaces are removed
406 detectfmt: optional bool when True the format will be detected if possible. To do so, the date
407 part should have a delimiter ( / - ) like 12/20/2021 or 2024-04-02, and preferably
408 where day value is unambiguous relative to month (i.e. > 12 )
409 Returns - result as yyyymmdd with suffix (dateformat) if detectfmt=True. Starts with 'notok:' if there is an error
410 """
411
412 result:str=""
413 dayspermonth:list=[]
414 hash_month_names:dict={}
415 hash_month_longnames:dict={}
416 hash_day_names:dict={}
417 hash_day_longnames:dict={}
418 has_short_month:bool=False
419 has_long_month:bool=False
420 has_day:bool=False
421 month_name_used:str=""
422 delimin:str=""
423 yr:str=""
424 mo:str=""
425 dy:str=""
426 liststr:list=[]
427 n1:int=-1
428 n2:int=-1
429 nidx:int=-1
430 ndy:int=-1
431 nmo:int=-1
432 nyr:int=-1
433 isleap:bool=False
434 str1:str=""
435 str2:str=""
436 str3:str=""
437 txt:str=""
438 txt1:str=""
439 txt2:str=""
440 prior_yr_min=69
441 prior_yr_max=99
442 try:
443 dayspermonth.append(31)
444 dayspermonth.append(28)
445 dayspermonth.append(31)
446 dayspermonth.append(30)
447 dayspermonth.append(31)
448 dayspermonth.append(30)
449 dayspermonth.append(31)
450 dayspermonth.append(31)
451 dayspermonth.append(30)
452 dayspermonth.append(31)
453 dayspermonth.append(30)
454 dayspermonth.append(31)
455 dayspermonth.append(29) #leap years
456
457 hash_month_names["jan"]="01"
458 hash_month_names["feb"]="02"
459 hash_month_names["mar"]="03"
460 hash_month_names["apr"]="04"
461 hash_month_names["may"]="05"
462 hash_month_names["jun"]="06"
463 hash_month_names["jul"]="07"
464 hash_month_names["aug"]="08"
465 hash_month_names["sep"]="09"
466 hash_month_names["oct"]="10"
467 hash_month_names["nov"]="11"
468 hash_month_names["dec"]="12"
469 hash_month_longnames["january"]="01"
470 hash_month_longnames["february"]="02"
471 hash_month_longnames["march"]="03"
472 hash_month_longnames["april"]="04"
473 hash_month_longnames["may"]="05"
474 hash_month_longnames["june"]="06"
475 hash_month_longnames["july"]="07"
476 hash_month_longnames["august"]="08"
477 hash_month_longnames["september"]="09"
478 hash_month_longnames["october"]="10"
479 hash_month_longnames["november"]="11"
480 hash_month_longnames["december"]="12"
481
482 hash_day_names["mon"]=1
483 hash_day_names["tue"]=2
484 hash_day_names["wed"]=3
485 hash_day_names["thu"]=4
486 hash_day_names["fri"]=5
487 hash_day_names["sat"]=6
488 hash_day_names["sun"]=7
489 hash_day_longnames["monday"]=1
490 hash_day_longnames["tuesday"]=2
491 hash_day_longnames["wednesday"]=3
492 hash_day_longnames["thursday"]=4
493 hash_day_longnames["friday"]=5
494 hash_day_longnames["saturday"]=6
495 hash_day_longnames["sunday"]=7
496
497 datein=datein.lower().strip()
498 formatin=formatin.lower().strip()
499
500 if len(datein)==0:
501 raise ValueError("no datein")
502 if len(formatin)==0 and not detectfmt:
503 raise ValueError("missing formatin")
504
505 for k,v in hash_day_longnames.items():
506 if datein.startswith(k):
507 has_day=True
508 datein=datein[(len(k)+1):].strip()
509 if datein.startswith(","):
510 datein=datein[1:].strip()
511 break
512 if not has_day:
513 for k,v in hash_day_names.items():
514 if datein.startswith(k):
515 has_day=True
516 n1=datein.find(",")
517 n2=datein.find(" ")
518 if 0<n1<n2:
519 datein=datein[(n1+1):].strip()
520 elif 0<n2<n1:
521 datein=datein[(n2+1):].strip()
522 else:
523 datein=datein[(len(k)+1):].strip()
524 if datein.startswith(","):
525 datein=datein[1:].strip()
526 break
527
528 for k,v in hash_month_longnames.items():
529 if k in datein:
530 has_long_month=True
531 month_name_used=k
532 mo=v
533 break
534 if not has_long_month:
535 for k,v in hash_month_names.items():
536 if k in datein:
537 has_short_month=True
538 month_name_used=k
539 mo=v
540 break
541 if "-" in formatin:
542 formatin=formatin.replace("-","")
543 if "/" in formatin:
544 formatin=formatin.replace("/","")
545 if "_" in formatin:
546 formatin=formatin.replace("_","")
547 if "." in formatin:
548 formatin=formatin.replace(".","")
549
550 if "-" in datein:
551 delimin="-"
552 elif "_" in datein:
553 delimin="_"
554 elif "/" in datein:
555 delimin="/"
556 elif "." in datein:
557 delimin="."
558
559 if len(formatin)>0:
560 if not "month" in formatin and not "*" in formatin:
561 str1=datein
562 if len(delimin)>0 and delimin in str1:
563 str1=str1.replace(delimin,"")
564 if len(str1)> len(formatin):
565 raise ValueError("too many characters for formatin: " + str(len(str1)) + " for " + formatin)
566 if len(str1)< len(formatin):
567 raise ValueError("too few characters for formatin: " + str(len(str1)) + " for " + formatin)
568 elif "*" in formatin:
569 if len(datein)< (len(formatin)-1):
570 raise ValueError("too few characters #= " + str(len(datein)) + " for formatin: " + formatin)
571
572 # attempt to detect format
573 if detectfmt and len(formatin)==0 and len(delimin)>0:
574 nidx= datein.find(delimin)
575 if nidx==4:
576 txt1= datein[nidx+1:]
577 nidx= txt1.find(delimin)
578 if nidx==3:
579 if has_short_month:
580 formatin="yyyymmmdd"
581 elif nidx in (1,2):
582 txt2= txt1[nidx+1:]
583 txt1=txt1[:nidx]
584 n1=-1
585 n2=-1
586 if numfuncs.is_int(txt1):
587 n1=int(txt1)
588 if numfuncs.is_int(txt2):
589 n2=int(txt2)
590 if n1>-1 and n2>-1:
591 if n1>12:
592 str1="d" if len(txt1)==1 else "dd"
593 str2="m" if len(txt2)==1 else "mm"
594 else:
595 str1="m" if len(txt1)==1 else "mm"
596 str2="d" if len(txt2)==1 else "d"
597 formatin="yyyy" + str1 + str2
598 elif nidx in (1,2):
599 txt1= datein[nidx+1:]
600 txt2=datein[:nidx]
601 n1=-1
602 n2=-1
603 if numfuncs.is_int(txt2):
604 n2=int(txt2)
605 nidx= txt1.find(delimin)
606 if nidx==3:
607 if has_short_month:
608 txt1=txt1[nidx+1:]
609 if len(txt1)==4:
610 formatin="ddmmmyyyy"
611 elif len(txt1)<4 and numfuncs.is_int(txt1):
612 n1=int(txt1)
613 if n1>31:
614 formatin="ddmmmyy"
615 elif n2>31:
616 formatin="yymmmdd"
617 elif nidx in (1,2):
618 txt=txt2
619 txt2= txt1[nidx+1:]
620 txt1=txt1[:nidx]
621 n1=-1
622 if numfuncs.is_int(txt1):
623 n1=int(txt1)
624 if n1>-1 and n2>-1:
625 if n2>12:
626 str1="d" if len(txt)==1 else "dd"
627 str2="m" if len(txt1)==1 else "mm"
628 str3="yyyy" if len(txt2)>=4 else "yy"
629 else:
630 str1="m" if len(txt)==1 else "mm"
631 str2="d" if len(txt1)==1 else "dd"
632 str3="yyyy" if len(txt2)>=4 else "yy"
633 formatin= str1 + str2 + str3
634 elif nidx==4:
635 txt=txt2
636 txt2=txt1[nidx+1:]
637 if numfuncs.is_int(txt2):
638 n1=n2
639 n2=int(txt2)
640 if n1>12:
641 str1="d" if len(txt)==1 else "dd"
642 str2="yy" if len(txt1)==2 else "yyyy"
643 str3="m" if len(txt2)==1 else "mm"
644 else:
645 str1="m" if len(txt)==1 else "mm"
646 str2="yy" if len(txt1)==2 else "yyyy"
647 str3="d" if len(txt2)==1 else "dd"
648 formatin= str1 + str2 + str3
649
650 if len(formatin)==0:
651 raise ValueError("no formatin")
652
653 if "dmonthy" in formatin or "ymonthd" in formatin:
654 datein=datein.replace(" ","")
655 if formatin in ["yyyymonthdd","yymonthdd"]:
656 if formatin.startswith("yyyy"):
657 n1=4
658 else:
659 n1=2
660 yr= datein[:n1]
661 datein=datein[n1:]
662 if (has_long_month or has_short_month):
663 mo=month_name_used
664 if len(datein)> len(month_name_used):
665 dy= datein[len(month_name_used):]
666 if len(dy)>2:
667 dy=dy[:2]
668 elif formatin in ["ddmonthyyyy","ddmonthyy"]:
669 dy=datein[:2]
670 datein=datein[2:]
671 if (has_long_month or has_short_month):
672 mo=month_name_used
673 if len(datein)> len(month_name_used):
674 yr= datein[len(month_name_used):]
675 elif formatin in ["monthdd,yyyy","ddmonth,yyyy"]:
676 datein=datein.replace(" ","")
677 if "," in datein:
678 n1= datein.find(",")
679 yr=datein[n1+1:]
680 datein=datein[:n1]
681 if len(month_name_used)>0:
682 mo=month_name_used
683 if formatin.startswith("dd"):
684 dy=datein[:datein.find(month_name_used)]
685 else:
686 dy=datein[len(month_name_used):]
687 elif len(delimin)>0:
688 liststr=datein.split(delimin)
689 if formatin in ["ddmmmyyyy","ddmmmyy"]:
690 if len(liststr)>=1:
691 dy=liststr[0]
692 if len(liststr)>=2:
693 mo=liststr[1]
694 if len(liststr)>=3:
695 yr=liststr[2]
696 elif formatin in ["yyyymmmdd","yymmmdd"]:
697 if len(liststr)>=1:
698 yr=liststr[0]
699 if len(liststr)>=2:
700 mo=liststr[1]
701 if len(liststr)>=3:
702 dy=liststr[2]
703 elif formatin in ["","mmddyy","mddyy","mmdyy","mdyy","mmddyyyy","mddyyyy","mmdyyyy","mdyyyy"]:
704 if len(liststr)>=1:
705 mo=liststr[0]
706 if len(liststr)>=2:
707 dy=liststr[1]
708 if len(liststr)>=3:
709 yr=liststr[2]
710 elif formatin in ["ddmmyy","ddmyy","dmmyy","dmyy","ddmmyyyy","ddmyyyy","dmmyyyy","dmyyyy"]:
711 if len(liststr)>=1:
712 dy=liststr[0]
713 if len(liststr)>=2:
714 mo=liststr[1]
715 if len(liststr)>=3:
716 yr=liststr[2]
717 elif formatin in ["yyddmm","yyddm","yydmm","yydm","yyyyddmm","yyyyddm","yyyydmm","yyyydm"]:
718 if len(liststr)>=1:
719 yr=liststr[0]
720 if len(liststr)>=2:
721 dy=liststr[1]
722 if len(liststr)>=3:
723 mo=liststr[2]
724 elif formatin in ["yymmdd","yymdd","yymmd","yymd","yyyymmdd","yyyymdd","yyyymmd","yyyymd"]:
725 if len(liststr)>=1:
726 yr=liststr[0]
727 if len(liststr)>=2:
728 mo=liststr[1]
729 if len(liststr)>=3:
730 dy=liststr[2]
731 elif formatin=="yyyyddd":
732 if len(liststr)>=1:
733 yr=liststr[0]
734 if len(liststr)>=2:
735 dy=liststr[1]
736 if len(dy)>0 and numfuncs.is_int(dy):
737 ndy=int(dy)
738 if ndy>0:
739 isleap=is_year_leap(int(yr))
740 n1=0
741 for i in range(12):
742 if i==1 and isleap:
743 n2 = dayspermonth[12]
744 else:
745 n2 = dayspermonth[i]
746 if ndy<=(n1+n2):
747 mo=str(i+1)
748 ndy -= n1
749 dy=str(ndy)
750 break
751 n1 += n2
752 else:
753 raise ValueError("unknown date format supplied: " + formatin)
754 elif formatin in ["ddmmmyyyy", "ddmmmyy"]:
755 if len(month_name_used)>0:
756 # must have month
757 mo=month_name_used
758 dy=datein[:2]
759 yr=datein[(datein.find(month_name_used)+len(month_name_used)):]
760 elif formatin in ["yyyymmmdd", "yymmmdd"]:
761 if len(month_name_used)>0:
762 # must have month
763 mo=month_name_used
764 yr=datein[:datein.find(month_name_used)]
765 dy=datein[(datein.find(month_name_used)+len(month_name_used)):]
766 elif "*" in formatin:
767 if formatin.endswith("yyyy"):
768 yr = datein[-4:]
769 if formatin.startswith("*dmm"):
770 mo=datein[-6:-4]
771 if len(datein)==7:
772 dy=datein[:1]
773 else:
774 dy=datein[:2]
775 elif formatin.startswith("*mdd"):
776 dy=datein[-6:-4]
777 if len(datein)==7:
778 mo=datein[:1]
779 else:
780 mo=datein[:2]
781 elif formatin.startswith("dd*m"):
782 dy=datein[:2]
783 if len(datein)==7:
784 mo=datein[-5:-4]
785 else:
786 mo=datein[-6:-4]
787 elif formatin.startswith("mm*d"):
788 mo=datein[:2]
789 if len(datein)==7:
790 dy=datein[-5:-4]
791 else:
792 dy=datein[-6:-4]
793 elif formatin in ["mmddyy","mmddyyyy","mmdyy","mmdyyyy"]:
794 n1=len(datein)
795 if n1<=2:
796 mo=datein
797 elif n1==3:
798 mo=datein[:2]
799 dy=datein[2:]
800 elif n1>=8:
801 mo=datein[:2]
802 dy=datein[2:4]
803 yr=datein[4:8]
804 elif n1>=4:
805 if "mdy" in formatin:
806 mo=datein[:2]
807 dy=datein[2:3]
808 yr=datein[3:]
809 else:
810 mo=datein[:2]
811 dy=datein[2:4]
812 if n1>4:
813 yr=datein[4:]
814 if formatin.endswith("dyy"):
815 if len(yr)>2:
816 yr=yr[:2]
817 elif len(yr)>4:
818 yr=yr[:4]
819 if "mdy" in formatin and len(dy)>1:
820 dy=dy[1:2]
821 elif formatin in ["mddyy","mddyyyy","mdyy","mdyyyy"]:
822 n1=len(datein)
823 if n1==1:
824 mo=datein
825 elif n1==2:
826 mo=datein[:1]
827 dy=datein[1:]
828 elif n1>=8:
829 mo=datein[:2]
830 dy=datein[2:4]
831 yr=datein[4:8]
832 elif n1>=3:
833 if "mdy" in formatin:
834 mo=datein[:1]
835 dy=datein[1:2]
836 yr=datein[2:]
837 else:
838 mo=datein[:1]
839 dy=datein[1:3]
840 if n1>3:
841 yr=datein[3:]
842 if formatin.endswith("dyy"):
843 if len(yr)>2:
844 yr=yr[:2]
845 elif len(yr)>4:
846 yr=yr[:4]
847 if "mdy" in formatin and len(dy)>1:
848 dy=dy[1:2]
849 elif formatin in ["ddmmyy","ddmmyyyy","ddmyy","ddmyyyy"]:
850 n1=len(datein)
851 if n1<=2:
852 dy=datein
853 elif n1==3:
854 dy=datein[:2]
855 mo=datein[2:]
856 elif n1>=8:
857 dy=datein[:2]
858 mo=datein[2:4]
859 yr=datein[4:8]
860 elif n1>=4:
861 dy=datein[:2]
862 if "dmy" in formatin:
863 mo=datein[2:3]
864 yr=datein[3:]
865 else:
866 mo=datein[2:4]
867 if n1>4:
868 yr=datein[4:]
869 if formatin.endswith("myy"):
870 if len(yr)>2:
871 yr=yr[:2]
872 elif len(yr)>4:
873 yr=yr[:4]
874 elif formatin in ["dmmyy","dmmyyyy","dmyy","dmyyyy"]:
875 n1=len(datein)
876 if n1==1:
877 dy=datein
878 elif n1==2:
879 dy=datein[:1]
880 mo=datein[1:]
881 elif n1>=8:
882 dy=datein[:2]
883 mo=datein[2:4]
884 yr=datein[4:8]
885 elif n1>=3:
886 dy=datein[:1]
887 if "dmy" in formatin:
888 mo=datein[1:2]
889 yr=datein[2:]
890 else:
891 mo=datein[1:3]
892 if n1>4:
893 yr=datein[3:]
894 if formatin.endswith("myy"):
895 if len(yr)>2:
896 yr=yr[:2]
897 elif len(yr)>4:
898 yr=yr[:4]
899 elif formatin in ["yyddmm","yyddm","yydmm","yydm"]:
900 n1=len(datein)
901 if n1<=2:
902 yr=datein
903 elif n1==3:
904 yr=datein[:2]
905 dy=datein[2:]
906 elif n1>=8:
907 yr=datein[:4]
908 dy=datein[4:6]
909 mo=datein[6:8]
910 elif n1>=4:
911 yr=datein[:2]
912 if "ydm" in formatin:
913 dy=datein[2:3]
914 mo=datein[3:]
915 else:
916 dy=datein[2:4]
917 if n1>4:
918 mo=datein[4:]
919 if formatin.endswith("dm"):
920 if len(mo)>1:
921 mo=mo[:1]
922 elif formatin in ["yyyyddmm","yyyyddm","yyyydmm","yyyydm"]:
923 n1=len(datein)
924 if n1<=4:
925 yr=datein
926 elif n1==5:
927 yr=datein[:4]
928 mo=datein[4:]
929 elif n1>=8:
930 yr=datein[:4]
931 dy=datein[4:6]
932 mo=datein[6:8]
933 elif n1>=6:
934 yr=datein[:4]
935 if "ydm" in formatin:
936 dy=datein[4:5]
937 mo=datein[5:]
938 else:
939 dy=datein[4:6]
940 if n1>6:
941 mo=datein[6:]
942 if formatin.endswith("dm"):
943 if len(mo)>1:
944 mo=mo[:1]
945 elif formatin in ["yymmdd","yymmd","yymdd","yymd"]:
946 n1=len(datein)
947 if n1<=2:
948 yr=datein
949 elif n1==3:
950 yr=datein[:2]
951 mo=datein[2:]
952 elif n1>=8:
953 yr=datein[:4]
954 mo=datein[4:6]
955 dy=datein[6:8]
956 elif n1>=4:
957 yr=datein[:2]
958 if "ymd" in formatin:
959 mo=datein[2:3]
960 dy=datein[3:]
961 else:
962 mo=datein[2:4]
963 if n1>4:
964 dy=datein[4:]
965 if formatin.endswith("md"):
966 if len(dy)>1:
967 dy=dy[:1]
968 elif formatin in ["yyyymmdd","yyyymmd","yyyymdd","yyyymd"]:
969 n1=len(datein)
970 if n1<=4:
971 yr=datein
972 elif n1==5:
973 yr=datein[:4]
974 mo=datein[4:]
975 elif n1>=8:
976 yr=datein[:4]
977 mo=datein[4:6]
978 dy=datein[6:8]
979 elif n1>=6:
980 yr=datein[:4]
981 if "ymd" in formatin:
982 mo=datein[4:5]
983 dy=datein[5:]
984 else:
985 mo=datein[4:6]
986 if n1>6:
987 dy=datein[6:]
988 if formatin.endswith("md"):
989 if len(dy)>1:
990 dy=dy[:1]
991 elif formatin=="yyyyddd":
992 n1=len(datein)
993 if n1<=4:
994 yr=datein
995 elif n1==7:
996 yr=datein[:4]
997 dy=datein[4:]
998 isleap=is_year_leap(int(yr))
999 if len(dy)>0 and numfuncs.is_int(dy):
1000 ndy=int(dy)
1001 if ndy>0:
1002 n1=0
1003 for i in range(12):
1004 if i==1 and isleap:
1005 n2 = dayspermonth[12]
1006 else:
1007 n2 = dayspermonth[i]
1008 if ndy<=(n1+n2):
1009 mo=str(i+1)
1010 ndy -= n1
1011 dy=str(ndy)
1012 break
1013 n1 += n2
1014 else:
1015 raise ValueError("unknown date format supplied: " + formatin)
1016
1017 if len(yr)==2 and numfuncs.is_int(yr):
1018 nyr=int(yr)
1019 str1= get_current_iso_datetime()
1020 n1=20
1021 if not str1.startswith("notok:") and len(str1)>=4:
1022 str1=str1[:2]
1023 if numfuncs.is_int(str1):
1024 n1=int(str1)
1025 if prior_yr_min<= nyr <=prior_yr_max:
1026 yr= str(n1-1) + yr
1027 else:
1028 yr= str(n1) + yr
1029 if numfuncs.is_int(yr):
1030 nyr=int(yr)
1031 if numfuncs.is_int(dy):
1032 ndy=int(dy)
1033 if len(mo)>0:
1034 if has_long_month:
1035 nmo= int(hash_month_longnames[mo])
1036 elif has_short_month:
1037 nmo= int(hash_month_names[mo])
1038 elif numfuncs.is_int(mo):
1039 nmo=int(mo)
1040
1041 if nyr<=0:
1042 raise ValueError("year is less than 1: " + str(nyr))
1043
1044 if 1<= nmo <=12:
1045 if nmo==2:
1046 if isleap:
1047 n1=29
1048 else:
1049 n1=28
1050 else:
1051 n1=dayspermonth[nmo-1]
1052 if ndy>n1:
1053 raise ValueError("days are greater than month max. Month=" + str(nmo) + ",days=" + str(ndy))
1054 elif nmo<=0:
1055 raise ValueError("month is less than 1: " + str(nmo))
1056 elif nmo>12:
1057 raise ValueError("month is greater than 12: " + str(nmo))
1058
1059 if ndy<=0:
1060 raise ValueError("day is less than 1: " + str(ndy))
1061 if ndy>31:
1062 raise ValueError("day is greater than 31: " + str(ndy))
1063
1064 yr=str(nyr)
1065 mo=str(nmo)
1066 dy=str(ndy)
1067 if len(yr)<4:
1068 if len(yr)==1:
1069 yr="000" + yr
1070 elif len(yr)==2:
1071 yr="00" + yr
1072 elif len(yr)==3:
1073 yr="0" + yr
1074 if len(mo)==1:
1075 mo="0"+mo
1076 if len(dy)==1:
1077 dy="0"+dy
1078 result= yr + mo + dy
1079 if detectfmt:
1080 result += "(" + formatin + ")"
1081 except (RuntimeError,ValueError,OSError) as err:
1082 result="notok:" + str(err)
1083 return result
1084
1085def is_date_format(datein:str,formatin:str) -> bool:
1086 """
1087 Determines if date string is in specified format.
1088 Time portions should be removed prior to sending into this method.
1089 Four delimiters in date part will be automatically detected: ( - / _ . )
1090 datein: incoming date string without time part.
1091 formatin: required
1092 mmddyy, mmdyy, mdyy, mmddyyyy, mmdyyyy, mdyyyy,
1093 ddmmyy, ddmyy, dmyy, ddmmyyyy, ddmyyyy, dmyyyy,
1094 yymmdd, yymmd, yymd, yyyymmdd, yyyymmd, yyyymd,
1095 yyyyddd (3 digit day number within year),
1096 yyyyMMMdd, ddMMMyyyy (MMM = 3 letter month title like 'JAN'),
1097 'MONTHdd,yyyy', 'ddMONTH,yyyy', yyyyMONTHdd, ddMONTHyyyy, yyMONTHdd, ddMONTHyy (MONTH full title),
1098 *dmmyyyy, mm*dyyyy, *mddyyyy, dd*myyyy (*= can be 1 or 2 characters)
1099 With these formats, incoming string must have all characters required so for mmddyyyy there must be
1100 8 characters meaning 1122011 fails but 01122011 is good.
1101 Leading title of day is removed like for Wednesday, March 14, 2001 which will be changed to
1102 March 14, 2001 and then will match formatin of MONTHdd,yyyy since spaces are removed
1103 Returns - bool True/False
1104 """
1105
1106
1107 is_date_fmt:bool=False
1108 result:str=""
1109 try:
1110 result= convert_date_to_iso(datein,formatin)
1111 if len(result)>0 and not result.startswith("notok:"):
1112 is_date_fmt=True
1113 except (RuntimeError,ValueError,OSError) as err:
1114 print("ERROR:" + str(err))
1115 return is_date_fmt
str convert_excel_date_to_iso(str strin)
Definition datefuncs.py:255
str convert_date_to_iso(str datein, str formatin, bool detectfmt=False)
Definition datefuncs.py:386
str get_current_iso_datetime(bool inctime=False, str tz="")
Definition datefuncs.py:27
bool is_year_leap(int nyear)
Definition datefuncs.py:364
bool is_date_format(str datein, str formatin)
str is_iso_date_str(str strin, bool timereq=False)
Definition datefuncs.py:56