Einfacher Datums-Stringparser

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

Einfacher Datums-Stringparser

Beitrag von Derren »

Mit diesem Code kann man ein Datum, welches als String vorliegt (numerisch, nichts mit "Sept" etc) in ein gültiges PB-Datum verwandeln (unix timestamp).

Man spart sich den Parser (Stringfield etc) und verwendet stattdessen einfach eine Maske, ähnlich FormatDate().
Unten im Code sind ein paar Beispiele.
Wird ggf. noch erweitert^^

Code: Alles auswählen

Procedure DateFromString(dateString.s, format.s, strict = #False)
	Protected *c_date.character, *c_format.character
	Protected c_date.c, c_format.c
	Protected sizeOfChar = SizeOf(character)	
	Protected.s year, month, day, hour, minute, second
	Protected pos.i, year_int.i	
	*c_date = @dateString
	*c_format = @format
	
	While *c_date\c <> 0
		pos+1
		c_date=*c_date\c
		c_format=*c_format\c			
		Select c_format
			Case 'Y','y' : year = year + Chr(c_date)
			Case 'M','m' : month = month + Chr(c_date)
			Case 'D','d' : day = day + Chr(c_date)
			Case 'H','h' : hour = hour + Chr(c_date)
			Case 'I','i' : minute = minute + Chr(c_date)
			Case 's','s' : second = second + Chr(c_date)
			
			Default 
				If strict = #True
					If c_date <> c_format
						Debug "STRICT: Error at position "+Str(pos)+": <"+Chr(c_date)+"> in DATE should be <"+Chr(c_format)+">"
						ProcedureReturn -1
					EndIf 
				EndIf 
		EndSelect 
		*c_date = *c_date+sizeOfChar
		*c_format = *c_format+sizeOfChar
	Wend 	
	year_int.i = Val(year)		
	If Len(year)=2
		If year_int>69
			year = "19" + year
		Else 
			year = "20"+year
		EndIf 
		year_int.i = Val(year)
	EndIf 
	
	If year_int<1970 
		Debug "Date must be greather than Jan 1st 1970"
		ProcedureReturn -1
	EndIf
	If year_int>2037 
		Debug "Date must not be greater than Dec 31st 1937" ;eigentlich 19. Jan. 2038, laut Hilfe, aber die paar Tage braucht auch niemand...
		ProcedureReturn -1
	EndIf
	
	ProcedureReturn Date(year_int, Val(month), Val(day), Val(hour), Val(minute), Val(second))	
EndProcedure 

date = DateFromString("01.17.89 19:17'33", "MM.DD.YY HH:ii'ss")
If date>-1
	Debug FormatDate("%DD.%MM.%YYYY - %hh:%ii:%ss", date)
EndIf 

date = DateFromString("01.17.89 19:17'33", "MM~DDuYY!HH-ii*ss")
If date>-1
	Debug FormatDate("%DD.%MM.%YYYY - %hh:%ii:%ss", date)
EndIf 

date = DateFromString("01.17.89 19:17'33", "MM~DDuYY!HH-ii*ss", 1) ; strict mode
If date>-1
	Debug FormatDate("%DD.%MM.%YYYY - %hh:%ii:%ss", date)
EndIf 

date = DateFromString("01.17.2089 19:17'33", "MM~DDuYY!HH-ii*ss") ;fehlerhafte ausgabe durch 4-stelliges Jahr im Datum, aber nur YY in der Maske
If date>-1
	Debug FormatDate("%DD.%MM.%YYYY - %hh:%ii:%ss", date) + "  -  fehlerhafte Ausgabe"
EndIf 

date = DateFromString("01.17.2089 19:17'33", "MM.DD.YY HH:ii'ss", #True) ;strict mode
If date>-1
	Debug FormatDate("%DD.%MM.%YYYY - %hh:%ii:%ss", date)
EndIf 
Signatur und so
c4s
Beiträge: 1235
Registriert: 19.09.2007 22:18

Re: Einfacher Datums-Stringparser

Beitrag von c4s »

Sieh dir mal ParseDate() an... ;)
"Menschenskinder, das Niveau dieses Forums singt schon wieder!" — GronkhLP ||| "ich hogffe ihr könnt den fehle endecken" — Marvin133 ||| "Ideoten gibts ..." — computerfreak ||| "Jup, danke. Gruss" — funkheld
sibru
Beiträge: 265
Registriert: 15.09.2004 18:11
Wohnort: hamburg

Re: Einfacher Datums-Stringparser

Beitrag von sibru »

geht auch noch "freier":

Code: Alles auswählen

;Modul      DatumScan Version 1.08 vom 02.08.2015
#PB_Vers  = "4.20"
;
;Funktion:  liefert UnixTime zu einer Datums-/ZeitAngabe mit beliebigen Trennzeichen, jedoch definierter Reihenfolge
;
;Aufruf:    UnixTime.l = DatumScan(Datum$ {,Maske$})
;           Datum$:  Datums-/Zeit-Angaben mit beliebigen nicht-Nummerischen Zeichen (auch fhrend) als
;                    Trennzeichen, nur die nummerischen Anteilen (=Zahlenwerte) werden bercksichtigt
;           Mask$:   definiert die Bedeutung der Zahlenwerte in Datum$ entsprechend an PB-DatumsMasken
;                    (D = Date,  M = Monat, Y = Year, H = Hour, I = Minute und S = Sekunde).
;                    Alle anderen Zeichen sind mglich, werden jedoch fehlermeldungsfrei ignoriert.
;                    Die PB-DatumsMaskenTeile wie %YYYY werden korrekt erkannt, Einzelzeichen (hier Y)
;                    in Maske sind jedoch ausreichend
;                    Wird Maske nicht angegeben, so wird "DMYHIS" benutzt (="%DD %MM %YY{YY} %HH %II %SS)
;
;#jaPBeExt exit
;==========  Begin Modul "ZiffBeg.PBI"  ==========
;Modul      ZiffBeg Version 1.02 vom 20.04.2014
#PB_Vers  = "4.20"
;
;Funktion:  liefert Wert fhrender Zahl in einem String und verkrzt Diesen entsprechend
;           (hnlich Modul Wort, jedoch ohne Trennzeichen, dafr Ziffern-basierend)
;
;Aufruf:    Zahl.d = ZiffBeg(@String$  {Typ})
;           String$: Text$, der ggf. mit Ziffer(n) beginnt, ggf. fhrende Leerzeichen
;                    werden ignoriert
;           Typ:     geben an, was in Strung$ vorkommen kann (wenn nicht definiert: integer)
#ZiffBeg_Int = 0     ;integer (Ganzzahl, Ziffern 0..9 und kein Kommata mglich)
#ZiffBeg_Real = 1    ;real (KommaZahl, Ziffern 0..9 sowie Komma . oder , mglich)
#ZiffBeg_Hex = 2     ;Hex (HexaDezimal, Ziffern 0..9 und A..F und keine Kommata mglich)
#ZiffBeg_noZiff = 16 ;entfernt alle nicht-Ziffern am Begin von String$, liefert immer 0 !!!
;           Diese Funktion liefert den nummerischen Wert, der am Anfang von String$
;           angegeben ist und String$ wird entsprechend verkrzt. String$ kann nach
;           Funktionsende ggf. fhrende Leerzeichen enthalten.
;
;Anwendungs-Beispiel:
;  Wert$ = "12.34"
;  Std.d = ZiffBeg(@Wert$)        --> Std=12 Wert$=".34"
;  ZiffBeg(@Wert,#ZiffBeg_noZiff) --> liefert 0, Wert$="34" (auch mehrere Trennzeichen {=nicht-Ziffern} mglich)
;  min.d = ZiffBeg(@Wert$)        --> min=34, Wert$=""
;#jaPBeExt exit
Procedure.d ZiffBeg(*String, Typ = #ZiffBeg_Int);- fhrende Ziffern holen, Typ: 0=int 1=real (dann 1 . oder , mglich) oder 2=hex (0..9+A..F)
  Protected noZiff, numChars$, numVal$, String$, Wort$
  If Typ> = #ZiffBeg_noZiff : Typ - #ZiffBeg_noZiff : noZiff = 1 : EndIf
  If Typ = #ZiffBeg_Hex
    numChars$ = "0123456789ABCDEF"
    numVal$ = "$" ;Fnc "val()" liefert Dez of Hex
  Else
    numChars$ = "0123456789"
    If Typ = #ZiffBeg_Real : numChars$ + ",." : EndIf ;Kommats zulassen
  EndIf
  If *String>1
    String$ = LTrim(PeekS(*String))
    If noZiff ;fhrende nicht-Ziffern entfernen
      While FindString(numChars$, Left(String$, 1), 1) = 0 And String$>""
        String$ = LTrim(Mid(String$, 2))
      Wend
    Else ;fhrende Ziffern separieren
      While FindString(numChars$, Left(String$, 1), 1)And String$>""
        numVal$ + Left(String$, 1)
        If FindString(",.", Left(String$, 1), 1);Kommata:
          numChars$ = Left(numChars$, Len(numChars$) - 2);kein weiteres Kommata zulassen
        EndIf
        String$ = Mid(String$, 2)
      Wend
    EndIf
    PokeS(*String, String$);RestParameter setzen
  EndIf
  numVal$ = ReplaceString(numVal$, ",", ".")
  ProcedureReturn ValD(numVal$)
EndProcedure
;==========  Ende Modul "ZiffBeg.PBI"  ==========

Macro _DS_Fld(kz)
  Wert(FindString(Mask2$, kz, 1))
EndMacro

Procedure DatumScan(Datum$, Mask$ = "DMYHIS")
  Protected Feld, Mask2$
  Protected Dim Wert(7)
  While Datum$>"" ;{Datum$-Teile in Wert(1..?) eintragen
    Feld + 1
    ZiffBeg(@Datum$, #ZiffBeg_noZiff);fhrende nicht-Numeric-Zeichen (Trennzeichen) entfernen
    Wert(Feld) = ZiffBeg(@Datum$);Zahlenwert holen bis erstes nicht-Numeric-Zeichen
  Wend ;}
  Mask$ = UCase(Mask$);{Maske filtern: nur noch gltige Zeichen
  While Mask$>""
    If FindString("DMYHIS", Left(Mask$, 1), 1)>0 And FindString(Mask2$, Left(Mask$, 1), 1) = 0 : Mask2$ + Left(Mask$, 1): EndIf
    Mask$ = Mid(Mask$, 2)
  Wend ;}
  ProcedureReturn Date(_DS_Fld("Y"), _DS_Fld("M"), _DS_Fld("D"), _DS_Fld("H"), _DS_Fld("I"), _DS_Fld("S"))
EndProcedure
Bild Bild
Antworten