biber
Goto Top

Workshop Batch for Runaways - Part III - Datums- und Zeitvariablen im Batch

Immer wieder gern gefragt.. wie kann ich im CMD-Batch Datums- und Zeitvariablen bilden und nutzen.
Ein paar Anregungen dazu hier im Workshop.

Weil es doch immer wieder mal als Frage gepostet wird, möchte ich heute versuchen, den grundsätzlichen Umgang mit Datums-und Zeitwerten in Batchdateien zu skizzieren.

Wozu brauche ich sowas?

Ich habe immer wiederkehrende Anforderungen in der täglichen Admin-Arbeit ( = sich wiederholende Abläufe = berechenbar und automatisierbar = geeignet für Batch).
Unter anderen gibt es zwei Fälle, die sicherlich jede(r) Admin(e) kennt und auch fast jeder Normal-User:

a) Bestimmte Dateien mit einem "von außen sichtbaren Zeitstempel" versehen oder neu anlegen. Beispiel sind Logfiles, die ich mit einem Namen anlege, die das aktuelle Datum erhalten.
Und innerhalb der Logdateien habe ich sicherlich Zeilen, die auch noch die aktuelle Uhrzeit enthalten.

b) Dateien mit einem Zeitstempel versehen, der ihrem Entstehungsdatum oder dem letzten Änderungsdatum entspricht. Beispiel für Entstehungsdatum (auch hier aus dem Forum): Faxprogramme, die eingehende Faxe unter zufällig generierten Dateinamen speichern. Selbst ein Umbenennen dieser Dateien in Dateien nach dem Muster 2000-10-01_13h34.tif macht das Wiederfinden einfacher.
Beispiel für letztes Änderungsdatum: Dateien, deren Namen gleich bleibt, von denen ich aber die Vorgängerversionen erhalten will. Die Anforderung gibt es ständig, von Worddateien bis zu Codeschnipseln... irgendwann gibt es eben eine neuere Version einer Datei und ich möchte die Vorgängerverion nicht überschreiben lassen.

Vorüberlegungen: Was brauche ich alles und was ist das Ziel?

Ziel: ich will Datums- und Zeitinformationen detailliert und strukturiert verwenden können.
Am besten in abfragbare Variablen wie "Tag", "Monat" oder "Jahr" gespeichert.
Ich brauche minimal:

- Datum (aktuell) mit einzeln identifizierbarem Tag, Monat und Jahr - nenne ich mal TT, MM, JJ
- Zeit (aktuell) mit einzeln identifizierbaren Stunden und Minuten - nenne ich mal hh und min

und entsprechend
- Datei-Datum (Erstellungs- bzw. letztes Änderungsdatum) mit einzeln identifizierbarem Tag, Monat und Jahr
- Datei-Zeit (aktuell) mit einzeln identifizierbaren Stunden und Minuten

Optional:
- Sekunden und Tausendstelsekunden bei der Zeit .. (würden dann ss und ms heißen)
- den Wochentag eines Datums (=DoW Day of Week)
- abgeleitetes aus dem Datum wie Tag des Jahres (=DoY Day of Year), Kalenderwoche (KW), Quartal...

Was habe ich?

Tja, jetzt der unangenehme Teil. M$ liefert mir, dem Windows-User, zwar die oben beschriebenen Datums- und Zeitinformationen optisch wiedererkennbar mit den CMD-Befehlen date und time , aber in der Darstellung leider abhängig von
  • Ländereinstellungen (die Darstellung von Silvester 2005 kann sein "31.12.2005" oder "2005/12/31" oder "31-12-05" oder oder oder..)
  • Betriebssystemversionen... auch M$ geht mit dem Geschmack der Zeit. Standard-Darstellung des Datums bei "älteren" OS-Versionen: "Sa, 31.12.2005". "Neuere" OS-Versionen "31.12.2005"
  • Benutzereinstellungen Zu allem Überfluss kann der Benutzer auch noch standardmäßig "seine" Umgebung definieren mit "Langen Datumsformaten" wie "Samstag, 31. Dezember 2005" oder "Kurzen Datumsformaten" wie "Sam 31.Dez 2005"
Ich weiß nicht, was Euch in solchen Momenten rausrutscht an spontanen Äußerungen, ich sage dann immer:
"Es liegt eine fehlende Passgenauigkeit zwischen Anforderungen und Voraussetzungen vor."
Nach dem Spruch geht es mir immer besser.

Wie komme ich zu meinen Datumsvariablen?

Gehen wirs locker an und erstmal zum CMD-Prompt. Mal sehen, wie wir es aufdröseln.
Ein kurzer Blick in die Hilfe zeigt uns drei Varianten, um an Datums/Zeit-Information via CMD-Prompt zu kommen.
- "Date" bzw "Time" eingeben (erwartet Benutzereingabe);
- "Date /t" bzw. "Time /t" (ohne Benutzereingabe, nur Ausgabe; Schalter /t seit Win2000?)
- Abfrage der immer vorhandenen Variablen %date% und %Time% (die Variablen gibts noch nicht unter WinNT, Win9x. Ausweichstrategie siehe letzte Zeile der CMD-Spielereien)

Ich zeig mal, was rauskommt an meinem Rechner, so wie der halt zufällig eingestellt ist:
 
>date
Aktuelles Datum: Sa 01.10.2005
Geben Sie das neue Datum ein: (TT-MM-JJ)

>time
Aktuelle Zeit: 12:43:14,96
Geben Sie die neue Zeit ein:

>date /t & time /t
Sa 01.10.2005
12:43

>echo %date% %time%
Sa 01.10.2005 12:44:00,82

echo exit|cmd /q /k"prompt $D $T"  
Sa 01.10.2005 12:45:03,29

Ich ändere mal in den Systemsteuerung->"Ländereinstellungen"/"Regional Settings" das Datumsformat auf das angebotene "JJJJ-MM-TT" und erhalte in einem neuen CMD-Fenster:
>date
Aktuelles Datum: Sa 2005-10-01
Geben Sie das neue Datum ein: (JJ-MM-TT)
>date /t
Sa 2005-10-01
>echo %date%
Sa 2005-10-01
Weshalb mir M$ hier unaufgefordert den Wochentag mitliefert, weiß nur Bill allein..*seufz
Ist aber auch nicht immer so... manchmal fehlt der Wochentag auch. Wunderwelt Windows. face-sad

Auch die DIR-Anzeige übernimmt das Datumsformat:
>dir *.reg
2005-08-12  21:51                1.272 MyCommandPrompt.reg
2005-09-19  00:16                  920 RegDLL_Restore.reg
2005-09-19  00:16                   50 RegDLL_Remove.reg
Na, fragen wir doch mal vom CMD-Prompt aus nach, wie das wohl in der Registry abgelegt ist.
(bei Fragen zu Elementarbefehlen wie REG.exe, FIND.exe bitte die entsprechende Hilfe konsultieren)
Achtung: Ich verwende hier die Reg.exe 2.00. Die NT-Variante und auch die XP-Variante 3.00 haben etwas andere Syntax. Ist aber hier nicht weiter wesentlich.
>reg query "HKCUControl PanelInternational" /s  
 HKEY_CURRENT_USERControl PanelInternational
    iCountry    REG_SZ  49
    iCurrDigits REG_SZ  2
    iCurrency   REG_SZ  3
    iDate       REG_SZ  2
    iDigits     REG_SZ  2
    iLZero      REG_SZ  1
    iMeasure    REG_SZ  0
    iNegCurr    REG_SZ  8
    iTime       REG_SZ  1
    iTLZero     REG_SZ  1
    Locale      REG_SZ  00000407
    s1159       REG_SZ
    s2359       REG_SZ
    sCountry    REG_SZ  Deutschland
    sCurrency   REG_SZ
    sDate       REG_SZ  -
    sDecimal    REG_SZ  ,
    sLanguage   REG_SZ  DEU
    sList       REG_SZ  ;
    sLongDate   REG_SZ  dddd, d. MMMM yyyy
    sShortDate  REG_SZ  yyyy-MM-dd
    sThousand   REG_SZ  .
    sTime       REG_SZ  :
    sTimeFormat REG_SZ  HH:mm:ss
    iTimePrefix REG_SZ  0
    sMonDecimalSep      REG_SZ  ,
    sMonThousandSep     REG_SZ  .
    iNegNumber  REG_SZ  1
    sNativeDigits       REG_SZ  0123456789
    NumShape    REG_SZ  1
    iCalendarType       REG_SZ  1
    iFirstDayOfWeek     REG_SZ  0
    iFirstWeekOfYear    REG_SZ  2 
Scheinen ja auf den ersten Blick nur drei Werte zu sein, die kleine Steuerschrauben für die Datumsdarstellung sein könnten, nämlich idate, sDate und sShortDate.
sShortdate ist selbst erklärend, sDate als Datumstrennzeichen auch nicht sonderlich aufregend.
Zu iDate gibt die M$-Dokumentation folgendes her:

iDate
HKCUControl PanelInternational
Data type Range Default value
REG_SZ 0 | 1 | 2 0

Description
Specifies the default format for displaying short dates.

Value Meaning
0 mm/dd/yy
1 dd/mm/yy
2 yy/mm/dd

Mal weiterspielen (ändern von iDate von 2 auf 1 und danach auf 0):

>reg add "HKCUControl PanelInternational" /v idate /t REG_SZ /d 1 /f  
>date
Aktuelles Datum: Sa 01-10-2005
Geben Sie das neue Datum ein: (TT-MM-JJ)

>date /t
Sa 01-10-2005
>reg add "HKCUControl PanelInternational" /v idate /t REG_SZ /d 0 /f  
>date /t
Sa 10-01-2005

>date
Aktuelles Datum: Sa 10-01-2005
Geben Sie das neue Datum ein: (MM-TT-JJ)
Okay, Microsoft, hab ich in Ansätzen verstanden, aber... *kopfkratz*
  • ?? Wieso werden mir die Jahrhunderte angezeigt? "01.10.05" will ich doch nur...
  • ?? Wieso kommt mal der Wochentag und mal nicht? Heute war er ja immer da... aber weg war er auch schon mal...
  • ?? Und wieso bekomme ich den Wochentag im deutschen Windows als 2 Zeichen ("Mo", "Di", "Mi",..) und im US-Amerikanischen als "Mon", "Tue".. mit drei Zeichen?
  • ?? Und wenn ich da tatsächlich "Montag" ..."Sonntag" als ganzes Wort stehen haben will?? Bekomm ich nicht hin..
  • ?? Und wo kann ich den Output bewundern, den ich mit sLongdate einstelle?
Ein paar Fragen bleiben schon offen... deinen Jungs möchte ich schon mal beim Coden zusehen, Billy..

Fassen wir mal zusammen, was wir jetzt haben.
Folgender kleinster gemeinsamer Nenner gilt unabhängig von Länder- und Usereinstellungen und OS-Version:
  • beim Output von "date", "date /t", kommt eine Zeile zurück, die Tag, Monat, Jahr enthält
  • der Output von "date /t" bzw "time /t" steht auch in der Umgebungsvariablen %date% bzw %time%
  • die Reihenfolge von Tag, Monat, Jahr kann ich aus der Registry über idate ermitteln
  • das Trennzeichen im Datum kann aus der Registry über sdate ermitteln
  • ob ich den Datumsstring mit oder Wochentagsanzeige zurückbekomme, kann ich weder vorher wissen noch steuern.
  • wenn ich keine Registryzugriffe machen will, kann ich die Tag/Monat/Jahr.Reihenfolge auch dem "date"-Output ermitteln.
  • bei "time", "time /t" scheint es nicht so aufregend zu werden. Da muss ich ggf. die Tausendstel-Sekunden entsorgen und bei den Stunden eine führende Null ergänzen, falls die fehlt. Aber mehr scheint da nicht zu tun zu sein.
  • Ermittlung von Tag, Monat, Jahr, Stunde, Minute, Sekunde ist also per Batch mit einem Einzeiler möglich. Das war ja der Pflichtteil.
  • Ermittlung von Wochentag, Kalenderwoche oder Tag des Jahres in Batchdateien scheint nicht so ganz aktiv unterstützt zu werden von Microsoft aus. Braucht man da in Redmond anscheinend nicht. *Kopfschüttelt*

Also beschränke ich mal auf das Abgreifen von (ich sags mal im Hobby-Programmierer-Slang, ihr versteht mich ja *gg) TT, MM, JJ und hh, min, ss.
Berücksichtigen will ich wenigstens auch, dass bei englischem/US-amerikanischem OS nicht TT und JJ geliefert werden, sondern DD und YY.

Machen wir erstmal einen simplen Batch-Oneliner zum Warmwerden.
Strategie, ich hole mir Zeit und Datum aus dem Output von "prompt $D $T" und der "date"-Abfrage.
Ich verzichte mal auf alle Registry-Abfragen (nur Bordmittel).
Und unterstelle, die Datums/Zeittrennzeichen sind -egal welche Sprache und welches Format- in diesen hier: (":/.-, ") enthalten.
Und gehe auch davon aus, dass ein Wochentag mitgeliefert wird.
So sähen die beiden Outputs also aus, die ich verwurste (Win2K, deutsch):
>echo exit|cmd /q /k"prompt $D $T"  
Sa 01.10.2005 11:38:26,79

>echo.|date
Aktuelles Datum: Sa 01.10.2005
Geben Sie das neue Datum ein: (TT-MM-JJ)
Lassen wir mal den Oneliner(ich hab ihn auf ein paar Zeilen verteilt) "GetAllSystemDateTimeInfos_1.bat" drüberlaufen.
---snipp Codefragment GetAllSystemDateTimeInfos_1.bat-----
For /f "tokens=1-7 delims=:/.-, " %%i in ('echo exit^|cmd /q /k"prompt $D $T"') do (  
    	For /f "tokens=2-4 delims=/-,() skip=1" %%a in ('echo.^|date') do (  
	for %%@ in ("dow=%%i" "DateOrder=%%a-%%b-%%c" "%%a=%%j" "%%b=%%k" "%%c=%%l" "hh=%%m" "min=%%n" "ss=%%o") do set %%@  
	)
)
::---snipp Codefragment GetAllSystemDateTimeInfos_1.bat-----
Ich rufe den Schnipsel mal auf und frag die gesetzten Variablen ab:
>GetAllSystemDateTimeInfos_1 >nul
For %i in (DateOrder DoW DD TT MM JJ YY hh min ss) do @if defined %i set %i
DateOrder=TT-MM-JJ
DoW=Sa
TT=01
MM=10
JJ=2005
hh=11
min=39
ss=03
Bei US-Versionen würde rauskommen:
DateOrder=YY-MM-DD
DoW=Sa
DD=01
MM=10
YY=2005
hh=11
min=39
ss=03
Damit hätten wir zumindest alles, was wir zum Beispiel zum Zusammenbasteln eines neuen Dateinamens für ein Logfile oder ähnliches brauchen.
Beispiel: Aufgabe sei, das Logfile SQL.Log unzubenennen in 2005-10-01_11h39_SQL.Log
>if defined TT if defined JJ ren SQL.Log %JJ%-%MM%-%TT%_%hh%h%min%_SQL.Log
>if defined DD if defined YY ren SQL.Log %YY%-%MM%-%DD%_%hh%h%min%_SQL.Log
Ich denke, den Faden brauche ich nicht weiterspinnen.
Problem dabei ist nur, dass ich eben -weil ich mich NICHT darauf verlassen kann, dass M$ mir den führenden Wochentag im Output liefert, dieser Codeschnipsel in die Grütze fasst, wenn die Output-Formatierung von Date keinen Wochentag enthält.

Also, hilft nichts, wenn ich flexibel bleiben will, muss ich die Strategie ändern. Ich gehe, um auf der sicheren Seite zu stehen, von einem Output aus, der keinen Wochentag enthält.
Und falls der drin sein sollte, schmeiss ich ihn weg.

Wie überzählige Teile eines Output zu entsorgen sind, habe ich schon einige Male vorgeturnt hier im Forum, das Beispiel mit dem Wochentag aus der Datumsausgabe bzw die Tausendstel-Sekunden aus dem Time-String zum Beispiel in diesem Thread (Variablen und Wildcards)
::--------kurzes Codebeispiel zum "schnellen" Bilden von Datums/Zeitstempeln; passt meistens.  
...
:CopyWithRename
Set "DatePrefix=%date%" & Set "TimeSuffix=%time%"  & REM Namenserweiterungen für die zu kopierende Datei   
FOR /f "tokens=2" %%i in ("%datePrefix%") do Set DatePrefix=%%i  
FOR /f "tokens=1-3 delims=." %%i in ("%datePrefix%") do Set Dateprefix=%%k%%j%%i  
for /F "tokens=1-3 delims=:," %%i in ("%time%") do Set "TimeSuffix=%%i%%j%%k"  
Set "TimeSuffix=%TimeSuffix: =0%"  
..
Ich mache also schweren Herzens aus dem Oneliner oben einen Mehrzeiler.
::-------snipp GetAllSystemDateTimeInfos_2.bat -----
@echo off & setlocal
:: (c) Biberware 2005 Placed in the Public Domain Oct 2005 for Educational Purposes
:: Ermittelt ein paar Datums/Zeitvariable aus dem CMD-Environment (Systemdatum/Systemzeit) OHNE REG-Abfragen
:: Einschränkung: Sollte schon NT oder höher sein, Deshalb die nächte Zeile
IF NOT "%OS%"=="Windows_NT" echo "%0 läuft nur unter WinNT oder höher. Sorry." && GOTO :eof  
SET "AllDateTimeVars=DateOrder KW DoW DoY DD TT MM JJ YY hh min ss ms"  
FOR %%i in (%AllDateTimeVars%) do @if defined %%i set %%i=
:: Datum will ich OHNE Wochentag haben. Zwischenschritt mit MyDateOhneDoW
Set MyDateOhneDoW=%date%
:: Die nächste Code-Zeile ändert nur dann etwas, wenn ein Wochentag mitgeliefert wird.
:: Aus "Sa 01.10.2005" wird dann "01.10.2005"  
FOR /F "tokens=2" %%i in ("%MyDateOhneDoW%") do Set MyDateOhneDoW=%%i  
FOR /F "tokens=1-7 delims=:/.-, " %%i in ("%MyDateOhneDow% %time%") do (  
    	For /f "tokens=2-4 delims=/-,() skip=1" %%a in ('echo.^|date') do (  
	for %%@ in ("DateOrder=%%a-%%b-%%c" "%%a=%%i" "%%b=%%j" "%%c=%%k" "hh=%%l" "min=%%m" "ss=%%n" "ms=%%o") do set %%@  
	)
)
:: Lass sehen, was wir haben....
For %%i in (%AllDateTimeVars%) do @if defined %%i set %%i
::-------snapp GetAllSystemDateTimeInfos_2.bat
Test am CMD-Prompt:
>GetAllSystemDateTimeInfos_2
DateOrder=TT-MM-JJ
TT=01
MM=10
JJ=2005
hh=13
min=36
ss=30
ms=02
Okay, schauen wir mal, ob wir mit Auslesen der Registry-Werte schneller, besser, vollständiger zum Ziel kämen.
Ich turn das mal exemplarisch vor für das System-Datum (System-Zeit läuft analog. Schenk ich mir.)
::--------snapp GetAllSystemDateTimeInfos_3.bat
@ECHO OFF
:: (c) Biberware 2005 Placed in the Public Domain Oct 2005 for Educational Purposes
:: Ermittelt ein paar Datumsvariablen aus dem CMD-Environment (Systemdatum) <b>MIT </b>REG-Abfragen

IF NOT "%OS%"=="Windows_NT" echo "%0 läuft nur unter WinNT oder höher.Sorry." && GOTO :eof  
Setlocal
SET "AllSystemDateTimeVars=DateOrder KW DoW DoY DD TT MM JJ YY hh min ss ms"  
:: Systemdatum abgreifen in lokale Variable, erstmal so wie geliefert
Set MyDateOhneDoW=%date%
:: Die nächste Code-Zeile ändert nur dann etwas, wenn ein Wochentag mitgeliefert wird.
:: Aus "Sa 01.10.2005" wird dann "01.10.2005"  
FOR /F "tokens=2" %%i in ("%MyDateOhneDoW%") do Set MyDateOhneDoW=%%i  
ECHO Heute ist %MyDateOhneDoW%

:: Registry-Werte in ein Temp-File auslesen mit RegEdit.exe 
:: <b>Nicht </b>mit REG.exe wegen der Reg.exe-Syntaxunterschiede je nach Version!
Set RegFile=%temp%\%random%.reg
START /W REGEDIT /E %Regfile% "HKEY_CURRENT_USERControl PanelInternational"  
:: [Anmerkung 13.2.2006 - Uuups, war mir damals gar nicht bewusst, dass der Parameter "/W" undokumentiert ist]  
:: [ Laut Hilfe mit "Start /?" heißt es "Start /Wait"... ich hatte echt nicht drüber nachgedacht. ]  
:: Auswerten der Werte iDate (0,1,2) und sDate (Datumstrennzeichen)
FOR /F "tokens=1* delims==" %%i IN ('Type %RegFile% ^| FIND /I "iDate"') DO SET iDate=%%j  
FOR /F "tokens=1* delims==" %%i IN ('Type %RegFile% ^| FIND /I "sDate"') DO SET sDate=%%j  
DEL %RegFile%

:: RegWerte weiterbehandeln... Anführungszeichen entfernen
SET iDate=%iDate:"=%  
SET sDate=%sDate:"=%  

:: Und nun in TT, MM, JJ sortieren
FOR /F "TOKENS=1-3 DELIMS=%sDate%" %%a IN ("%MyDateOhneDoW%") DO echo %%a %%b %%c  

FOR /F "TOKENS=1-3 DELIMS=%sDate%" %%a IN ("%MyDateOhneDoW%") DO (  
     IF %iDate%==0 For %%i in ("JJ=%%c" "MM=%%a" "TT=%%b" "DateOrder=MM-TT-JJ") DO SET %%i  
     IF %iDate%==1 For %%i in ("JJ=%%c" "MM=%%b" "TT=%%a" "DateOrder=TT-MM-JJ") DO SET %%i  
     IF %iDate%==2 For %%i in ("JJ=%%a" "MM=%%b" "TT=%%c" "DateOrder=JJ-MM-TT") DO SET %%i  
     )
:: Lass sehen, was wir haben....
For %%i in (%AllSystemDateTimeVars%) do @if defined %%i set %%i

ENDLOCAL
GOTO:EOF
:: --------snapp GetAllSystemDateTimeInfos_3.bat
Output wäre:
DateOrder=TT-MM-JJ
TT=01
MM=10
JJ=2005

Führe ich nicht weiter fort. Hilft mir nur wenig weiter, außer das ich tatsächlich die Tag/Monat/Jahr-Reihenfolge iDate und das Datumstrennzeichen sDate "amtlich" habe.
Aber es kann sein, dass ich diese Technik verwenden muss, wenn ich andere Registry-Einstellungen brauche, zum Beispiel
den iFirstDayOfWeek (REG_SZ, 0-6) oder den iFirstWeekOfYear(REG_SZ, 0-2) zur Berechung der Kalenderwoche.
Und, der Vollständigkeit halber: es gibt noch zwei weitere:
den iCalendarType (REG_SZ,1...nn) und darauf bezogen den folgenden Reg-Key:
HKEY_CURRENT_USERControl PanelInternationalCalendarsTwoDigitYearMax (steht z.B. der Strimg "2038" drin)
Dieser Wert zeigt an, bis zu welchem Jahr Datumswerte OHNE Jahrhundertangabe automatisch zum 21. Jahrhundert gehören.
(Also konkret: ein Datum "01.10.05" wird interpretiert als "01.10.2005", das Datum "01.10.87" nicht.)

Wer in seinem Batch sauber arbeiten will, muss die Werte aus der Registry lesen.
Wer lieber ein bisschen rumschlampt (bei der KW-Berechnung z.B.) darf gerne "Konstanten" verwenden.

Ich zeig weiter unten noch, was ich meine.
Uns fehlen ja noch unter anderem die abgeleitete Kalenderwoche, der Wochentag und der Tag des Jahres.
Können wir auf viele Arten berechnen:

Beispiel Berechnung Wochentag aus einem Datum mit Bordmitteln:
:: --- snipp GetDoW-Codeschnipsel.bat 
:: Berechnet den DoW / Tag der Woche als numerischen Wert 1=Montag...7=Sonntag
:: IN-Parameter: Para 1-3 yy, mm, dd ( Jahr 2 oder 4stellig, Monat, Tag (mit/ohne führende "0" egal)   
:: ..............Para 4 ist ein Variablennamen für den DoW/TachDerWoche
:: ..............Optional: Para 5 die Datumsgrenze (z.B. TwoDigitYearMax aus der REG). 
::...............Mein Default (vollkommen willkürlich) 2038
:: ich mach hier <b>KEINE </b> Parameterüberprüfung und ähnliches, soll ja später nicht als eigenständiger Batch verwendet werden.
:: Algorithmus frei nach [http://www.commandline.co.uk/lib/treeview/index.php Batch Function Library for Windows NT4/2000/XP/2003]
@echo off & Setlocal
IF [%5]== (set /a "TwoDigitYearMax=2038%%1000") Else set /a TwoDigitYearMax=%5%%1000  
set yy=%1 & set mm=%2 & set dd=%3 
if 1%yy% LSS 200 if 1%yy% LSS 1%TwoDigitYearMax% (set yy=20%yy%) else (set yy=19%yy%)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
endlocal&Set %4=%dow%&goto :EOF
:: --- snipp GetDoW-Codeschnipsel.bat 
Aufruf und Output wären zum Beispiel (setzen einer numerischen Wochentagsvariablen für den 2.Oktober 2005)

getDow 2005 10 2 nWochentag
nWochentag=7

Die Kalenderwoche und den Tag des Jahres kann ich auch mit reinen Batch-Bordmitteln ermitteln.
Habe ich auch schon bei ein paar Threads hier im Forum vorgekaspert.
Die skizzierte Mimik mit puren CMD-Befehlen ist wie folgt:

.. Datum ermitteln aus %date%
.. Datum entsprechend dem vorgefundenen aufdröseln in Tag, Monat, Jahr, Wochentag
.. Zwischenberechnung des TagDesJahres (abgespeckte Variante hatte ich mal skizziert unter Laufender Tag im Jahr und Zähler
.. KW berechnen als SET /A KW = %TachDesJahres% - %Wochentach% + 7 ( wenn Wochentach von 0=Mo bis 7=So gesetzt ist)
.. SET /A KW /= 7 ... fertig

Aber es geht ja auch einfacher, wenn ich bestehende Algorithmen bzw. vorhandene Funktionen nutze.
Habe ja nicht den Ehrgeiz, jedes Rad neu zu erfinden. Bei JScript und VBScript existieren ja Datums-Aufdrösel-Funktionen, also nehme ich doch diese.

Beispiel-Codeschnipsel in VBscript:
 '-------snipp DateStrings.vbs  
If wscript.arguments.count = 1 Then
   MyDate=wscript.arguments.item(0)
Else  
   Mydate=Date
End If
wScript.echo "Ausgangsdatum der Berechnungen          : " & Mydate   
wScript.echo "DoY/Tag des Jahres : datepart('y' ,Date): " & datepart("y" ,MyDate)  
wScript.echo "Kalenderwoche      : datepart('ww',Date): " & datepart("ww",MyDate)  
wScript.echo "DoW/Wochentag 1=So : datepart('w',Date) : " & datepart("w" ,MyDate)   
'--------Snapp DateStrings.vbs   
In Aktion:
 >cscript //nologo Datestrings.vbs
Ausgangsdatum der Berechnungen          : 03.10.2005
DoY/Tag des Jahres : datepart('y' ,Date): 276  
Kalenderwoche      : datepart('ww',Date): 41  
DoW/Wochentag 1=So : datepart('w',Date) : 2  

>cscript //nologo Datestrings.vbs 01.10.2005
Ausgangsdatum der Berechnungen          : 01.10.2005
DoY/Tag des Jahres : datepart('y' ,Date): 274  
Kalenderwoche      : datepart('ww',Date): 40  
DoW/Wochentag 1=So : datepart('w',Date) : 7   
Idee ist jetzt ziemlich einfach: wenn ich einen kleinen *.vbs-Zweizeiler benutze, der mir die Werte liefert, die ich in meinem Batch brauche, brauche ich nichts mit eigenen Algorithmen berechnen.
Berechnet haben möchte ich vier Werte:
- Kalenderwoche (so, wie sie z.B. in meiner Firma definiert ist)
- Kalenderwoche wie sie laut Registry errechnet wird. Ihr erinnert Euch an die beiden Werte iFirstDayOfWeek (REG_SZ, 0-6) und iFirstWeekOfYear(REG_SZ, 0-2)? Die stehen da drin, aber ich kann die nicht ändern über die GUI/Ländereinstellungen. Microsoft. *gg
- Wochentag / DoW als numerischen Wert. Egal wie durchnummeriert, ich muss es bloß wissen. Hier kommt es als 1=So, 2=Mo etc zurück
- Tag des Jahres / DoY

Okay, wenn der Zweizeiler so aussieht:
'----snipp DateKrams.vbs  
Wscript.Echo " " & DatePart("ww",Date,vbSunday,vbFirstFourDays) & " " &_  
DatePart("ww",Date) & " " & DatePart("w",Date) & " " & DatePart("y",Date)  
'------snapp DateKrams.vbs   
...dann kann ich heute, am 3.10.2005 folgenden Output vom CMD-Prompt aus erhalten:
>for /f "tokens=2-5" %a in ('cscript //nologo DateKrams.vbs') do @echo KW=%a,KWOS=%b,DoW=%c,DoY=%d  
KW=40,KWOS=41,DoW=2,DoY=276
Prinzip verstanden?
Zum Verständnis der VBS-DatePart()-Parameter empfehle ich M$-Technet Retrieving Specific Portions of a Date and Time Value.
Okay, ich bau das nochmal ein in den GetAllSystemDateTimeVars.bat.
Und wenn ich da eh schon ändere, setze ich auch noch den "sprechenden" Wochentag cDoW als "Mo", "Di", etc für deutsches und englisches Windows.
Und ich erlaube mal statt der defaultmäßigen Aufdröselung von Systemdatum/Systemzeit auch die Berechung von Datum und Zeit, die als Parameter übergeben werden.
Dann noch eine Minimalhilfe (so wie üblich; bei Aufruf mit /? poppt es hoch).
Ich setze sowohl TT wie DD und auch JJ und YY für den Einsatz unter Win (dt) und Win (en).
Dann noch einen optionalen Parameter zum Setzen der ermittelten Werte "global". Nenn ich mal /s wie SET.
Und einen Parameter /u wie UNSET.
Und einen Parameter /q wie Quiet.
Und alle Werte innerhalb eines Datums/einer Zeit will ich zweistellig, mit führender "0"
Und die Jahreszahl 4stellig, also 1997 statt 97 und 2005 statt 05.
Dann müsste jeder Noob/jede Noobine damit klarkommen,

Also dann, The Final Version 0.01 Beta --> Version 0.10 23.10.2008 --> version 0.11. 20.02.2012 -> version 012. 11.10.2013.
::-------snipp GetAllDateTimeInfos.cmd -----
@echo off & setlocal
:: (c) Biberware 2005 Placed in the Public Domain Oct 2005 for Educational Purposes
:: Ermittelt Datums/Zeitvariable aus dem CMD-Environment und stellt sie als Variablen bereit
:: Einschränkung: Sollte schon NT oder höher sein. Deshalb die erste Zeile
:: Vers. 0.02 Minor Bugfix; siehe zweiten Kommentar unten. 14.12.2005 by Biber
:: Vers. 0.06a Bugfix/Workarounds für Formfeed-Bug W2K und "Set /a 09"-Bug  
:: -------------- Und alle SETs in Anführungszeichen. Siehe unten. 13.2.2006
:: Vers. 0.06b Aus Kompat-Gründen jetzt ohne erwartete "DelayedExpansion"-Reg-Voreinstellung  
:: Vers. 0.07 Aufrufe mit Parameter/? bzw /h NACH dem Setzen der %AllDateTimeVar%s verschoben
:: Vers. 0.08 Windows NT scheint die Variablen %date% und %time% nicht zu kennen
:: Vers. 0.09 undokumentierten Befehl "echo." durch undokumentierten "echo" ersetzt  
::            Ausgabe nur der "eigenen" Variablen ---> set "eigenevar"|find "eigenevar="  
:: Vers. 010 Bugfix Variable AlldateTimeVars wurde nicht gelöscht, siehe srmerlins Kommentar unten
:: Vers. 011 Erweiterung und Feinschliff, s.u. pieh-ejdsch schreibt am 19.02.2012, 17:24:36 Uhr 
:: Vers. 012 Erweiterun Umgang mit chinesischem Datumsformat von Robsei, siehe Kommentar vom 11.10.2013 unten.
IF NOT "%OS%"=="Windows_NT" echo "%0 läuft nur unter WinNT oder höher. Sorry." && GOTO :eof  

For %%i in ( %1 %2 %3 %4) Do (
If /i [%%i]==[/s] Endlocal
If /i [%%i]==[/u] Endlocal
If /i [%%i]==[/h] %0 /?
)

SET "AllDateTimeVars=DateOrder KW OSKW DoW DoY cDoW DD TT MM JJ YY hh min ss ms"  
If /i [%1]==[/?] goto Syntax

FOR %%i in (%AllDateTimeVars%) do @if defined %%i set "%%i="  
:: Datum will ich OHNE Wochentag haben. Zwischenschritt mit INDate

:: erweitert, Parameter erlaubt.. Param1=Datumswert;Param2=Zeit
For %%i in (%1 %2 %3) Do If /i %%i==/u Set "AllDateTimeVars=" & goto :Cleanup  
:: Vers 0.09 11.7.2007:  Windows NT kennt weder %date%  noch %time%.
:: Deshalb lieber folgende Syntax wählen statt der beiden Zeilen darunter:
FOR /F "delims=" %%i in ('date/T') do Set "INDate=%%i"  
:: Vers. 0.10 20.9.2007: time/T hat Format "hh:mm" ; %time% dagegen "hh:mm:ss, nn"  
:: ----> Time /t nur im Notfall - wenn %time% nicht definiert ist - verwenden.
IF       defined time     set "INTime=%time%"  
IF not defined INtime FOR /F "delims=" %%i in ('time/T') do Set "InTime=%%i"  
:: --- ersetzt durch Vers. 010 FOR /F "delims=" %%i in ('time/T') do Set "INTime=%%i"  
::  -- ersetzt Vers. 0.09 Set "INDate=%date%"  
::  -- ersetzt Vers. 0.09 Set "INTime=%time%"  

If NOT [%1]== If /i [%1] NEQ [/q] If /i [%1] NEQ [/s] set "INDate=%1"  
If NOT [%2]== If /i [%2] NEQ [/q] If /i [%2] NEQ [/s] set "INTime=%2"  

:: Die nächste Code-Zeile ändert nur dann etwas, wenn ein Wochentag mitgeliefert wird.
:: Aus "Sa 01.10.2005" wird dann "01.10.2005"  
REM vor v 012: FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
:: Beschränkung auf existierende Datumsformate siehe Posting mit CAT unten.
:: Das Format "Mo, 13. Februar 2006" mit zwei Leerzeichen würde NICHT funktionieren!  
REM Datumsformat “Sa 01.10.2005“
If "%indate:~5,1%" == ":" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "/" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "." FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "-" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "," FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
REM Datumsformat “Fri 10/11/2013"  
If "%indate:~6,1%" == ":" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "/" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "." FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "-" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "," FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
REM Datumsformat “2013-10-10 ???“ (??? sind 3 chinesische Zeichen)
If "%indate:~4,1%" == ":" FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "/" FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "." FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "-" FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "," FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
:: Erweiterung von RobSei 11.10.2013 -- siehe in den Kommentaren dieses Datums.
FOR /F "tokens=1-7 delims=:/.-, " %%i in ("%INDate% %INTime%") do (  
       FOR /F "tokens=2-4 delims=./-,() skip=1" %%a in ('date^<nul') do (  
        for %%@ in ("DateOrder=%%a-%%b-%%c" "%%a=%%i" "%%b=%%j" "%%c=%%k" "hh=%%l" "min=%%m" "ss=%%n" "ms=%%o") do set %%@  
          )
     )
FOR /F "delims=DTMYJ-dtmyj" %%i in ("%DateOrder%") do set "Other=%%i"&if not defined JJ call set "JJ=%%%%i%%"  
For %%i in (ss ms) do if not defined %%i set "%%i=00"  
:: ----- v005 Workaround wegen W2K-Formfeed-Bug: Kommata als Delimiter im VBS-Schnipsel-Output
SET "vbsSnippet=%temp%\%random%.vbs"  
echo If wscript.arguments.count = 1 Then dDate=wscript.arguments.item(0) Else dDate=Date>%vbssnippet%
echo Wscript.Echo "x," ^& DatePart("ww",dDate,vbSunday,vbFirstFourDays) ^& "," ^&_>>%vbsSnippet%  
echo DatePart("ww",dDate) ^& "," ^& DatePart("w",dDate) ^& "," ^& DatePart("y",dDate)>>%vbsSnippet%  

for /F "delims=, tokens=2-5" %%a in ('cscript //nologo %vbsSnippet% %INDate%') do (  
(Set "KW=%%a" ) && Rem Kalenderwoche nach verbreiteter Berechnung  
(Set "OSKW=%%b" ) && Rem Kalenderwoche nach Betriebssystemvorgaben nicht über GUI änderbar! *lacht  
(Set "DoW=%%c" ) && Rem Day of Week 1=Sonntag, 2= Montag... bei meinen REG-Settings  
(Set "DoY=%%d" ) && Rem Day of Year, Kalendertag  
)
del %vbssnippet%

:: Tag, Monat und Stunde möchte ich immer zweistellig haben, also mit führender "0"  
:: v006-Fix<s>For %%i in (DD TT MM hh) DO IF DEFINED %%i Set /a %%i+=100 </s>
:: v006-Fix wg. Set /A-Bug. THX an superfrog für den Bugreport
:: v006b-Fix-Fix <s>For %%i in (DD TT MM hh) DO IF DEFINED %%i Set "%%i=10!%%i!"</s>  
:: --- für die beiden Fixes siehe die Kommentare von superfrog unten -- 14.2.2006
IF defined DD Set "DD=10%DD%"  
IF defined TT Set "TT=10%TT%"  
IF defined MM Set "MM=10%MM%"  
IF defined hh Set "hh=10%hh%"  
:: -v006b ----Vier Zeilen ohne DelayedExpansion statt eine FOR..IN..DO-Anweisung

set "hh=%hh:~-2,2%"  
set "MM=%MM:~-2,2%"  
:: ... und den Wochentag will ich auch, wenigstens in zwei Sprachen.
IF defined DD (
set "DD=%DD:~-2,2%"  
IF %DoW%==1 Set "cDoW=Sun"  
IF %DoW%==2 Set "cDoW=Mon"  
IF %DoW%==3 Set "cDoW=Tue"  
IF %DoW%==4 Set "cDoW=Wed"  
IF %DoW%==5 Set "cDoW=Thu"  
IF %DoW%==6 Set "cDoW=Fri"  
IF %DoW%==7 Set "cDoW=Sat"  
)

IF defined TT (
set "TT=%TT:~-2,2%"  
IF %DoW%==1 Set "cDoW=So"  
IF %DoW%==2 Set "cDoW=Mo"  
IF %DoW%==3 Set "cDoW=Di"  
IF %DoW%==4 Set "cDoW=Mi"  
IF %DoW%==5 Set "cDoW=Do"  
IF %DoW%==6 Set "cDoW=Fr"  
IF %DoW%==7 Set "cDoW=Sa"  

)
:: so.. hier schlampe ich mal.. 
:: alle 2-stelligen Jahre kleiner 39 werden zu 2001...2038
:: alle 2-stelligen Jahre größer gleich 39 und kleiner gleich 99 werden zu 1939 bis 1999

IF not defined YY goto YYskip
IF %YY% LSS 39 set "YY=20%YY%"  
IF %YY% LEQ 99 set "YY=19%YY%"  
:YYskip
If not defined JJ goto JJskip
IF %JJ% LSS 39 set "JJ=20%JJ%"  
IF %JJ% LEQ 99 set "JJ=19%JJ%"  
:JJskip

IF defined YY set "JJ=%YY%"  
IF defined JJ set "YY=%JJ%"  
IF defined DD Set "TT=%DD%"  
IF defined TT Set "DD=%TT%"  
:: Lass sehen, was wir haben....falls kein Parameter /q
For %%i in (%1 %2 %3 %4) Do If /i %%i==/q goto cleanUp

For %%i in (INDate INTime %Other% %AllDateTimeVars%) do @if defined %%i set %%i|find /i "%%i="  

:cleanUp
For %%i in (vbssnippet INTime INDate %Other% Other) Do Set "%%i="  
goto :eof

:Syntax
ECHO
ECHO %~nx0 By Frank / dem Biber aus Bremen 2005
ECHO
ECHO Ermittelt strukturierte Informationen über Datum und Zeit
ECHO Angezeigt oder optional in Variablen gesetzt werden:
ECHO %AllDateTimeVars%
ECHO 
Echo Syntax:
ECHO %~nx0 [Datum] [Zeit] [/s^|/u] [/q] [/?]
ECHO 
ECHO Datum Datumswert wie vom System oder der Dir-Ausgabe geliefert.
ECHO Default Systemdatum.
ECHO Zeit Zeit wie vom System oder der Dir-Ausgabe geliefert. Default Systemzeit.
ECHO /s SET Setzt die oben genannten Umgebungsvariablen
ECHO /u UNSET Löscht die gesetzten Umgebungsvariablen
ECHO /q QUIET Unterdrückt die Bildschirmanzeige der ermittelten Werte
ECHO/^? Zeigt diese schöne Hilfe
Goto :eof
::-------snapp GetAllDateTimeInfos.cmd
Nun nochmal schauen, was passiert:
 GetAllDateTimeInfos /?

GetAllDateTimeInfos.bat By Frank / dem Biber aus Bremen 2005

  Ermittelt strukturierte Informationen über Datum und Zeit
  Angezeigt oder optional in Variablen gesetzt werden:
  DateOrder KW OSKW DoW DoY cDoW DD TT MM JJ YY hh min ss ms

Syntax:
  GetAllDateTimeInfos.bat [Datum] [Zeit] [/s|/u] [/q] [/?]

  Datum Datumswert wie vom System oder der Dir-Ausgabe geliefert.
        Default Systemdatum.
  Zeit  Zeit wie vom System oder der Dir-Ausgabe geliefert. Default Systemzeit
  /s    SET   Setzt die oben genannten Umgebungsvariablen
  /u    UNSET Löscht die gesetzten Umgebungsvariablen
  /q    QUIET Unterdrückt die Bildschirmanzeige der ermittelten Werte
  /?    Zeigt diese schöne Hilfe

>GetAllDateTimeInfos 01.10.2005 12:33:44
INDate=01.10.2005
INTime=12:33:44
DateOrder=TT-MM-JJ
KW=39
OSKW=40
DoW=7
DoY=274
cDoW=Sa
DD=01
TT=01
MM=10
JJ=2005
YY=2005
hh=12
min=33
ss=44
ms=00

>GetAllDateTimeInfos 01.11.2002 00:00:44
INDate=01.11.2002
INTime=00:00:44
DateOrder=TT-MM-JJ
KW=44
OSKW=44
DoW=6
DoY=305
cDoW=Fr
DD=01
TT=01
MM=11
JJ=2002
YY=2002
hh=00
min=00
ss=44
ms=00

(---hier mal ein zweistelliges Datum aus dem letztem Jahrhundert--)
>GetAllDateTimeInfos 01.04.99 12:47
INDate=01.04.99
INTime=12:47
DateOrder=TT-MM-JJ
KW=13
OSKW=14
DoW=5
DoY=91
cDoW=Do
DD=01
TT=01
MM=04
JJ=1999
YY=1999
hh=12
min=47
ss=00
ms=00

>GetAllDateTimeInfos 01.11.2002 00:00:44 /q /s
 (keine Bildschirmausgabe, aber alle Variablen sind gesetzt)
 
>GetAllDateTimeInfos /u
 (keine Bildschirmausgabe, und alle Variablen sind gelöscht)
 
>GetAllDateTimeInfos
INDate=03.10.2005
INTime= 6:43:02,31
DateOrder=TT-MM-JJ
KW=40
OSKW=41
DoW=2
DoY=276
cDoW=Mo
DD=03
TT=03
MM=10
JJ=2005
YY=2005
hh=06
min=43
ss=02
ms=31

>GetAllDateTimeInfos /q
 (keine Bildschirmausgabe, es werden auch keine Variablen gesetzt, aber alle berechnet)

Und was mach ich jetzt damit??

Endlich kommen wir zur entscheidenden Frage face-wink

Ich kann diesen Batch von CMD-Prompt aus oder von anderen Bätchen aufrufen, um mir Datums-/Zeitvariablen zu ermitteln, die ich unabhängig von Benutzereinstellungen, Ländereinstellungen und Windowsunterschieden (von NT, w2000, XP und W2003 Server) verfügbar habe.
Egal auf welchen Rechner ich den laufen lasse.
Und ich verwende dazu keine Registry-Zugriffe oder andere Tools (ausgenommen *.vbs).

Beispiel 1: ich will (in einem Batch) ein LogFile mit der Namens-Struktur JJJJ-MM-TT_hh$min.log anlegen.
Call GetAllDateTimeInfos /s
 (alle Variablen sind gesetzt)
 set logfilename=%JJ%-%MM%-%TT%_%hh%$%min%.log
 Call GetAllDateTimeInfos /u
 ..
oder, Beispiel zwei, ich möchte von CMD-Prompt aus alle VBScripte in einem bestimmten Verzeichnis so umbenennen, dass im Datei-Namen der Kalendertag vorangestellt wird.
Also aus Test.vbs wird 255_Test.vbs etc. Geht vom CMD-Prompt aus mit einer Zeile.
(Ich habe noch ein @echo vor das "ren" gesetzt... wollte ja nur zeigen, was geht.)
>for %i in (d:	emp*.vbs) do @for /f "tokens=2 delims==" %a in ('GetAllDateTimeInfos.bat %~ti^|find "DoY"')do @echo ren %~dpnxi %a_%~nxi  
ren d:	empGetips.vbs 223_Getips.vbs
ren d:	empTemplatesPath.vbs 225_TemplatesPath.vbs
ren d:	empTemplatesPathHf.vbs 225_TemplatesPathHf.vbs
ren d:	empSpecialFolders.vbs 225_SpecialFolders.vbs
ren d:	empTemplatesPathHffull.vbs 225_TemplatesPathHffull.vbs
ren d:	empRun BatchHiddenwith.Vbs 227_Run BatchHiddenwith.Vbs
ren d:	empMonatsOrdnerAnlegen.vbs 246_MonatsOrdnerAnlegen.vbs
ren d:	empListLocalUsers.vbs 259_ListLocalUsers.vbs
Wer jetzt noch Fragen zum Thema Datums- und Zeit-Berechnung in Batch-Dateien hat, der/dem kann auch ich nicht helfen.

Keep on Batchin' face-smile

Frank / der Biber aus Bremen
Last Edits:
20.9.2007 v0.10 time/T hat Format "hh:mm" ; %time% dagegen "hh:mm:ss, nn"
Time /t nur im Notfall - wenn %time% nicht definiert ist - verwenden. Nur bei NT also.
siehe yotamans Hinweis unten
11.7.2007 v0.08 NT-Bugfix - %date% / %time% -Umgebungsvariablen kennt Windows NT noch nicht.
Siehe hier: Datensicherung: Probleme mit einer Batchdatei unter NT
29.2.2008 v0.09 Minor cosmetics.
25.2.2009 Späte Korrektur in GetAllDateTimeVars_3.bat (siehe Kommentare dotsch)
19.6.2009 Find /i statt Find beim Variablen-Finden (s.Kommentar Noc06)
20.2.2012 Erweiterung/optimierung s. Kommentar pieh-ejdsch
11.10.2013 Erweiterung Chinesisches Datumsformat von RobSei
Querverweise hier auf administrator.de:
Internationales Datum und Zeitzone bestimmen

Erweiterung: mit AM PM Amerikanischen Zeiten mit AM PM in deutschen 24 Stunden umwandeln (von mycroftone)

Die vorangegangenen Workshops
Workshop Batch for Runaways - Part II - Ein bisschen Handwerkszeug
Workshop Batch for Runaways - Part I - Beispiel FindLongPath.Bat Bedenklich lange Pfade finden

und immer hilfreich
Tutorial zur FOR-Schleife von Friemler

Content-ID: 17083

Url: https://administrator.de/contentid/17083

Ausgedruckt am: 21.11.2024 um 13:11 Uhr

Biber
Biber 19.10.2005 um 15:59:01 Uhr
Goto Top
Ich wurde gerade darauf aufmerksam gemacht, dass mein Englisch vielleicht doch nicht so gut ist, wie ich immer gehofft habe. Ich werde also eventuelle weitere Teile meiner kleinen Reihe wieder auf Deutsch übertiteln.

Das nächste Tutorial heißt dann also statt "Batch for Runaways Part IV" wieder allgemein verständlich "Stapel für Fortgeschrittene Teil IV".

Danke für die Rückmeldung @engelstaub
Biber
Biber
Biber 14.12.2005 um 18:24:46 Uhr
Goto Top
Moin alle,

ich wurde in einem darauf hingewiesen, dass die oben gepostete Version des Beispiel-Batches "GetAllDateTimeInfos.bat" Version 0.01 nicht robust genug für ein Copy & Paste ist, wenn ich mich aus Versehen an die dokumentierte M$-Syntax halte. (wird nicht wieder vorkommen, sorry... *fg)

Der Fehler liegt bei mir bzw. in folgenden Blöckchen (dokumentierte MS-Syntax):
... ...(in den endlosen Weiten des GetAllDateTimeInfo.Bat 0.01).....
IF not defined YY goto YYskip
IF %YY% LSS 39 set YY=20%YY%
IF %YY% LEQ 99 set YY=19%YY%
:YYskip
If not defined JJ goto JJskip
IF %JJ% LSS 39 set JJ=20%JJ%
IF %JJ% LEQ 99 set JJ=19%JJ%
:JJskip

IF defined YY set JJ=%YY%
IF defined JJ set YY=%JJ%
IF defined DD Set TT=%DD%
IF defined TT Set DD=%TT%
...

Da sollten, damit der Sinn auch nach dem Cut & Paste erhalten bleibt, alle "SET"-Anweisungen auch auf die immer von mir beschworene undokumentierte Art in Anführungszeichen gesetzt werden.
Also so:
IF not defined YY goto YYskip
IF %YY% LSS 39 set "YY=20%YY%"
IF %YY% LEQ 99 set "YY=19%YY%"
:YYskip
If not defined JJ goto JJskip
IF %JJ% LSS 39 set "JJ=20%JJ%"
IF %JJ% LEQ 99 set "JJ=19%JJ%"
:JJskip

IF defined YY set "JJ=%YY%"
IF defined JJ set "YY=%JJ%"
IF defined DD Set "TT=%DD%"
IF defined TT Set "DD=%TT%"

Das war das Problem. In meinem Original-Batch waren natürlich keine trailing blanks am Ende der "If defined"-Zeilen.
Beim Cut & Paste kommen aber offensichtlich wieder welche rein bzw können reinkommen.

Hoffe nicht, dass weiterhin in so kurzen Abständen Bugs gemeldet werden... face-wink

!!! Ist oben im Tutorial korrigiert !!!
Danke an @f.e.dorr für die Rückmeldung!!

Grüße Biber
[Edit 3.2.2006]
Bugfix des Bugfixes vom 14.12.2005
Nun waren Anführungszeichen beim Setzen von TT, MM, HH an der falschen Stelle. Sorry.
[/Edit]
cat1510
cat1510 06.02.2006 um 18:50:02 Uhr
Goto Top
Hi,

super Anleitung.
Habe das meiste auch kapiert.
Normalerweise scipte ich in sh oder zsh.
Wie umstaendlich Win sein kann ist echt faszinierend.

Noch was vorweg.
Habe auch ein bisschen mit der Registry rumgespielt.
Nimmt man den Wert von sLongDate und ersetzt den Wert von sShortDate,
kommt auf jeden Fall die Tag Angabe.


Nun zu Deinem Script.

In meinem Falle bekomme ich gar nichts zurueck oder er meint es ist Donnerstag.
Habe das mal in ein textfile schreiben lassen:


C:\>IF NOT "Windows_NT" == "Windows_NT" echo "test.cmd läuft nur unter WinNT oder höher.Sorry." && GOTO :eof

C:\>If /I [/s] == [/?] goto Syntax

C:\>For %i in (/s) Do (
If /I [%i] == [/s] Endlocal
If /I [%i] == [/u] Endlocal
If /I [%i] == [/h] goto Syntax
)

C:\>(
If /I [/s] == [/s] Endlocal
If /I [/s] == [/u] Endlocal
If /I [/s] == [/h] goto Syntax
)

C:\>SET "AllDateTimeVars=DateOrder KW OSKW DoW DoY cDoW DD TT MM JJ YY hh min ss ms"

C:\>FOR %i in (DateOrder KW OSKW DoW DoY cDoW DD TT MM JJ YY hh min ss ms) do @if defined %i set %i=

C:\>For %i in (/s) Do If /I %i == /u goto :eof

C:\>If /I /s == /u goto :eof

C:\>Set "INDate=Mo, 06. Feb 2006"

C:\>Set "INTime=18:46:17,65"

C:\>If NOT [/s] == If /I [/s] NEQ [/q] If /I [/s] NEQ [/s] set "INDate=/s"

C:\>If NOT == If /I NEQ [/q] If /I NEQ [/s] set "INTime="

C:\>FOR /F "tokens=2" %i in ("Mo, 06. Feb 2006") do Set INDate=%i

C:\>Set INDate=06.

C:\>FOR /F "tokens=1-7 delims=:/.-, " %i in ("06. 18:46:17,65") do (For /F "tokens=2-4 delims=/-,() skip=1" %a in ('echo.|date') do (for %@ in ("DateOrder=%a-%b-%c" "%a=%i" "%b=%j" "%c=%k" "hh=%l" "min=%m" "ss=%n" "ms=%o") do set %@ ) )

C:\>(For /F "tokens=2-4 delims=/-,() skip=1" %a in ('echo.|date') do (for %@ in ("DateOrder=%a-%b-%c" "%a=06" "%b=18" "%c=46" "hh=17" "min=65" "ss=" "ms=") do set %@ ) )

C:\>(for %@ in ("DateOrder=TT-MM-JJ" "TT=06" "MM=18" "JJ=46" "hh=17" "min=65" "ss=" "ms=") do set %@ )

C:\>set "DateOrder=TT-MM-JJ"

C:\>set "TT=06"

C:\>set "MM=18"

C:\>set "JJ=46"

C:\>set "hh=17"

C:\>set "min=65"

C:\>set "ss="

C:\>set "ms="

C:\>For %i in (ss ms) do if not defined %i set "%i=00"

C:\>if not defined ss set "ss=00"

C:\>if not defined ms set "ms=00"

C:\>set vbsSnippet=C:\DOKUME~1\cat\LOKALE~1\Temp\30758.vbs

C:\>echo If wscript.arguments.count = 1 Then dDate=wscript.arguments.item(0) Else dDate=Date 1>C:\DOKUME~1\cat\LOKALE~1\Temp\30758.vbs

C:\>echo Wscript.Echo " " & DatePart("ww",dDate,vbSunday,vbFirstFourDays) & " " &_ 1>>C:\DOKUME~1\cat\LOKALE~1\Temp\30758.vbs

C:\>echo DatePart("ww",dDate) & " " & DatePart("w",dDate) & " " & DatePart("y",dDate) 1>>C:\DOKUME~1\cat\LOKALE~1\Temp\30758.vbs

C:\>for /F "tokens=2-5" %a in ('cscript //nologo C:\DOKUME~1\cat\LOKALE~1\Temp\30758.vbs 06.') do (
(Set "KW=%a" ) && Rem Kalenderwoche nach verbreiteter Berechnung
(Set "OSKW=%b" ) && Rem Kalenderwoche nach Betriebssystemvorgaben nicht über GUI änderbar! *lacht
(Set "DoW=%c" ) && Rem Day of Week 1=Sonntag, 2= Montag... bei meinen REG-Settings
(Set "DoY=%d" ) && Rem Day of Year, Kalendertag
)

C:\>(
(Set "KW=1" ) && Rem Kalenderwoche nach verbreiteter Berechnung
(Set "OSKW=6" ) && Rem Kalenderwoche nach Betriebssystemvorgaben nicht über GUI änderbar! *lacht
(Set "DoW=5" ) && Rem Day of Week 1=Sonntag, 2= Montag... bei meinen REG-Settings
(Set "DoY=" ) && Rem Day of Year, Kalendertag
)

C:\>del C:\DOKUME~1\cat\LOKALE~1\Temp\30758.vbs

C:\>For %i in (DD TT MM hh) DO IF DEFINED %i Set /a %i+=100

C:\>IF DEFINED DD Set /a DD+=100

C:\>IF DEFINED TT Set /a TT+=100

C:\>IF DEFINED MM Set /a MM+=100

C:\>IF DEFINED hh Set /a hh+=100

C:\>set "hh=17"

C:\>set "MM=18"

C:\>IF defined DD (
set "DD=~-2,2"
IF 5 == 1 Set "cDow=Sun"
IF 5 == 2 Set "cDow=Mon"
IF 5 == 3 Set "cDow=Tue"
IF 5 == 4 Set "cDow=Wed"
IF 5 == 5 Set "cDow=Thu"
IF 5 == 6 Set "cDow=Fri"
IF 5 == 7 Set "cDow=Sat"
)

C:\>IF defined TT (
set "TT=06"
IF 5 == 1 Set "cDow=So"
IF 5 == 2 Set "cDow=Mo"
IF 5 == 3 Set "cDow=Di"
IF 5 == 4 Set "cDow=Mi"
IF 5 == 5 Set "cDow=Do"
IF 5 == 6 Set "cDow=Fr"
IF 5 == 7 Set "cDow=Sa"
)

C:\>IF not defined YY goto YYskip

C:\>If not defined JJ goto JJskip

C:\>IF 46 LSS 39 set "JJ=2046"

C:\>IF 46 LEQ 99 set "JJ=1946"

C:\>IF defined YY set "JJ="

C:\>IF defined JJ set "YY=1946"

C:\>IF defined DD Set "TT="

C:\>IF defined TT Set "DD=06"

C:\>For %i in (/s) Do If /I %i == /q goto cleanUp

C:\>If /I /s == /q goto cleanUp

C:\>For %i in (INDate INTime DateOrder KW OSKW DoW DoY cDoW DD TT MM JJ YY hh min ss ms) do @if defined %i set %i
INDate=06.
INTime=18:46:17,65
DateOrder=TT-MM-JJ
KW=1
OSKW=6
DoW=5
cDow=Do
DD=06
TT=06
MM=18
JJ=1946
YY=1946
hh=17
min=65
ss=00
ms=00

C:\>For %i in (vbssnippet INTime INDate) Do Set %i=

C:\>Set vbssnippet=

C:\>Set INTime=

C:\>Set INDate=

C:\>goto :eof


Nun ist die Frage, wo der Fehler ist, bzw was nicht richtig laeuft...

Bis dann.

CAT
Biber
Biber 06.02.2006 um 21:28:27 Uhr
Goto Top
Na, vielen Dank Cat1510,

endlich mal jemand, der mein Tutorial etwas genauer liest...face-wink
Gerade rechtzeitig - am Wochenende war ich so gefrustet, weil ein anderer User im Bereich "Batch & Shell" gepostet hatte, er würde kein Wort in meinem Tutorial verstehen.. da wollte ich schon die Brocken hinschmeißen (im Moment wird ziemlich nachdrücklich von zwei Mitgliedern ein Generationenwechsel eingefordert, das wäre der zweite Grund).
Aber bis zur endgültigen Beantwortung Deiner frage bleib ich noch.. *g

Ich hatte es ja damals im Tutorial schon geschrieben: ich habe keinen vorgesehenen Weg gefunden, in Windows eine "lange" Datumsanzeige zu erreichen/zu erzwingen.
Über den Klicki-Bunti-Weg ("Start"-"Einstellungen"-"Systemsteuerung"->"Regions- und Sprachoptionen") lassen sich zwar sowohl "kurze Datumsformate" ("06.02.2006" und Variationen) wie auch "lange Datumsformate" ("Mo, 06. Februar 2006" und Varianten) einstellen.
Aber angezeigt wird immer das "kurze Datum". Egal, welches Format ich für das "lange Datumsformat" nehme.

Nach "legaler" Bearbeitung über die GUI sieht es in der Registry ungefähr so aus:

[HKEY_CURRENT_USER\Control Panel\International]
"sShortDate"="dd.MM.yyyy"
"sDate"="."
"iDate"="1"
"sLongDate"="ddd, dd. MMM yyyy"

...nur nach "Registry-Hacking", also brutales Überschreiben des sShortDate mit dem Wert von sLongDate bekomme ich sowas hin:
[HKEY_CURRENT_USER\Control Panel\International]
"sShortDate"="ddd, dd. MMM yyyy"
"sDate"="."
"iDate"="1"
"sLongDate"="ddd, dd. MMM yyyy"

Über die GUI darf ich das gar nicht...

Hab ich natürlich damals auch gemacht... und ganz schnell wieder zurückgedreht, als ich bemerkt habe, dass sich bei mir diverse Excel-Dateien (mit Makros; Zeiterfassungssysteme etc.) nicht mehr öffnen ließen. Bei M$ schlägt öffensichtlich ein Datumsanzeigeformat bis auf hinterlegte Office-Datums-Berechnungsformeln durch..
*kopfschüttel* *kopfschüttel* *kopfschüttel*
Na ja... wegen dieser Seiteneffekte hab ich es ganz schnell wieder begradigt.

Aber ich will ja Deiner Frage nicht ewig ausweichen: wenn am CMD-Prompt tatsächlich ein "langes Datumsformat" zurückkommen könnte (ich meine ohne Registry-Vergewaltigung), dann wäre es ein Haufen Mehrarbeit, diese Fälle abzufangen.
Denn: ein langes Datumsformat könnte sein:
"ddd, dd. MMM yyyy" ------->Anzeige: Mo, 06.Jan 2006
"dddd, d. MMMM yyyy" ----->Anzeige: Montag, 6. Januar 2006
"ddd, dd. MMMM yyyy" ----->Anzeige: Mo, 06.Januar 2006
...usw. usw.., Spiel ohne Grenzen..
Nicht zu vergessen die
- Variationen mit Wochentags- und Monatsnamen in anderen Sprachen
- und, absolut lumpig, die nicht herleitbaren Abkürzungen ("Januar"=="Jan";"Februar"=="Feb"; "März"=="Mrz" ??!?)

Für diese im Deutschen noch denkbaren drei Beispiel-Fälle eines Langdatums kann ich relativ schnell noch eine kleine Ergänzung liefern.
Für Tages- und Monatsnamen auf Englisch, Französisch, Spanisch etc bitte ich Interessierte, es selber auszutüfteln.

(Ist natürlich ein Scherz - ich würde dann naturlich das Datum komplett über vorhandene Datumsfunktionen in VB- oder JScript ermitteln..)

Aber nur aus sportlichen Gründen
In einem geänderten Batch muss ergänzt/ersetzt werden:
::.....
If NOT [%1]== If /i [%1] NEQ [/q] If /i [%1] NEQ [/s] set "INDate=%1"   
If NOT [%2]== If /i [%2] NEQ [/q] If /i [%2] NEQ [/s] set "INTime=%2"   

:: Die nächste Code-Zeile ändert nur dann etwas, wenn ein Wochentag mitgeliefert wird. 
:: Aus "Sa 01.10.2005" wird dann "01.10.2005"  ##red| 
REM FOR /F "tokens=2" %%i in ("%INDate%") do Set INDate=%%i   
FOR /F "delims=, tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"   
REM 
REM Echo Wochentag und Komma von [%date%] sind entfernt.. [%Indate%]
(echo "%INDate%"|find " ">nul)|| goto NoSpacesInDate  
Set "Indate=%Indate: =%"  
Set "Indate=%INDate:Januar=.01.%"  
Set "Indate=%INDate:Februar=.02.%"  
Set "Indate=%INDate:März=.03.%"  
Set "Indate=%INDate:April=.04.%"  
Set "Indate=%INDate:Mai=.05.%"  
Set "Indate=%INDate:Juni=.06.%"  
Set "Indate=%INDate:Juli=.07.%"  
Set "Indate=%INDate:August=.08%"  
Set "Indate=%INDate:September=.09.%"  
Set "Indate=%INDate:Oktober=.10.%"  
Set "Indate=%INDate:November=.11.%"  
Set "Indate=%INDate:Dezember=.12.%"  

Set "Indate=%INDate:Jan=.01.%"  
Set "Indate=%INDate:Feb=.02.%"  
Set "Indate=%INDate:Mrz=.03.%"  
Set "Indate=%INDate:Apr=.04.%"  
Set "Indate=%INDate:Mai=.05.%"  
Set "Indate=%INDate:Jun=.06.%"  
Set "Indate=%INDate:Jul=.07.%"  
Set "Indate=%INDate:Aug=.08%"  
Set "Indate=%INDate:Sep=.09.%"  
Set "Indate=%INDate:Okt=.10.%"  
Set "Indate=%INDate:Nov=.11.%"  
Set "Indate=%INDate:Dez=.12.%"  
Set "Indate=%Indate:..=.%"  

:NoSpacesInDate ##
REM ab hier alles unverändert.
FOR /F "tokens=1-7 del.............  
......

Ergebnis:
>date /t
Mo, 06. Feb 2006

<=21:26:43  C:\Dokumente und Einstellungen\Biber=>
>e:\GetAllDateTimeInfos.bat
INDate=06.02.2006
INTime=21:26:46,80
DateOrder=TT-MM-JJ
KW=6
OSKW=2
DoW=37
DD=06
TT=06
MM=02
JJ=2006
YY=2006
hh=21
min=26
ss=46
ms=80
Schönen Abend
Biber
cat1510
cat1510 06.02.2006 um 22:15:52 Uhr
Goto Top
Danke fuer Deine Antwort.

also ich habe die Registry mal wieder zurueckgedreht.
zumindest die Einstellungen, die Du aufgezaehlt hast.

Ich bekomme nun auch alles richtig ausgegeben.
Was fuer mich am wichtigsten ist: Der Wochentag.

Den bekomm ich nicht.
Du auch nicht.

cDOW fehlt bei beiden Ergebnissen.

Mit VB script kenn ich mich nun gar nicht aus. Aber ich denke der Fehler liegt dort.
Wird das auf jeden Rechner gleich ausgefuerht?

C:\>(
(Set "KW=6" ) && Rem Kalenderwoche nach verbreiteter Berechnung
(Set "OSKW=2" ) && Rem Kalenderwoche nach Betriebssystemvorgaben nicht über GUI änderbar! *lacht
(Set "DoW=37" ) && Rem Day of Week 1=Sonntag, 2= Montag... bei meinen REG-Settings
(Set "DoY=" ) && Rem Day of Year, Kalendertag
)

Zurueck gibt er diese Werte...
DoW = Wochentag?


Danke

CAT

P.S. Geht nichts uerber `date +%A`, gibt es nicht fuer WIN!
Biber
Biber 06.02.2006 um 22:22:28 Uhr
Goto Top
@cat
DoW=DayOfWeek fehlt...stimmt, habe ich eben nicht gesehen.
Mach ich morgen mal...
P.S. Geht nichts uerber `date +%A`, gibt es nicht fuer WIN!
Stimmt erst recht *gg

Grüße Biber
Biber
Biber 07.02.2006 um 13:53:01 Uhr
Goto Top
So, CAT,
ich habe recherchiert.

Mit dieser lustigen "Parameter 2 bis 5-Auswahl" in der Zeile, in der der VBS-Schnipsel aufgerufen wird:
BUGGY: for /F "tokens=2-5" %%a in ('cscript //nologo %vbsSnippet% %INDate%') do (   
... wollte ich eigentlich einen W2K-Bug umgehen... da wird in der ersten Zeile einer Ausgabe ein Steuerzeichen (FormFeed/Seitenvorschub, ^L) mitgeliefert.
Und dieses erste Zeichen habe ich übersprungen. Passt natürlich nicht mehr bei den Versionen, bei denen M$ diesen Bug gefixt hat.
Habe es grad unter XP SP2 probiert, da hilft mir der Bugfix nix.

Richtige Syntax für beide Win-Versionen (bau ich auch im Tutorial ein):

BESSER:
for /F "delims=^L   tokens=1-4" %%a in ('cscript //nologo %vbsnippet.vbs% %InDate%') do (  
[[Delims ist das Zeichen für weiblich gefolgt von einem Leerzeichen. Siehe im Tut. und das P.S.]]

HTH Biber
[Edit] P.S.
Ich glaube, der Obertroll hat doch recht.. ich bin irgendwie zu blöd.. *gg
Ich bekomme es hier unten im Kommentar nicht hin, das richtige Delimiterzeichen hier anzuzeigen. Richtig wäre das Zeichen für "weiblich" == chr(012) ==^L .
Sobald ich das hier eintippe, werden alle Umlaute umcodiert und auch das Zeichen "weiblich" selbst erscheint nicht -statt dessen zwei Buchstaben aus einem anderen Zeichensatz.

Oben im Beitrag selbst erscheint es "richtig", auch Umlaute bleiben oben erhalten.
Falls jemand weiß, wie ich dieses Formatierungsproblem in den Griff bekomme, bitte posten.
Danke.
[/Edit]
Biber
Biber 11.02.2006 um 22:41:19 Uhr
Goto Top
Also, jetzt explodier ich gleich.... jetzt werden sogar meine Kommentare unterhalb meines eigenen Tutorials mit Minuspunkten bewertet...
...weil sie nicht hilfreich für Dein Problem waren???

Irgendwann erwisch ich Dich auf frischer Tat, Du Möchtegern-TopUser.


So, was ich eigentlich wollte... ich hatte rausgefunden, wie ich das Zeichen für "weiblich" in HTML-Nomenklatur schreiben kann, sonst hätte ich gar nicht hier reingeschaut...

Aber das teste ich wenigstens, ob das klappt:
for /F "tokens=1-4 delims=&#9792;" ..... sollte das Zeichen für weiblich bringen.. geschrieben habe ich

for /F "tokens=1-4 delims=&_#_9792_;" ....ohne Unterstriche... nur zur Verdeutlichung.

Und ich habe einen besseren Workaround für den genannten W2K-Bug gefunden und oben im Tutorial eingebaut.
(ich nehme jetzt im VBSSnippet Kommata als Trennzeichen für die Output... da ist es mir egal, ob das erste Zeichen ein FormFeed ist.

Manno, bin ich jetzt stinkig wegen dieses Trolls....so stinkig... und traurig face-sad
Biber
[Edit] 12.02.2006
So um keinen weiteren Kommentar und keine neue Angriffsfläche für eine Minusbewertung zu bieten, editier ich den vorhandenen Kommentar.
Habe einen weiteren M$-Bug abgefangen und zwei Sourcecode-Zeilen geändert.
Aktuelle Version ist jetzt v0.06. Danke an die User Cat1510 und superfrog für das Bugtracking und die hilfreichen Rückmeldungen.
Wegen Benutzern wie Euch bin ich gerne hier! face-wink
[/Edit]
superfrog
superfrog 12.02.2006 um 19:43:16 Uhr
Goto Top
@Biber

mit deiner Änderung in v0.06 ist mein Problem bezüglich DD/TT wird zu 00 jetzt behoben.

Hatte erst an mir selbst gezweifelt, statt mal davon auszugehen, dass mir da vielleicht ein M$-Bug Probleme bereitet.
Dachte schon ich bin zu blöd, weil es für mich einfach keinen Sinn ergeben wollte, warum es am 07. eines Monats funtioniert, am 08. und 09. aber nicht.

Wie auch immer, das Problem (bei DD=08 oder DD=09) tritt jetzt nicht mehr auf, so dass mein Batch ohne Probleme läuft.
Danke nochmal für deine Hilfe. Hat mir sehr geholfen.

Aber Leute die undankbar sind, obwohl man ihnen Hilfe anbietet, wird es wohl immer geben.
Sollten man sich nicht drüber ärgern.

Es gibt auch Leute die deine nützlichen Beiträge zu schätzen wissen.

Grüße
superfrog
cat1510
cat1510 13.02.2006 um 15:15:31 Uhr
Goto Top
Also als erstes Biber: NICHT aergern.

Habe mir eine Nacht spaeter alle Deine TUTO's reingezogen und muss sagen, ich bin beeindruckt.

Tuto I ist ein bisschen viel finde ich. Also komplex mein ich damit.
Tuto II ist schon sehr viel freundlicher. Zumindest, was das Verstaendnis anbelangt.
Tuto III ist am besten, weil der Stil wie ein Experiment ist.
Der User/Leser wird halt schrittweise darauf hingefuehrt.
Und es macht Spass zu lesen, weil man alle Deine Schritte auch auf der eigenen Konsole nachvollziehen kann.

Naja, habe mich auch mal in den anderen Tuto's umgeschaut, wo man aber deutliche Kompetenzunterschiede feststellen kann. ;)

Mit der Formatierung habe ich auch ein Problem gehabt. War aber so schlau und habe den Inhalt der Site einfach in meinen Gvim kopiert.
Dann gab es keine Probleme mit dem Zeichensatz, der unter Win auch ein bisschen durcheinander scheint manchmal.

Dann habe ich am WE noch eine Neuerung in einer Zeitschrift gefunden:
WinScript XP 3.0

Damit kann man Dialog Boxen und Browserfelder per batch oeffnen.
Ok, wenn man das so weit treiben moechte kann man doch auch in VB-Script das machen oder? Ist aber ein schneller und eleganter Weg, wie ich finde.

http://toolsandmore.de/Central/Produkte/Software/System-Tools/Winscript ...


MFG

CAT
Biber
Biber 13.02.2006 um 19:15:54 Uhr
Goto Top
Danke, CAT,

für die Blumen - ich muss allerdings auch sagen, so eine Art von Dialog, wie ich ihn die letzten Tage mit superfrog und Dir geführt habe, finde ich um Welten besser als die sinnfreie Chatterei, die wir im Forum in den letzten zwei Monaten so oft hatten.

Hier (in diesem unbedeutenden Seitenarm des Forums *gg) war für mich wieder etwas von dem ursprünglichen community-Gedanken, dem gemeinsamen (Weiter-)Entwickeln und Verbessern von Lösungen spürbar. Hat mir gut getan.

Zu den WinScript XP 3.0 Sachen....hmm ja... nicht schlecht, aber...
In dieser Beziehung gehöre ich doch eher zu den Batch-Puristen. Ich setze ja meistens dann Batches ein, wenn ich keine Dialoge und keine Aktivitäten mit XP-konformen abgerundeten Ecken sehen will. Batches sind für mich das Werkzeug, um still und unauffällig im Hintergrund zu arbeiten - schlimmstenfalls eine Fehlerzeile in ein Logfile zu schreiben.
Und mich eben nicht vor meinem ersten Kaffee schon mit Fragen zu belästigen wie "Sind Sie sicher, dass Sie die Datei löschen wollen?" oder mir ein Fenster hochzupoppen mit "Sicherung wurde ohne Fehler durchgeführt. Laufzeit 2 min 13sek. Drücken Sie OK, wenn Sie das wissen wollten."

Und der zweite Grund, weshalb mir Batches so lieb sind... ich kann mir auf jedem Rechner schnell was zusammenschroten... brauche keine installierten Komponenten und registrierte *.DLLs... nur eine Tastatur und einen Editor.

Aber das sind persönliche Vorlieben... grundsätzlich kommt es immer auf die Aufgabe für die Auswahl der Werkzeuge an... und es kann auch sinnvoll sein, solche GUI-Komponenten zu nutzen.

@all wg. v006a
ich habe noch ein paar Zeilen nachdokumentiert und noch zwei übersehene [SET var=Wert]-Anweisungen in Anführungszeichen gesetzt.
Durch das Copy & Pasten des Batches aus dem Artikel oben hatte ich plötzlich bei mir trailing blanks und Fehler im Sourcecode...deshalb habe ich (wo nötig) jetzt ersetzt:
ALT: Set var=wert
NEU: Set "var=wert"

Hintergrund: An einer Stelle wurde meine gewollte Zeile:
Set %%i=
wegen eines Leerzeichens am Ende interpretiert als
Set %%i=_ (als ein Leerzeichen).
Und so etwas ist natürlich ein fast nicht zu findender Fehler.

Schönen Abend noch
Grüße Biber
superfrog
superfrog 13.02.2006 um 23:59:55 Uhr
Goto Top
Hallo Biber,

leider hat sich an meinem Problem mit der neuen (zugemailten) Version nichts geändert.
Ich kriege damit immer noch diese Ausgabe:

C:\test>New007GetAllDateTimeInfos.bat /s
INDate=13.02.2006
INTime=22:23:42,04
DateOrder=TT-MM-JJ
KW=7
OSKW=2
DoW=44
DD=T!
TT=T!
MM=M!
JJ=2006
YY=2006
hh=h!
min=23
ss=42
ms=04

Ich hab jetzt nochmal ausprobiert und es liegt immer noch an der selben Zeile.
Wenn ich die Zeile
For %%i in (DD TT MM hh) DO IF DEFINED %%i Set "%%i=10!%%i!"
einfach mal auskommentiere, klappt schon alles.

C:\test>New007GetAllDateTimeInfos.bat /s
INDate=13.02.2006
INTime=22:25:26,78
DateOrder=TT-MM-JJ
KW=7
OSKW=2
DoW=44
DD=13
TT=13
MM=02
JJ=2006
YY=2006
hh=22
min=25
ss=26
ms=78

Wenn die Zeile nur dafür notwendig ist um auf 2 Stellen aufzufüllen, dann brauch ich sie ja eigentlich nicht oder ?
Denn bei mir sind die Werte ja auch ohne diese Zeile schon immer zweistellig.
Wundert mich nur, dass es bei dir damit klappt.

Was mich jetzt etwas verwirrt hat, waren die anderen Zeilen, die du da als Kommentar drin hattest.

::IF defined DD Set "DD=10%DD%"
::IF defined TT Set "TT=10%TT%"
::IF defined MM Set "MM=10%MM%"
::IF defined DD Set "hh=10%hh%"
:: Endlocal DisAbleDelayedExpansion

Erfüllen die nicht den gleichen Zweck ?
Wenn ich die mit reinnehme klappt es jedenfalls noch ? Oder war das echt nur als Test von dir gedacht ?
In der 4. Zeile vermute ich, meintest du aber warscheinlich hh, nicht DD.

Also ohne die For-Schleife kriege ich bei mir auf jeden Fall die gewünschten Ergebnisse und damit läuft mein LogBatch jetzt auch so wie gewünscht (sogar am 08. und 09. eines Monats).

Ach und noch ne kleine Sache die mir aufgefallen ist, wenn du im TUT ne Zeile nur durchstreichst, wird sie mir Copy&Paste trotzdem als ganz normale Zeile übernommen. Sollte man evtl. zusätzlich auskommentieren.
Könnte ne Fehlerquelle für andere sein.

Auf jeden Fall erst nochmal Danke für deine Mühe.

nette Grüße
superfrog
Biber
Biber 14.02.2006 um 09:36:36 Uhr
Goto Top
Hallo superfrog,

leider hat sich an meinem Problem mit der neuen (zugemailten) Version nichts geändert.
Ich kriege damit immer noch diese Ausgabe:

merkwürdig, ich habe mit sicherheitshalber noch mal die Version, die ich Dir gemailt habe bei mir (Win XP Pro SP29 laufen lassen.
Ich habe den Effekt nicht. *ratlos guck*

Ich hab jetzt nochmal ausprobiert und es liegt immer noch an der selben Zeile.
Wenn ich die Zeile
For %%i in (DD TT MM hh) DO IF DEFINED %%i Set "%%i=10!%%i!"
einfach mal auskommentiere, klappt schon alles.

Was mich jetzt etwas verwirrt hat, waren die anderen Zeilen, die du da als Kommentar drin hattest.

::IF defined DD Set "DD=10%DD%"
::IF defined TT Set "TT=10%TT%"
::IF defined MM Set "MM=10%MM%"
::----IF defined DD Set "hh=10%hh%" --------->Tippfehler
::IF defined hh Set "hh=10%hh%"

Erfüllen die nicht den gleichen Zweck ?
Doch. Dann machen wir das so.. denn diese FOR..Zeile hat ja den gleichen Effekt wie die 4 Einzelzeilen OHNE DelayedExpansion.

Wenn ich die mit reinnehme klappt es jedenfalls noch ? Oder war das echt nur als Test von dir gedacht ?
In der 4. Zeile vermute ich, meintest du aber warscheinlich hh, nicht DD.
Stimmt...das war einer meiner üblichen Tippfehler.

Ach und noch ne kleine Sache die mir aufgefallen ist, wenn du im TUT ne Zeile nur durchstreichst, wird sie mir Copy&Paste trotzdem als ganz normale Zeile übernommen. Sollte man evtl. zusätzlich auskommentieren.
Könnte ne Fehlerquelle für andere sein.

Ja, da hast Du recht. Ändere ich oben.

Ich versuche mal den Fehler zu reproduzieren bei mir (aber ändere oben erstmal nichts im Tut.)
Bei mir läuft es..

Bei Dir: mach es ohne "EnableDelayedExpansion"
a) die erste Zeile ändern in "@echo off & setlocal"
b) statt der FOR..IN..DO-Anweisung die aufgelösten 4 (tippfehlerbereinigten) Zeilen.

Solche nicht-reproduzierbaren Bugs liebe ich... *ggg

nette Grüße zurück

Biber
Biber
Biber 14.02.2006 um 11:35:15 Uhr
Goto Top
@superfrog

Hat doch alles eine logische Erklärung... war mein Fehler
Da ich ja nicht davor zurückschrecke, auch weniger dokumentierte Einstellungen und Schalter zu verwenden, habe ich bei mir ein paar Grundeinstellungen für die CMD-Umgebung in die Registry eingetragen.

Sieht bei mir so aus:
reg query "HKEY_CURRENT_USER\Software\Microsoft\Command Processor"

! REG.EXE VERSION 3.0

HKEY_CURRENT_USER\Software\Microsoft\Command Processor
CompletionChar REG_DWORD 0x9
DefaultColor REG_DWORD 0x74
PathCompletionChar REG_DWORD 0x9
DisableUNCCheck REG_DWORD 0x1
EnableExtensions REG_DWORD 0x1
DelayedExpansion REG_DWORD 0x1


Der zweite (nicht oder kaum dokumentierte) fette Eintrag bewirkt bei mir, dass die DelayedExpansion, die verzögerte Variablenauflösung standardmäßig aktiviert ist.
Bei "Auslieferung ab Werk" ist das auf Windowsrechnern nicht so.

Also, wie immer im Leben zwei Möglichkeiten für Dich

a) entweder den Batch OHNE "EnableDelayedExpansion" laufen lassen (denn mit den Schaltern /u und /s wird ja wieder auf den Standardzustand zurückgesetzt). Und der ist bei Dir "DisableDelayedExpansion"...
Daher bleibt Dir nur der Weg der vier Zeilen statt der einen FOR-Zeile...
IF defined DD Set "DD=10%DD%"
IF defined TT Set "TT=10%TT%"
b) oder die beiden fett gedruckten Registry-Einträge übernehmen für Deinen USER oder für den ganzen Rechner
(=HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor)

Aus Kompatibilitätsgründen, wenn Du den Batch auf verschiedenen, "unbekannten" Rechnern einsetzen willst, empfehle ich die Variante a)

Die Variante b), DelayedExpansion als Default zu setzen, ist halt undokumentiert und, wie eben bewiesen, macht die Benutzung solcher undocumented features nur Ärger...

Grüße
Frank / der Biber aus Bremen
[Edit 14.2.2006]
Habe es oben in der Tut-Version auf die "kompatible Version" zurückgedreht.
Sollte jetzt (v006b) also auf jedem W2K++-Rechner laufen.
Aber das habe ich auch schon bei der v001 gedacht *gg.
[/Edit]
superfrog
superfrog 14.02.2006 um 14:05:50 Uhr
Goto Top
Hallo Biber,

schön dass du es noch rausgefunden hast und es kein "nicht-reproduzierbarer Bug" ist.
An sowas kann man nämlich verzweifeln.

Werde mich dann aber wohl für Version a) entscheiden, da wie du schon sagst, ich die Registry-Settings auf anderen Rechnern ja nicht unbedingt weiß.
Obwohl es genau genommen doch nicht so unübersichtlich viele Rechner sein werden, als dass man da nicht auch nen bestimmten Registry-Eintrag vorraussetzen könnte.
Aber ich will die Fehlerquellen bei sowas möglichst minimieren.

Hab meinen Batch jetzt entsprechend angepasst.

Nett von dir dass du, obwohl du es nicht nachvollziehen konntest, trotzdem nochmal geforscht hast.
Abermals Danke.

Grüße
superfrog
as-net
as-net 17.02.2006 um 12:05:19 Uhr
Goto Top
Hallo,

klasse Anleitung.

Habe aber leider 2 Kleine Probleme bei mir gefunden.

Beim Aufruf der Batch Datei von einem Netzlaufwerk, das als Laufwerk
Verbunden ist, hängt die Batch, oder geht in eine Schlaufe.
Da der Aufruf von /? und /h Funktioniert, denke ich, das beim Aufruf der VBS
Routine was daneben geht.

Das 2. Problem betrifft die Variablen DoY und cDoW werden nicht angezeigt.
Desweiteren scheint die Variable DoW der Wert für DoY zu sein.

Folgende Ausgabe bekomme ich:

INDate=17.02.2006
INTime=12:00:26,93
DateOrder=TT-MM-JJ
KW=7
OSKW=6
DoW=48
DD=17
TT=17
MM=02
JJ=2006
YY=2006
hh=12
min=00
ss=26
ms=93

Getestet habe ichs auf meinem Rechner (Windows XP Pro De, Sp2), sowie auf einem meiner Server (Windows 2000 De, SP4)

Die Variablen DoY und cDoW werden gar nicht gesetzt.

Muss ich an meinem System noch diverse Einstellungen vornehmen?

Grüße

AS-Net
Biber
Biber 17.02.2006 um 13:04:23 Uhr
Goto Top
@as-net

Mea culpa, mea culpa, Andreas...

Tippfehler...
Vorhin stand oben:
for /F "delims=, tokens=2-5" %%a in ('cscript //nologo %vbsSnippet% %INDate%') do (   

...Jetzt richtig, wie <i>eigentlich</i> auch im vorangegangenen Bugfix verbal beschrieben:

for /F "delims=, tokens=1-4" %%a in ('cscript //nologo %vbsSnippet% %INDate%') do (   
Sorry für den Stress und ich mach das nicht, um diesen Beitrag am Leben zu erhalten... war pure Dussligkeit..

Schönes Wochenende
Biber
Aktuelles Ergebnis mit dem Copy & Paste von oben:
h:\tmp\v006bGetDatetimeInfos.bat /s
INDate=17.02.2006
INTime=13:06:26,83
DateOrder=TT-MM-JJ
KW=7
OSKW=7
DoW=6
DoY=48
cDow=Fr
DD=17
TT=17
MM=02
JJ=2006
YY=2006
hh=13
min=06
ss=26
ms=83
[Edit]
So, nochmal in Ruhe:
Grund für diese merkwürdige FOR /F..IN.DO-Klausel "tokens=2-5" war ja ein M$-Bug in den frühen W2000-Versionen.
Da kommt vom VB-Schnipsel statt
(Erwartet laut Doku: KW OSKW DoW DoY) = 7 7 6 48
zurück:
(Real Win2000: KW OSKW DoW DoY) =&#9792;7 7 6 48
...also dieses unsägliche FormFeed-Zeichen.

Wenn ich das umgehen will, muss ich schon bei "tokens 2-5" bleiben, ABER einen ersten DUMMY-Wert mit zurückgeben.
Rückgabe VBS neu:
(Win2000: KW OSKW DoW DoY) &#9792;x,7,7,6,48
(WinXP: KW OSKW DoW DoY) x,7,7,6,48

Deshalb: ich ändere es oben wieder auf "tokens 2-5", ABER führe einen DUMMY-Parameter#1 mit dem Wert "x" ein. Und den ersten Token, egal ob "&#9792;x" oder nur "x" lasse ich außen vor.

Sorry für das Geeiere... jetzt sollte es unter Win2000(first editions) und W2000fixed und WinXP ff laufen... flehend gen Himmel blick
[/Edit]
as-net
as-net 17.02.2006 um 14:29:00 Uhr
Goto Top
Hi Biber,

Yepp, das war es.

Kleiner Fehler, große Wirkung. face-smile

Danke für die schnelle Hilfe, und wegen deiner "Dussligkeit"
dank ich dir für das Script, und Fehler können nun mal,
und vor allem in der Heutigen Zeit schnell Passieren.


Ich wünsch dir Ebenfalls ein schönes Wochenende

Gruß

AS-Net
sv-klm
sv-klm 12.03.2006 um 16:24:58 Uhr
Goto Top
wollte, warum es am 07. eines Monats
funtioniert, am 08. und 09. aber nicht.


... das liegt an der Grenze der Oktalzahlen - diese gehen von 0 -7 und die vermaledeite Shell vermutet an dieser Stelle eben genau Oktalzahlen face-sad

Gruß,
Martin
perry6
perry6 21.04.2007 um 13:25:39 Uhr
Goto Top
@Biber

Hallo Biber

super Tutorial, darfst gerne so weitermachen, wie hast du geschrieben ? .. "Ich verzichte mal auf alle Registry-Abfragen (nur Bordmittel). .." genau richtig, nochmals super.

So genug gelobt (trotzdem super), eins verstehe ich trotz intensiven studierens nicht.

Wie erreiche ich, das die mit set gesetzten "umgebungsvariablen" in allen anderen (nach Ablauf des GetAllDateTimeInfos.bat) geöffneten Eingabeaufforderungen auch abrufbar und benutzbar sind, also "global" nutzbar.

Du hast zwar mal geschrieben

"... Dann noch einen optionalen Parameter zum Setzen der ermittelten Werte "global". Nenn ich mal /s wie SET. ..."

aber die %umgebungsvariablen% sind nur in diesem cmd-Aufruf verwertbar nicht in weiteren ??
Welchen Schalter habe ich übersehen ? Welche Einstellung muss ich ändern ??

Gruss und Danke
perry6
Biber
Biber 22.04.2007 um 17:26:14 Uhr
Goto Top
Moin perry6,

willkommen um Forum und danke für das Auseinandersetzen mit dem Thema.

Mal sehen, ob ich die Unklarheiten ausräumen kann.

Zu Deiner Frage "Wie kann ich erreichen, dass die gesetzten Variablen auch in nachfolgend geöffneten CMD-Sessions verfügbar sind"..

Schlechte Nachricht: Gar nicht.
Gute Nachricht: Ist auch nicht erstrebenswert.

Die Umgebungsvariablen einer CMD-Session gelten grundsätzlich immer nur in einer Session,
d.h. solange das Fenster, in der Batch (oder die Kommandozeilenbefehle) ablaufen nicht geschlossen wird.
Oder exakter gesagt, innerhalb dieser Instanz der CMD.exe.
Anders wäre es aus mehreren Gründen auch nicht handelbar.

Stell Dir vor, jede in einem Batch/jede am CMD-Prompt gesetzte Variable wäre für alle nachfolgend geöffneten CMD-Instanzen gültig.
Du würdest raschelig werden. Sobald Du dann in einem Batch den aktuellen Pfad ändern würdest mit CD,
würden alle nachfolgend CMD/Batchaufrufe sich in diesem Verzeichnis befinden.
Denn die Variable %CD% ("aktuelles Verzeichnis") würde ja für alle gelten.
Wenn Du in einem Batch die Farbenauf gelb auf blau einstellst oder die Variable %logFile% auf "x:\logs\my.log" setzt, würde es für jeden hinterher gestarteten Batch inclusive der im Taskplaner hinterlegten gelten etc etc.

Diese Art von "globalen" Variablen meinte ich nicht - diese wären für mich "sessionübergreifende Systemvariable".
Diese gibt es auch unter Windows - Variable, die für alle CMD-Sessions als Anfangsumgebung gelten werden über die Registry gesteuert (%username%, %computername%, %userprofile% oder,
da M$ ja immer im Laufe der Implementierung Angst vor der eigenen Courage bzw. der eingeschlagenen Programmierstrategie bekommt, zum Teil auch beiläufigt bei DLL-Aufrufen (%APPDATA% durch die shell.dll; %LOGONSERVER%...).
Was dann zu so lustigen Effekten führt wie "%APPDATA% nicht gesetzt" (Hatten wir neulich gerade im Forum).

Was M$ aber nicht tut oder nur SEHR verhalten ist: Variablen zu definieren, die sich zur Laufzeit selbst aktualisieren.
Es existieren einige wenige Variablen, die "dynamischen" Charakter haben, also zu unterschiedlichen Zeiten auch jeweils die Gerade-Jetzt-Werte haben.
Dazu gehören %date%, %time%, %cd% - aber alle Vaariablen, die Du über den Befehl SET angezeigt bekommst, sind statisch.
Bedeutet, die bekommen bei Start des CMD-Inerpreters einen Anfangswert (aus der Registry) und dieser Wert ist eine einmalig erzeugte Kopie der Werte aus der Registry.
Ändert sich höchstens, wenn Du ihn änderst, aber auch dann hättest Du nur den Wert der Kopie geämdert.
Das würde zwar in der aktuellen CMD-Session weiter gelten, aber ein "neu" geöffnetes CMD-Fenster würde wiederum die Registrywerte als Blaupause nehmen.

Geht also per design nicht, was Du Dir erhoffst.
Zwar könntest Du durch einen Autostart-Batch bei jedem CMD-Start die von mir definierten Werte für Tag, Monat, Jahr, Stunde, Minute etc. sofort "als erstes" setzen (lassen).
Aber diese würden "statisch" sein, Bedeutet, wenn Du um 16:34h ein CMD-Fenster öffnest, würden zwar %HH% auf 16 und %MM% auf 34 gesetzt werden.
Aber: auch um 22:55h wären %hh% immer noch 16 und %mm% immer noch 34 und nich 22 bzw. 55.
Dynamische Variablen und/oder "allgemeinverbindliche" Variablen für alle geöffneten CMD-Instanzen sind nicht möglich.

Der CMD-Interpreter ist ein Werkzeug mit der Intension, dass es mehrfach parallel aufgerufen werden kann, um auch verschiedene Aufgaben/Tasks/Batches unabhängig und unbeeinflusst voneinander parallel ausführen zu können.

Mein Tipp an dieser Stelle: Versuche niemals, gegen das Design, gegen die Philosophie eines Werkzeugs zu programmieren.
Dann ermittle eben die %TT%, %MM%, %JJ%...-Variablen aktuell, jedesmal unmittelbar vor der Stelle, wo Du sie brauchst.

Alles andere wird aufwändig oder zu sehr neben der Spur, um allgemein verwendbar zu sein.

Somit also sorry, du hast keinen Schalter übersehen. Dieses Feature kann ich ich nicht bieten.

Grüsse
Biber
bjoernanger
bjoernanger 18.07.2007 um 13:23:53 Uhr
Goto Top
Hi,

ich habe mit Begeisterung gesehen, was man alles aus Windows rauskitzeln kann. Aber eine kleines Problem hab ich da dennoch:
Gibt es eine Möglichkeit, das die Kalenderwochen am Montag anfängt?
Also 15.7.07 = KW 28 und 16.7.07 = KW 29
Oder hab ich da etwas übersehen ?

Gruß
Biber
Biber 18.07.2007 um 18:18:46 Uhr
Goto Top
Moin bjoernanger,

ich habe es oben beschrieben (oder zumindest skizzert):

Suche mal nach folgendem Abschnitt:
....
Idee ist jetzt ziemlich einfach: wenn ich einen kleinen *.vbs-Zweizeiler benutze, der mir die Werte liefert, die ich in meinem Batch brauche, brauche ich nichts mit eigenen Algorithmen berechnen.
Berechnet haben möchte ich vier Werte:
- Kalenderwoche (so, wie sie z.B. in meiner Firma definiert ist)
- Kalenderwoche wie sie laut Registry errechnet wird. Ihr erinnert Euch an die beiden Werte iFirstDayOfWeek (REG_SZ, 0-6) und iFirstWeekOfYear(REG_SZ, 0-2)? Die stehen da drin, aber ich kann die nicht ändern über die GUI/Ländereinstellungen. Microsoft. *gg
- Wochentag / DoW als numerischen Wert. Egal wie durchnummeriert, ich muss es bloß wissen. Hier kommt es als 1=So, 2=Mo etc zurück
- Tag des Jahres / DoY

Okay, wenn der Zweizeiler so aussieht:


'----snipp DateKrams.vbs  
Wscript.Echo " " & DatePart("ww",Date,vbSunday,vbFirstFourDays) & " " &_   
DatePart("ww",Date) & " " & DatePart("w",Date) & " " & DatePart("y",Date)  
'------snapp DateKrams.vbs   
...dann kann ich heute, am 3.10.2005 folgenden Output vom CMD-Prompt aus erhalten:

>for /f "tokens=2-5" %a in ('cscript //nologo DateKrams.vbs') do @echo KW=%a,KWOS=%b,DoW=%c,DoY=%d  
KW=40,KWOS=41,DoW=2,DoY=276
Zum Verständnis der VBS-DatePart()-Parameter empfehle ich M$-Technet Retrieving Specific Portions of a Date and Time Value.

Und ja, um der Frage zuvorzukommen, manchmal muss man/frau 2x200 Zeilen lesen, um 2 Batch- und 2 VBS-Zeilen zusammenschroten zu können...

Passt?

Grüße
Biber
bjoernanger
bjoernanger 19.07.2007 um 13:56:33 Uhr
Goto Top
Das ist ja echt ein wenig kompliziert ...
Wenn man erstmal weiß, das man den DatePart-Befehl tunen muß ist alles super.
Dann kommt man schnell zum Schalter vbSunday der in meinem Fall vbMonday heißen muß.

Jetzt läufts.
Biber
Biber 19.07.2007 um 14:16:51 Uhr
Goto Top
Moin bjornanger,

>..der in meinem Fall vbMonday heißen muß.
Tja, mein Vorteil an dieser Stelle ist, dass ich seit Beginn des Arbeitslebens jeden Tags aufs Neue das Gefühl habe, dass mein Schalter permanent auf vbMonday steht.. face-wink

Von daher habe ich mich viel damit beschäftigt...

Freut mich, dass es läuft.

Grüße
Biber
yotaman
yotaman 20.09.2007, aktualisiert am 21.02.2012 um 00:10:06 Uhr
Goto Top
hi Biber,

ein fantastisches Script face-smile

Habe eine Anmerkung:
Durch deine Änderung von %time& auf 'time/t' (weil es %time% nicht unter WinNT gibt, werden nun die Sekunden (+ms) nicht mehr ausgegeben (zumindest bei mir)
ich meine damit:

C:\Documents and Settings\Administrator>echo %time%
1:00:48,41
C:\Documents and Settings\Administrator>time/t
01:00

Das ist für meine Zwecke schlecht (will Zeiten in ein Log schreiben, und da brauch ich auch die Sekunden)


ich denke, das kann man so unmgehen

...
set MYTIME=%time%
TIME /T > CURRTIME.tmp
IF NOT DEFINED MYTIME SET /p MYTIME=<CURRTIME.tmp
del /q CURRTIME.tmp
...

und weiter:
FOR /F "delims=" %%i in ("%MYTIME%") do Set "INTime=%%i"
(statt: FOR /F "delims=" %%i in ('time/T') do Set "INTime=%%i" )
...

Falls %time% gesetzt ist, nimmt er es. Falls nicht, nimmt er time/t

lg,
Werner
Biber
Biber 20.09.2007 um 13:15:23 Uhr
Goto Top
Moin yotaman,

danke für Deine Ergänzung " %time% unter NT" - baue ich oben im Skript ein heute abend.
Und schreibe vielleicht noch einen Satz zu dem Programmierstil von Bills Bande bei %time% vs. time/t.

Dein versehentliches Doppelposting weiter unten habe ich gelöscht.

Danke und Grüße
Biber
[Edit 20.9.2007 abends]
Änderungen eingebaut in das GetAllDateTimeInfos-Skriptchen als "Vers. 0.10"
[/Edit]
yotaman
yotaman 21.09.2007 um 00:06:26 Uhr
Goto Top
hab noch eine kleinigkeit hinzugefügt:

Da man oft auch das Jahr 2 stellig braucht:

...
For %%i in (INDate INTime %AllDateTimeVars%) do @if defined %%i set %%i

:: Werner 070920: Add ShortYear
IF defined YY SET "YYSHORT=%YY:~-2,2%"
IF defined JJ SET "JJSHORT=%JJ:~-2,2%"
echo YYshort=%YYSHORT%
echo JJshort=%JJSHORT%


lg,
Werner
Safwat
Safwat 28.02.2008 um 17:39:08 Uhr
Goto Top
Hallo,

hab Dein Artikel gelesen ! und finde es supper. Danke an dieses Stelle!
Hab aber noch ein Frage die Dein Script aufbaut.
Kann ich irgendwie ein verzeichniss durchsuchen und nur die Dateien bestimmtes Monat rausfilten ?
Also ich will in bestimmtes Verzechnis alle Dateien suchen und nur die Dateien bestimmtes Monat zippen oder wo anders verschieben ! Hättest Du da ein Tipp ?

danke im voraus
Biber
Biber 28.02.2008 um 18:15:00 Uhr
Goto Top
Moin safwat,

willkommen im Forum.

Deine Anforderung ist ohne großartige Datumsarithmetik lösbar, hier reicht ein einfacher Textvergleich mit dem Dateidatum.

Demo am CMD-Prompt [einzugebende Zeilen durch ">" gekennzeichnet]:
(=18:02:19  D:\temp=)
>for %i in (e:\work\*.txt d:\temp\*.txt) do @echo %~ti "%i" |find "04.2007"  
25.04.2007 07:13 "d:\temp\nureinzelne.txt"  
30.04.2007 19:46 "d:\temp\UPDate.txt"  
13.04.2007 15:54 "d:\temp\Userids.txt"  
25.04.2007 07:09 "d:\temp\vcard.txt"  

(=18:02:44  D:\temp=)
>for %i in (e:\work\*.txt d:\temp\*.txt) do @echo %~ti|find "04.2007 ">nul && @echo "%i"  
"d:\temp\nureinzelne.txt"  
"d:\temp\UPDate.txt"  
"d:\temp\Userids.txt"  
"d:\temp\vcard.txt"  

Der erste proof-of-concept-Befehl würde aus zwei Verzeichnissen alle *.txt-Dateien herausflöhen,
die ein "04.2007" im Dateidatum haben, also aus dem April 2007 sind.
Alle anderen Monat/Jahr-Kombinationen werden herausgefiltert.
...natürlich würden auch alle Dateien angezeigt, die den Text "04.2007 " im Namen tragen, ich weiß, ich weiß..

Der zweite Befehl zeigt eher, worauf es ankommt - auf eine Liste der Dateinamen mit vollständigem Pfad in Anführungszeichen.

Der dritte Befehl, der Dein nächster Schritt sein könnte und deshalb nicht von mir hingetippt wurde,
sollte dann das @echo "%i" ersetzen durch ein move "%i" X:\Aprildaten\" oder was immer Du mit diesen Dateien vorhast.

Grüße
Biber
P.S. Und der/die nächste, der hier wieder "danke im voraus" schreibt, wird ohne Kommentar gelöscht... face-wink
Safwat
Safwat 03.03.2008 um 14:48:25 Uhr
Goto Top
Danke Biber für Dein Antwort !
Es klappt das ich die Dateien rausfiltern kann !
das moven klappt auch !
Hast Du ein Idee wie diese Files dann in einem einzigen File zippen kann ?
z.B. von Dein Beispiel die gefilterte Daten dann auch als 042007.zip zu sichern ?
Biber
Biber 03.03.2008 um 19:05:34 Uhr
Goto Top
Moin safwat,

dazu brauchst Du ein von der Commandline bedienbares Zip-Utility.
Sehr empfohlen wird 7-zip, siehe auch die Links unten.

Über die Forumssuche nach 7zip findest Du auch mehrere Beispielbätche, falls die mitgelieferte Hilfe nicht ausreichend sein sollte.

Hier im Rahmen dieses Tutorials wollte ich lieber noch ein bisschen Platz für eventuelle Rückfragen lassen und dieses Nebengleis nicht weiter verfolgen.

Wenn Du gar nicht weiterkommst mit der Zipperei, mach bitte in "Batch & Shell" einen neuen Thread auf.

Grüße
Biber
65645
65645 27.05.2008 um 17:40:31 Uhr
Goto Top
Hallo Biber,

vielen Dank für die Erklärung und Weitergabe Deines KnowHows.

Rauspicken kann ich mir diverse Teile, nur funktioniert Dein batch leider bei mir nicht.
ich bekomme immer eine Fehlermeldung:
"Set" ist syntaktisch an dieser Stelle nicht verarbeitbar.

Dies kommt m.E. aus der Belegung der cDoW.
Wenn ich nach ... set "MM=%MM:~-2,2%" ... einfüge:
IF NOT defined DoW echo DoW nicht definiert
pause
hält der batch bei pause an und sagt vorher, daß DoW nicht definiert ist.

Eine Pause nach den zwei Blöcken: If defined DD / TT erreicht der Batch nicht mehr,
da er mit: "Set" ist syntaktisch an ... abbricht.

Was habe ich falsch konfiguriert? Gibt es das öfter oder bin ich der Einzige?

lg
stefan#
Biber
Biber 27.05.2008 um 18:29:29 Uhr
Goto Top
Moin stefan#,

Was habe ich falsch konfiguriert?
Du wahrscheinlich nicht...siehe unten.
Gibt es das öfter..
Verglichen mit dreibeinigen Schimmelstuten oder verglichen mit Werbepausen auf SAT1?
...oder bin ich der Einzige?
Nein, aber anderen skripten selbst einen Workaround..face-wink


Tja, Spass beiseite, das kommt davon, wenn M$'s und meine geballte Schlamperei aufeinandertreffen....

Die Ursache wird vermutlich
  • einerseits in den oben geposteten Registry-Einstellungen liegen, insbesondere an dem "Mit welchem Tag fängt die Woche an"-Wert
HKCU\Control Panel\International\iFirstDayOfWeek
siehe hier beim sympathischen Weltmarktführer
  • andererseits in meiner Schlamperei, dass (weil anderes bei meinen Tests nie aufgetreten ist) die numerischen Returnwerte für Wochentage nur 1-7 sein können... einen Wert 0 an der Stelle fange ich nicht ab.

<rausred>Deshalb habe ich auch damals schon mit den den Sourcecode geschrieben: "mit meinen Reg-Einstellungen".</rausred>

Ich hab mich damals schon köstlich darüber amüsirt, dass sich diese Werte (VIELE Datumsdefinitionswerte!) gar nicht über GUI ändern lassen... wenn Dir also irgendeine verschnarchte Freeware-Firlefanz-Anwendung diese Datums-Berechnungs-Grundlagen meint umschießen zu müssen... da kommst Du nie wieder dran. Und bei Dir ist dann Sonntag Montag.

Bitte lass uns Deinen Fall über PN oder Mail (in meinem Profil) klären und hier dann den endgültigen Backfix posten.

Grüße
Biber
testbild
testbild 09.06.2008 um 15:19:22 Uhr
Goto Top
Hallo Biber,

erst mal vorab: Klasse, der Workshop!

Und nu ne kleine Frage, die ich bisher noch nicht lösen konnte...
Deinen Zweizeiler und ne kleine Batch dazu als Grundlage hab ich was ähnliches zusammengebaut, um die Datensicherung mehrerer Server (welche einmal die Woche komplett woandershin gesichert werden) nach Kalenderwochen zu speichern, so weit, so gut, klappt auch. Jetzt das Problem: Da der Speicherplatz leider etwas beschränkt ist (und der Kunde zu geizig, mehr bereitzustellen), kann ich maximal 3 Wochensicherungen auf der Platte unterbringen, für die nächste muss zuerst die älteste Sicherung gelöscht werden. Also hab ich mir gedacht, könnte man ja anhand der aktuellen Kalenderwoche berechnen, einfach davon 3 abziehen, die entsprechende Sicherung mit der Kalenderwochennummer drin löschen, gut ist... Dem war aber nicht so, ich krieg das einfach nicht hin und muss stattdessen zur Zeit jede Woche VOR der aktuellen Sicherung die älteste manuell löschen. Ist jetzt nicht mühselig, aber da ich irgendwann auch mal Urlaub habe, würde ich das gerne automatisieren, bevor mir die Sache um die Ohren fliegt und ich nicht da bin... Leider gibt es vor Ort nur User, die gerade mal wissen, wo der Einschaltknopf vom eigenen Rechner ist, von da ist also auch keine Hilfe zu erwarten. Hast Du irgendeine Idee, wie ich das hinkriegen könnte?

Danke schon mal vorab
testbild
Biber
Biber 09.06.2008, aktualisiert am 18.10.2012 um 18:35:49 Uhr
Goto Top
Moin testbild,

danke - freut mich, wenn es nützlich war.

zum Thema "Nur die letzten x Sicherungen behalten und alle anderen Löschen"... dazu haben wir im Bereich "Batch & Shell" einige Varianten.

Die Mimik ist unter anderem hier Die ältesten Dateien löschen beschrieben... und auch noch in ein paar anderen Beiträgen.

Aber der erstgenannte ist so ziemlich die Mutter aller "Lösch-mir-die-ältesten-Sicherungsordner" hier im Forum.

Wenn es klemmt (=nach einer halben Stunde immer noch keinen Ansatz gefunden), dann mach einen neuen Beitrag auf in "Batches4Hell", da wird es auch vor dem Abendessen fertig.

Grüße
Biber
35784
35784 11.06.2008 um 17:25:55 Uhr
Goto Top
Hallo Biber,

ich tüftele schon eine Weile (leider bis jetzt Erfolglos) mit Deiner Batch herum!
Ich möchte alle Dateien in einem Verzeichnis in das Format:

JJ.MM.TT_hh.mm.ss.ms.txt

umbenennen.

Die Zeit muss dem Zeitstempel der Datei entsprechen!


Hast Du hierfür auf die eine Lösung für mich?
Biber
Biber 11.06.2008 um 20:48:08 Uhr
Goto Top
Moin washy,

... in das Format:
JJ.MM.TT_hh.mm.ss.ms.txt umbenennen.

Wie meistens im täglichen K(r)ampf mit dieser Redmonder Softwareklitsche gibt es auch hier gute und schlechte Nachrichten für Dich.

Die schlechten lieber zuerst:

Mit Batch (also dem, was CMD.exe beinhaltet) und auch mit VBSkript ist da Feierabend... beide sind primär dafür da, schnell die wichtigsten und am häufigsten nachgefragten Informationen zu liefern - vorzugsweise auf den Bildschirm.
Nicht oder seltenst zur maschinellem Weiterverarbeitung.
Und das, was mal schnell mit einem DIR am CMD-Prompt oder mit einem WScript.Echo rausgerotz^H^H rausgeschrieben wird, ist halt nicht so genau.
Beim Dateidatum hört das Allgemeininteresse bei den Sekunden auf,
beim DIR-Befehl sogar bei den Minuten (Ausgabe: "11.06.2008 20:05 ....").
VBScript ist kaum genauer:
Set f = CreateObject("Scripting.FileSystemObject").GetFile(Wscript.Arguments(0))  
Wscript.Echo "Datei " & f.Path  
Wscript.Echo "Erstellt am: " & f.DateCreated  
Wscript.Echo "Letzter Zugriff: " & f.DateLastAccessed  
Wscript.Echo "Letzte Änderung: " & f.DateLastModified  
Ausgabe bei Aufruf:
>cscript //nologo e:\daily\GetFileAttr.vbs werte.txt
Datei D:\temp\werte.txt
Erstellt am: 06.07.2006 19:18:58
Letzter Zugriff: 29.04.2008 22:27:04
Letzte Änderung: 06.07.2006 19:19:54
... keine Millisekunden zu sehen... genauer als so gehts nicht.

Deshalb habe ich oben im Workshop-Schnipsel eine verschämte Zeile drin:
...
For %%i in (ss ms) do if not defined %%i set "%%i=00"   
...
...auf deutsch: wenn Du keine Sekunden und keine Millisecs hast, setze sie auf "00".
Und beim Datei-Datum ist es immer so.

Ende der schlechten Nachrichten.
Gute Nachricht:
Beispielsweise JScript kann schon damit umgehen, dass es auch Menschen mit Millisekunden-Faible auf der Welt gibt.
In JScript lässt sich ein Datumswert aufdröseln bis auf MilliSekunden.
...
var myDate = new Date(pDate);
var ms= myDate.getMilliseconds();   // <-- Nur in JS.
   // Nicht unter CMD.exe oder  VBScript
...
Wenn also JS aus einem als Parameter übergeben Dateidatum die Millisecs rausflöhen könnte...
-> Das wäre die billigste Lösung, es sei denn, Du treibst irgendwo ein (existierendes) Konsolenutility auf, das das Dateidatum mit Sekunden und Millsekunden ausgibt.
Sollte es eigentlich auch schon geben.

Mit meinen eher beschränkten Batch-Mitteln ist es nicht möglich, aus dem Datum einer bestehenden Datei Sekunden und Millisekunden zu ermitteln.

...aber das Sternzeichen könnte ich errechnen..

Grüße
Biber
35784
35784 11.06.2008 um 23:56:08 Uhr
Goto Top
Hallo Biber,

VIELEN Dank für die schnelle und ausführliche Antwort!

hmmmm
auf Millisekunden könnte ich ja verzichten!
Aber ich kann nicht ausschließen, das zwei (oder mehrere) Dateien innerhalb einer Minute erzeugt werden!
Daher sind Sekunden wohl die minimale Anforderung!

Da muss ich noch mal auf die Suche gehen
und schau'n, wo ich die Sekunden her bekomme!


...aber das Sternzeichen könnte ich errechnen..

*Schmunzel*
Biber
Biber 12.06.2008 um 07:48:18 Uhr
Goto Top
Moin washy,

na ja, wenn es nur um die Sekunden des Dateidatums geht, das bekommen wir mit diesen beiden Schrubbeltools CMD.exe und VBSkript grad noch hin.
Nur Millisekunden sind auf dem Weg unerreichbar.
Wenn Du das Dateidatum genauer brauchst als es der DIR-Befehl anzeigt, dann holen wir das mit den gestern angedeuteten GetFile()-Methoden.

Demoschnipsel:
::---- snippFdLastModified.cmd
@echo off & setlocal
Set "GFLM=%temp%\GetFiledateLastModified.vbs"  
 >"%GFLM%" Echo Set f = CreateObject("Scripting.FileSystemObject").GetFile(Wscript.Arguments(0))  
>>"%GFLM%" Echo Wscript.Echo f.DateLastmodified  
For /f "delims=" %%i in ('cscript //nologo "%GFLM%" "%~1"') do set "fdlastMod=%%i"  
Echo Dateidatum "%~1":   
Echo %fdlastMod%
Demo am CMD-Prompt, erst Datumsgenauigkeit mit DIR, danach mit VBSkript:
>for %i in (werte.txt) do @echo %~ti
06.07.2006 19:19
>FdlastModified.cmd werte.txt
Dateidatum "werte.txt":  
06.07.2006 19:19:54
Diese Rückgabe (Variable %fdlastMod%) kann wiederum von meiner GetAlldateTimeInfos verarbeitet werden.
Oder aber (was sinnvoller wäre) direkt für diese Umbenamserei nutzen.
Wenn Du Schwierigkeiten haben solltest, das in einen Massenverarbeitungs-Batch (viele Dateien/ganze Unterverzeichnisse) einzubauen, lass uns das bitte in einem neuem Beitrag klären.

Grüße
Biber
51705
51705 22.10.2008 um 14:28:24 Uhr
Goto Top
Danke Biber!

Kleine Anmerkung, 'AllDateTimeVars' wird mit '/u' nicht gelöscht.

Grüße, Steffen
Biber
Biber 23.10.2008 um 23:20:35 Uhr
Goto Top
Moin srmerlin,

Danke für den Hinweis.

Da hatte ich wohl ein bisschen geschlampt...

Habe oben im Tut die Zeile 30 geändert von...
 For %%i in (%1 %2 %3) Do If /i %%i==/u goto :Cleanup
...auf...
For %%i in (%1 %2 %3) Do If /i %%i==/u Set "AllDateTimeVars=" & goto :Cleanup

Damit wäre dieser Schönheitsfehler behoben.

Grüße
Biber
Aspedal99
Aspedal99 07.11.2008 um 17:23:55 Uhr
Goto Top
Hallo Biber,

ich setze dein sehr gutes Script schon sein fast 2 Jahren erfolgreich ein, auf WinXP SP2 Rechnern. Heute wollte ich es auf einem XP Sp3 Rechner einrichten leider ohne erfolg, es verschwinden alle Icons auf dem Desktop, inklusive der Startleiste und ich kann nur noch über den TaskManager den Benutzer abmelden.

Ich habe auf dem Client cwrsync drauf und sicher so verschiedene Daten. ich habe deine Datei mit folgendem Befehl in meine rsync Batch Datei mit eingebunden:


Call GetAllDateTimeInfos /s /q
set budir=%JJ%-%MM%-%TT%
set logdir=%JJ%-%MM%-%TT%_%hh%-%min%
Call GetAllDateTimeInfos /u

rsync -rlt --force --stats --ignore-errors --exclude-from=ausschluss.txt --delete --delete-excluded --backup --backup-dir=%budir% -av '/cygdrive/c/Dokumente und Einstellungen/User' TKServer::cwrsyncuser >E:\%logdir%_Logfile_von_User_Eigene.log


Mach ich da was falsch, oder ist dir bekannt dass es mit SP3 Probleme gibt?
Biber
Biber 07.11.2008 um 18:06:07 Uhr
Goto Top
Moin Aspedal99,

nein, das ist mir noch nicht bekannt.
Aber bei diesen ja in der Tat verheerenden Fehlfunktionen unter XP SP3 will ich natürlich schnellstmöglich die Ursache finden und in meinem Schnipsel berücksichtigen.

Bitte lass uns beide per PN oder Mail (in meinem Profil angegeben) weitermachen, das Ergebnis kommt dann natürlich hier ins Forum.

Grüße
Biber

[Edit 16.11.2008]
Habe bis heute noch keine Antwort - auch auf eine PN - erhalten und von daher keinen Ansatzpunkt.
Sollte auch jemand anderes diese Problem kennen, bitte gerne PN oder Mail an mich.
[/Edit}
dolsch
dolsch 19.02.2009 um 10:09:33 Uhr
Goto Top
Hallo Biber,

herzlichen Dank für das Tutorial. Du glänzt nicht nur mit Wissen, sondern kannst es auch sehr gut vermitteln.

Ich habe ein paar Fragen bzw. Anregungen:
  • Du ermittelst die Sekundenbruchteile, also eine zweistellige Zahl. Das müßten demzufolge hundertstel Sekunden und nicht tausendstel Sekunden sein, also Zentisekunden (cs, zs oder hs) und nicht Millisekunden (ms). Stimmt doch, oder?

  • Am Ende vieler Zeilen befinden sich ein oder mehrere Leerzeichen. Kann ich (oder muss ich) diesen Weißraum am Ende aller Zeilen bedenkenlos entfernen, oder gibt es irgend eine Zeile, in der er beabsichtigt ist? Als Alternative zur Möglichkeit, sich den Code in Form einer Datei herunterzuladen – was hältst du davon, die Zeilen mit Schlüsselwörtern zu beenden, die der Benutzer dann zusammen mit dem Weißraum per »Suchen und Ersetzen« im Editor entfernt?

  • Du wirst mir hoffentlich die folgende Anfängerfrage verzeihen: Wenn ich GetAllDateTimeInfos /s aus einem anderen Batch heraus aufgerufen habe, muss ich die Variablen wieder mit GetAllDateTimeInfos /u löschen?
Biber
Biber 19.02.2009 um 12:13:54 Uhr
Goto Top
Zitat von @dolsch:
Hallo Biber,
Moin dotsch und willkommen im Forum.
herzlichen Dank für das Tutorial. Du glänzt nicht nur mit
Wissen, sondern kannst es auch sehr gut vermitteln.
Danke für die Blumen.
Ich habe ein paar Fragen bzw. Anregungen:

  • Am Ende vieler Zeilen befinden sich ein oder mehrere Leerzeichen.
Kann ich (oder muss ich) diesen Weißraum am Ende aller Zeilen
bedenkenlos entfernen, oder gibt es irgend eine Zeile, in der er beabsichtigt ist? ...
Nein, diese in fast jeder (aber eben nicht jeder) Zeile vorhandenenen Leerzeichen am Ende-der-Zeile sind absolut unbeabsichtigt.
habe ich eben gerade auch durch "in den Speicher kopieren" des GetAllDateTimeInfo-Schnipsels reproduzieren können.
Keine Ahnung, wie das zustandekam - vermutlich habe ich die zuletzt ins Tutorial Version im "falschen" Editor bearbeitet oder so...
Ich stelle noch mal eine um die trailing blanks bereinigte Sourcecode-Kopie ins Tutorial oben.
Am nächsten Regentag.

* Du wirst mir hoffentlich die folgende Anfängerfrage verzeihen:
Wenn ich GetAllDateTimeInfos /s aus einem anderen Batch heraus
aufgerufen habe, muss ich die Variablen wieder mit GetAllDateTimeInfos /u löschen?
Sauberer ist es. Die mit "GetAllDateTimeInfos /s" sollen ja -by concept- global gesetzt werden.
Global im Zusammenhang mit Batch-Programmen bedeutet ja "für die Lebensdauer der CMD/Batch-Session".
Nicht für neue (z.B. über den Explorer oder den Taskplaner gestartete) Batches, die zufällig zeitgleich, aber "in einem eigenen Fenster" laufen.
Aber für das von Dir angedeutete Szenario:
  • CMD-Session-Fenster ist offen
  • BatchEins.bat wird gestartet
  • in BatchEins.bat wird "GetAllDateTimeInfos /s" aufgerufen
  • BatchEins.bat ist fertig und CMD-Session-Fenster bleibt geöffnet --------> Alle "GetAllDateTimeInfos /s"-Variablen sind und bleiben gesetzt,
  • Cmd-Session-Fenster wird geschlossen ----> Alle "GetAllDateTimeInfos /s"-Variablen sind Geschichte.

* Du ermittelst die Sekundenbruchteile, also eine zweistellige Zahl.
Das müßten demzufolge hundertstel Sekunden und nicht tausendstel Sekunden sein,
also Zentisekunden (cs, zs oder hs) und nicht Millisekunden (ms). Stimmt doch, oder?

Ja, da hast Du Recht und ich habe seit Jahrzehnten ganz gedankenlos auch die 2stelligen Sekundenbruchteile als "ms" bzw Millisekunden bezeichnet.
Sicherlich teilweise verleitet von der oft z.B. bei Datenbank-Uhrzeitformaten verwendeten Nomenklatur.
Formal wäre richtig, bei Millisekunden (= Tausendstel Sekunden) auch drei NK-Stellen anzuzeigen und nicht wie die Redmonder nur zwei NK-Stellen:
>time
Aktuelle Zeit: 12:01:39,16
Hier hingegen:
also Zentisekunden (cs, zs oder hs) und nicht Millisekunden (ms). Stimmt doch, oder?
Zenti-Sekunden ...... hmmja.... ... als Analogie zu
  • Meter->[Dezimeter]->Zentimeter->Millimeter würde es passen.
Hier habe ich [Dezimeter] in eckige Klammern gesetzt, weil Dezimeter eher ungebräuchlich ist und von einem geringen Prozentsatz der MitbürgerInnen verstanden wird.
  • Sekunde->[[Dezisekunde???]]->[Zentisekunde?]->[Millisekunde]
---> den Ausdruck "Dezisekunde" habe ich noch nie irgendwo gesehen und auch Zentisekunde.... hm ja, klingt einleuchtend, aber auch ungebräuchlich.

Eventuell mach ich doch aus dem Tutorial eine Doktorarbeit "Umgang mit Datum und Zeit in der Datenverarbeitung" ? face-wink

Grüße
Biber
dolsch
dolsch 21.02.2009 um 10:43:00 Uhr
Goto Top
Hallo Biber!

Danke für die Beantwortung meiner drei Fragen.

Google liefert nur 44 Ergebnisse für Zentisekunde. Die allgemein gebräuchliche Bezeichnung ist sicherlich Hundertstel (Sekunde). Ich meine aber, dass Zentisekunde deutlich bequemer auszusprechen ist.

Die englische Bezeichnung Centisecond findet Google immerhin 11000 mal.

Wie viele Mitbürger Dezimeter oder Dezisekunde nicht verstehen, halte ich in diesem Zusammenhang nicht für relevant, weil wahrscheinlich noch mehr Mitbürger weniger von Batch-Scripting verstehen als ich und sogar 99,9% weniger als du. Ich hätte also keine Bedenken, auch die Bezeichnung Deciseconds einzusetzen, wenn ich davon ausgehen müßte, dass 99,9% meiner Mitbürger auch dann gründlich (einige gar vergeblich) nachdenken müßten, wenn ich Zehntel Sekunde schriebe. Smile.

So viel zur Allgemeingebräuchlichkeit der Bezeichnungen für die Sekundenbruchteile zwischen der vollen Sekunde und der Millisekunde.

Es gibt aber einen internationalen Standard für die Bezeichnungen der Vielfachen und der Bruchteile von Einheiten im metrischen System. Über die Sekunde und das internationale Einheitensystem:

http://en.wikipedia.org/wiki/Second
http://de.wikipedia.org/wiki/Sekunde_(Einheit)#Abgeleitete_Ma.C3.9Feinh ...
http://en.wikipedia.org/wiki/International_System_of_Units#Units

Sowohl Decisecond als auch Centisecond sind wohldefiniert. Dezisekunde und Zentisekunde sind nur die Angleichungen der englischen bzw. lateinischen Bezeichnungen an die deutsche Sprache; sie sind logischerweise ebenfalls offiziell, wenn auch im Alltag ungebräuchlich.


Übrigens gibt es auch einen internationalen Standard für die Reihenfolge und die Schreibweise von Jahr, Monat und Tag (Teil von ISO 8601):
http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_u ...

Wenn also irgendwo folgendes steht:

2003-04-01

Dann sagt der Standard, dass der zweite Block der Monat und der dritte der Tag ist. Und soweit ich verstanden habe, entspräche folgendes schon nicht mehr dem internationalen Standard:

03-04-01 oder 2003/04/01

Es ist also wichtig, dass erstens der Bindestrich verwendet wird und zweitens, dass das Jahr als vierstellige Zahl angegeben wird.

Für den Zeitstempel im Dateinamen meiner Logfiles werde ich folgende Schreibweise verwenden:

YYYY-MM-DDThh;mm;ss

Beispiel

2009-02-21T12;19;00

Das Semikolon kommt dem Doppelpunkt optisch am nächsten und ist im Gegensatz zum Doppelpunkt in Dateinamen erlaubt. Die Schreibweise hat außerdem den Vorteil, dass die Reihenfolge in der Dateienliste, die ein Dateibrowser anzeigt, der Ordnung nach Datum logisch entspricht, wenn sich der Benutzer die Liste nach Namen ordnen lässt.

Mmh, in Bezug auf die Zeitzone, scheint mir der ISO-Standard 8601 wenig eindeutig zu sein. Wenn ich also eines meiner Logfiles an einen Kumpel in der Ukraine verschicken will, wäre es wohl besser, ich schriebe

YYYY-MM-DDUTC+1hh;mm;ss bzw. YYYY-MM-DDUTC+2hh;mm;ss (Sommerzeit)

Oha, auch dafür gibt es eine internationale Schreibweise, nämlich laut http://de.wikipedia.org/wiki/Koordinierte_Weltzeit

YYYY-MM-DDThh;mm;ss+1;00 bzw. YYYY-MM-DDThh;mm;ss+2;00 (Sommerzeit)
Biber
Biber 21.02.2009 um 17:53:08 Uhr
Goto Top
Moin dotsch,

vielen Dank für Deine Recherche und Ergänzung.
Und soweit ich verstanden habe, entspräche folgendes schon nicht mehr dem internationalen Standard:

03-04-01 oder 2003/04/01
Da würde ich uneingeschränkt zustimmen.
Insbesondere die Datumsdarstellung mit 2-stelligen Jahresangaben ist in der IT-Welt gottseidank kaum noch anzutreffen und eigentlich absolut unverantwortlich.
Erst Recht, seit es nun wirklich jedes Jahr wie den "08.08.08", den "09.09.09" oder den "11.11.11" gibt. Erst in vier Hahren wird es wieder kontextbedingt einfacher.

Deine Variante mit den Semikola als stilisierte Doppelpunkte... hm, okay, solange es für jede/n Leser/in interpretierbar und erkennbar ist...
Wenn ich in meiner Erinnerung krame.... meistens lass ich die Trennzeichen bei hhmmss-Angaben in Logfilenamen ganz weg.
Weill eben da die Reihenfolge ohnehin für jeden klar und nicht diskussionswürdig zu sein scheint - egal ob diesseits oder jenseits des Pazifiks.

Grüße
Biber
dolsch
dolsch 23.02.2009 um 10:15:18 Uhr
Goto Top
Hallo Biber,

Test unter Windows XP Pro SP3

Mal weiterspielen (ändern von iDate von 2 auf 1 und danach auf 0):

>reg add "HKCU\Control Panel\International" /v idate /t REG_SZ /d 1 /f  
>date
Aktuelles Datum: Sa 01-10-2005
Geben Sie das neue Datum ein: (TT-MM-JJ)

>date /t
Sa 01-10-2005
>reg add "HKCU\Control Panel\International" /v idate /t REG_SZ /d 0 /f  
>date /t
Sa 10-01-2005

>date
Aktuelles Datum: Sa 10-01-2005
Geben Sie das neue Datum ein: (MM-TT-JJ)

Ich versuche das nachzuvollziehen. Bei mir ändert sich nur die Reihenfolge von Tag, Monat und Jahr bei der Eingabeaufforderung. An der Reihenfolge im aktuellen Datum ändert sich nur etwas, wenn ich auch den Wert von sShortDate ändere.

Übrigens habe ich nicht getestet, ob die Werte nach einem Windows-Neustart aneinander angepasst werden, wenn man z. B. nur den Wert von iDate ändert. Andererseits ist es meiner Meinung nach praxisfern, dass ein Benutzer die Werte über die Registry anpasst. Worauf ich hinauswill, ist, dass iDate (möglicherweise) nicht mit 100%iger Sicherheit die tatsächliche Reihenfolge bei der Ausgabe des Datums liefert.

(Über ‹Systemsteuerung/Regions- und Sprachoptionen› werden natürlich alle Werte aneinander angepasst, sollten sie wenigstens.)


Ein Fehler in und ein oder zwei Fragen zu GetAllSystemDateTimeInfos_3.bat:

Anstatt

For %%i in (%AllDateTimeVars%) do @if defined %%i set %%i 
muss es, glaube ich,
For %%i in (%AllSystemDateTimeVars%) do @if defined %%i set %%i 
heißen, weil sonst nie eine Ausgabe erfolgt.

In diesem Zusammenhang wüßte ich gerne, wozu das at-Zeichen vor dem if gut ist (weil echo ja schon ausgeschaltet ist). Wahrscheinlich wurde die Frage irgendwo im Forum schon mehr als einmal beantwortet. Ich habe zwar noch nicht das Forum nach einer Antwort durchsucht, bin aber mit Google nicht weitergekommen. Und auch nicht mit der Forum-internen Suche. (Für den Moment gehe ich davon aus, dass das @ lediglich eine Markierung ist, die der Übersicht dienen soll, aber sonst nichts bewirkt, sofern echo bereits ausgeschaltet ist.)

Du hast außerdem geschrieben, der Parameter "/W" sei undokumentiert im Gegensatz zu dem Parameter "/Wait". In GetAllSystemDateTimeInfos_3.bat hast du es trotzdem bei dem undokumentierten Parameter belassen. Absicht oder vergessen?

Editiert

In GetAllSystemDateTimeInfos_3.bat wird das Datum noch mit Hilfe von %date% ermittelt. Das ist in der letzten Version korrigiert:
:: Vers 0.09 11.7.2007:  Windows NT kennt weder %date%  noch %time%.

Lässt sich Code eigentlich als Zitat formatieren?

Editiert 2

Kürzlich habe ich gelesen, dass manche Leute den nativen Befehl Find durch den (Unix ?) Befehl Find ersetzen (Für Windows über cygwin erhältlich, soweit ich mich erinnere). Das Unix-Find scheint sogar recht beliebt bei Windows-Benutzern zu sein. Wäre es nicht gut, in GetAllSystemDateTimeInfos_3.bat den Befehl Find durch Findstr ersetzen? Dann würde es auch bei den Leuten funktionieren, die nicht mehr über den nativen Befehl Find verfügen. Wobei ich nicht weiß, ob es Findstr schon unter (frühen) Windows-NT-Versionen gab. Und kompatibel zu NT soll dein Script ja sein. Übrigens heißen doch auch XP und Vista intern NT. Das Echo
Läuft nur unter WinNT oder höher.Sorry.
ohne Versionsangabe ist also möglicherweise mißverständlich.

Grüße

dolsch
Biber
Biber 25.02.2009 um 08:48:36 Uhr
Goto Top
Moin dotsch,

sorry für die späte Antwort.
Hier meine Rest-Hinweise auf die (noch nicht selbst beantworteten) Fragen und Unsauberkeiten.
In diesem Zusammenhang wüßte ich gerne, wozu das at-Zeichen vor dem if gut ist
Das ist einfach nur überflüssig bzw. Schlampigkeit von mir. Meistens "teste" ich ja nur Teil-Zeilen der hier skizzierten Batche am CMD-Prompt.
Und da habe ich natürlich meistens dieses "Inline-Echo-Off"-Zeichen "@" drin. Und manchmal verbleibt es dann auch redundanterweise im Batch.
... dem undokumentierten Parameter belassen. Absicht oder vergessen?
Vergessen. Bzw. war mir hier bei diesem Thema vollkommen egal. Nicht im Fokus. Unwichtiges Nebengeplänkel.
....müsste .... %AllSystemDateTimeVars% .... heißen
Ist oben korrigiert. (aber nur punktuell, ich habe NICHT die ganzen Pre-Versionen der am Ende des Tuts hergeleiteten Batch bereinigt/geprüft).
Lässt sich Code eigentlich als Zitat formatieren?
Nicht automatisch. Das wäre ein "feature request", den Du im Bereich "Off Topic"->"administrator.de-Feedback" stellen könntest.
Find vs. FindStr...
Jein..... Als ich das Tut geschrieben habe, war (mir) FindStr.exe noch relativ neu. Und ich habe keine Ahnung mehr, ob FindStr.exe schon bei WinNT dabei war oder erst ab Win2000. Das (Redmonder) Find.exe dagegen ist auf jedem Windows-Rechner dabei.... und wer das durch ein migriertes Unix-Find.exe mit anderen Parametern ersetzt... nicht mein Problem.
Übrigens heißen doch auch XP und Vista intern NT. Das Echo "Läuft nur unter WinNT oder höher.Sorry.".... ist ... missverständlich
Auch ein "Jein". Die von M$ höchstselbst damals empfohlene Prüfung auf "isses noch Win9x?" oder "isses was richtiges, also Windows_NT-Architektur?" war:
Prüfe die Existenz der Umgebungsvariablen %OS%. Wenn es die gibt UND da drin "Windows_NT" steht , ist es mit hoher Wahrscheinlichkeit ein Windows NT oder was immer danach kommt.
Wenn es die Variable NICHT gibt, ist es so Gates will ein Win95/98....
Wegen dieses halbseidenen Konstruktes wird eben auch unter WinXP oder Vi$ta am CMD-Prompt angezeigt
>set os
OS=Windows_NT
und die Internetforen sind voll von Hilfe suchenden AnwenderInnen mit gesundem Menschenverstand, die schreiben
"Ich hab da einen Fehler. ich habe einen WinXP-Rechner, bei dem im Environment steht "OS=Windows_NT". Wie kann ich das in der Registry ändern??"

Vielen Dank für Deine Nachfragen... ich freue mich, wenn mein Tutorial ernsthaft und natürlich auch kritisch gelesen wird.

Grüße
Biber
dolsch
dolsch 26.02.2009 um 11:02:22 Uhr
Goto Top
Hallo Biber!

Vielen Dank für Deine Nachfragen... ich freue mich, wenn mein
Tutorial ernsthaft und natürlich auch kritisch gelesen wird.

Wie gesagt, ich bin Anfänger. Dein Tutorial ist schon alleine aus folgenden Gründen interessant für mich.

1. weil ich gelernt habe, dass man Batch-Scripte mit Parametern aufrufen kann und den Benutzer z.B. über den Parameter /? zu einer Batch-internen Hilfe umleiten kann.

2. weil ich einiges über Tokens und Delims gelernt habe.

3. weil ich gelernt habe, wie man Variablen in Schleifen vordefiniert, individuell definiert, resettet u.s.w..

Danke dir ein weiteres Mal für die Beantwortung meiner Fragen.

An alle inkl. Biber

Hier ist die Basis für meinen Zeitstempler:

@echo off

:: With the help of the tutorial
:: 
set "myDate=%date%"  
for /f "tokens=2" %%i in ("%myDate%") do set "myDate=%%i"  

if not exist DateOrder?.txt (
start /wait regedit /e SystemDateTimeInfo.reg "HKEY_CURRENT_USER\Control Panel\International"  
for /f "tokens=1* delims==" %%i in ('type SystemDateTimeInfo.reg ^| findstr /i "iDate"') do set "iDate=%%j"  
del SystemDateTimeInfo.reg
)

if defined iDate echo File represents order in date output. Delete it, if you change the regional settings on this computer. > DateOrder%iDate%.txt

for /f "tokens=1-3 delims=-/." %%a in ("%myDate%") do (  
if exist "DateOrder0.txt" for %%i in ("JJ=%%c" "MM=%%a" "TT=%%b") do set %%i  
if exist "DateOrder1.txt" for %%i in ("JJ=%%c" "MM=%%b" "TT=%%a") do set %%i  
if exist "DateOrder2.txt" for %%i in ("JJ=%%a" "MM=%%b" "TT=%%c") do set %%i  
)

for /f "tokens=1-3 delims=:,." %%a in ("%time%") do for %%i in ("hh=%%a" "min=%%b" "ss=%%c") do set %%i  
set "hh=%hh: =0%"  

echo Log %JJ%-%MM%-%TT% @ %hh%;%min%;%ss%
pause

Ich habe das mit vielen (aber nicht allen) über das Panel "Regions- und Sprachoptionen" verfügbaren Voreinstellungen auf meinem Rechner getestet. Leider hat mir dann jemand, den ich gebeten hatte, das Script zu testen, geschrieben, auf seinem simplified-Chinese-Rechner hätte er den Wochentag (also z. B. Dienstag -- auf Chinesisch) mit im Output. Ich konnte das auf meinem Rechner nicht nachvollziehen, obwohl ich die entsprechenden regionalen Einstellungen nachinstalliert hatte. Jedenfalls wollte ich zur Sicherheit, noch eine Abfrage einbauen, die klärt, ob die Werte von TT, MM und JJ Zahlen sind und falls ja, ob z. B. JJ größer als 1900 ist.

Zu diesem Zweck, habe ich folgendes Hilfsscript geschrieben:

@echo off
set "alpha=beliebig"  
if not defined alpha echo alpha ist nicht definiert.
set /a "beta=1%alpha%" 2>NUL  
if errorlevel 1 (
echo %alpha% ist weder Dezimal- noch Oktalzahl, aber m”glicherweise eine Hexadezimalzahl.
) else (
echo %alpha% ist eine Dezimal- oder Oktalzahl, keinesfalls aber eine Hexadezimalzahl.
)
pause

Das funktioniert ordentlich, allerdings nur solo. Denn als ich es in den Zeitstempler eingebaut habe, habe ich gemerkt, dass der Errorlevel nie 0 ist, wenn ich in der Registry sShortDate auf MM.yyyy setze. Die For-Schleife, in der JJ gesetzt wird, scheint einen Errorlevel ungleich 0 zurückzugeben, der permanent bestehen bleibt. Jetzt frage ich mich, ob es so eine Art Errorlevel-Hierarchie gibt oder aus welchen Gründen sonst, es keine Rolle mehr zu spielen scheint, ob set (zweites Script) erfolgreich abgearbeit wird.

Editiert

Errorlevel 1, weil eine der Variablen (TT, MM oder JJ) in der inneren der verschachtelten for-Schleifen kein Wert zugewiesen wird, da es ja nur noch zwei tokens gibt; einen hatte ich ja aus sShortDate entfernt. Aber ich versteh halt nicht, warum der errorlevel nicht beim nächsten erfolgreich abgearbeiteten set (oder irgend einem anderen erfolgreich abgearbeiteten Befehl, der einen Errorlevel zurückgibt,) wieder automatisch auf 0 gesetzt wird. Ich dachte, so würde errorlevel gehandhabt.

Editiert 2

Offenbar habe ich das Problem mit den Errorlevels nicht, wenn ich den Dateityp von bat zu cmd ändere. Alternativ könnte ich CD>NUL benutzen, um den Errorlevel zu resetten. Alles sehr merkwürdig.

Ein paar Links zu den Unterschieden zwischen Bat und CMD:
http://waynes-world-it.blogspot.com/2008/08/difference-between-bat-and- ...
http://www.msfn.org/board/index.php?showtopic=120599&mode=linearplu ...
http://www.msfn.org/board/CMD-vs-Bat-file-t91459.html&mode=linear

Irgendwo hatte ich außerdem gelesen, dass prompt $T $D etwas unterschiedlich behandelt wird, je nachdem, ob aufgerufen via COMMAND.COM oder CMD.EXE. Bin mir gerade nicht sicher, ob ich das im Zusammenhang mit der Ausgabe des Wochentages gelesen hatte -- in irgend einem Kommentar auf http://www.robvanderwoude.com. Ne, hier habe ich das gelesen:

http://windowsitpro.com/Articles/Index.cfm?ArticleID=9177&DisplayTa ...
»rem Modified for Windows 2003 server
rem The Date command does not display the Day of week anymore in the standard cmd.exe
rem The old command.com still does, so this command shell is used to create the date and time elements.)«



Merkwürdig finde ich auch, wie sich manche von mir verschachtelte if-Abfragen verhalten, besonders dann, wenn sich Sprungbefehle darin befinden, selbst wenn sich die Sprungziele außerhalb der Schachtel befinden.

Ich glaub’, ich sollte mir mal ein Buch über Batch-Scripting kaufen. Schon alleine, um mir die richtige Terminologie anzueignen.


Übrigens beschleunigt der Weg über die Dummy-Datei DateOrder….txt den Zeitstempler. Zweiter Grund ist die Möglichkeit, das ganze auf Rechnern zu verwenden, auf denen die Benutzerrechte keinen Zugriff auf Regedit erlauben.

Editiert 3

Das Problem mit dem resistenten Errorlevel habe ich, glaube ich, gelöst.

Dateityp .cmd!

Habe ich der Übersicht wegen gelöscht, da im Wesentlichen so wie in EDITIERT 4

Dass ich auf die Registry zugreife, gefällt mir nicht. Die finale Version mit dem VBS-Script muss ich erst noch nachvollziehen. Vielmehr werde ich versuchen, sie auf meine Erfordernisse zurückzustutzen. Ich möchte, dass das Script schnell abgearbeitet wird. Den ausgeschriebenen Wochentag oder die Kalenderwoche brauche ich nicht, um den Dateinamen meiner Logfiles zeitzustempeln.

Editiert 4

Es war nicht nötig, Bibers finale Version in allen Einzelheiten nachzuvollziehen, um festzustellen, dass an der Langsamkeit des Scripts nicht Biber bzw. die Komplexität seines Codes schuld ist. Die Einbindung von vbs fand ich überraschend einfach.

Hier meine aktuelle Version. Sie greift nur im Notfall auf vbs zurück, und zwar ausschließlich wegen der Geschwindigkeit. Auch (?) meine (an meine Erfordernisse angepasste) Version bietet keine 100%ige Sicherheit, dass Datum und Zeit tatsächlich im Format JJJJ-MM-TT @ hh;min;ss ausgegeben werden.

Muss als Dateityp CMD abgespeichert werden! Und sollte ab Windows 2000 funktionieren.
@echo off

:: With the help of the tutorial
:: 
set "myDate=%date%"  
for /f "tokens=2" %%i in ("%myDate%") do set "myDate=%%i"  

if not exist DateOrder?.txt (
start /wait regedit /e SystemDateTimeInfo.reg "HKEY_CURRENT_USER\Control Panel\International"  
for /f "tokens=1* delims==" %%i in ('type SystemDateTimeInfo.reg ^| findstr /i "iDate"') do set "iDate=%%j"  
del SystemDateTimeInfo.reg
)

if defined iDate echo File represents order in date output. Delete it, if you change the regional settings on this computer. > DateOrder%iDate%.txt

for /f "tokens=1-3 delims=-/." %%a in ("%myDate%") do (  
if exist "DateOrder0.txt" for %%i in ("YY=%%c" "MM=%%a" "DD=%%b") do set %%i  
if exist "DateOrder1.txt" for %%i in ("YY=%%c" "MM=%%b" "DD=%%a") do set %%i  
if exist "DateOrder2.txt" for %%i in ("YY=%%a" "MM=%%b" "DD=%%c") do set %%i  
)

for %%i in (YY MM DD) do if not defined %%i goto :ViaVBS

set /a "test=1%YY%" 2>NUL  
if errorlevel 1 goto :ViaVBS
set /a "test=1%MM%" 2>NUL  
if errorlevel 1 goto :ViaVBS
set /a "test=1%DD%" 2>NUL  
if errorlevel 1 goto :ViaVBS

if 1000 GTR %YY% goto :ViaVBS
if 12 LSS %MM% goto :ViaVBS
if 31 LSS %DD% goto :ViaVBS

goto :output

:ViaVBS
echo Wscript.Echo Year(Now)^&"-"^&Month(Now)^&"-"^&Day(Now) > ComputeDateVia.vbs  
for /f "tokens=1-3 delims=-" %%a in ('cscript //nologo ComputeDateVia.vbs') do (  
for %%i in ("YY=%%a" "MM=%%b" "DD=%%c") do set %%i  
)
if %MM% LSS 10 set "MM=0%MM%"  
if %DD% LSS 10 set "DD=0%DD%"  
del ComputeDateVia.vbs

:output
for /f "tokens=1-3 delims=:,." %%a in ("%time%") do for %%i in ("hh=%%a" "min=%%b" "ss=%%c") do set %%i  
set "hh=%hh: =0%"  

echo Log %YY%-%MM%-%DD% @ %hh%;%min%;%ss%
pause

Schöne Grüße

dolsch
Noc06
Noc06 19.06.2009 um 15:40:03 Uhr
Goto Top
Hallo Biber,

erstmal herzlichen Dank für Dein sehr ausführliches Tutorial. Ich wurde von einem anderen Nutzer auf dieses hingewiesen, da ich derzeit versuche, ein Backup-Skript mit Wochentag- und KW-Aufteilung zu erstellen.

Nach ein wenig ausprobieren habe ich bemerkt, daß Deine finale Version bei mir auf einem Win2k3 System keine Wochentage (also Mo, Di,...) ausgibt. Kann es sein, daß in den Zeilen 87-93 und 98-104 jeweils cDoW stehen müßte (der Code also casesensitv ist)?

Schönen Gruß
Noc06
Biber
Biber 19.06.2009 um 16:46:59 Uhr
Goto Top
Moin Noc06,

danke für Deine Nachfrage.

Kann es sein, daß ...[..] der Code also casesensitv ist)?

Nein, case-sensitive Variablen unter Windows-Systemen.... die Einführung dieses Features hätte sicherlich höhere Wellen geschlagen.
Wenn es so wäre, dann dürften von x über Media- oder sonstige Sonderposten-Märkte verhökerten M$-Systemen circa x-3 nicht mehr über das Anmeldeskript hinauskommen.
Denn mehr als 3 AnwenderInnen haben sicherlich nicht die konsistente Klein- und Grossschreibung der Umgebungsvariablen verifiziert.

Wird an irgendetwas Banalerem hängen - ...vielleicht ist die Rückgabe oder Auswertung des VBS-Schnipsels (oben im Tut als %vbssnippet% temporär angelegt) marode.

Lass uns da mal ansetzen.
Der temporäre Gib-mal-ein-paar-Datumsdetails-VBS-Code sähe ja als VbsSnippet.vbs so aus:
' ---- vbsSnippet.vbs  
If WScript.arguments.count = 1 Then 
   dDate=WScript.arguments.item(0) 
  Else 
    dDate=Date
End If       
Wscript.Echo "x," & DatePart("ww",dDate,vbSunday,vbFirstFourDays) & "," & _    
DatePart("ww",dDate) & "," & DatePart("w",dDate) & "," & DatePart("y",dDate)  
und würde beim Aufruf vom CMD-Prompt z.b. liefern (Eingabezeile durch ">" kenntlich gemacht):
>cscript Vbssnippet.vbs 10.12.2009
x,49,50,5,344
Mit dieser grottig kryptischen Ausgabezeile kann natürlich niemand etwas anfangen, ...
ausgenommen die FOR/F-Anweisung. die ich im Tut-beispiel dieses Geraffel umspeichern lasse.

Wenn wir das am CMD-Prompt nachkaspern, sieht das so aus:
>for /f "delims=, tokens=2-5" %i in ('cscript Vbssnippet.vbs 10.12.2009') do @echo KW:%i OSKW:%j DoW:%k DoY:%l  
KW:49 OSKW:50 DoW:5 DoY:344
wichtig und von Dir bitte zu Testen ist in dieser Ausgabe natürlich speziell der DoW-Wert "5" für den zu erwartenden Donnerstag.

Denn aus diesem Wert "5" wird ja so etwas wie "Do" oder "Thu" abgeleitet.

Bitte überprüfe das mal eben - wenn dort kein Fehler zu identifizieren ist, lass uns per PN weiterforschen.

Sollte aber ein lösbares Problem sein.

Grüße
Biber
Noc06
Noc06 19.06.2009 um 18:22:23 Uhr
Goto Top
Hallo Biber,

Danke für Deine prompte Antwort!

Da ich, zumindest was das Skripten angeht, lediglich (wenn überhaupt) über rudimentäre Kenntnisse verfüge, hatte ich in der Zwischenzeit und aufgrund von Unwissenheit einfach mal die "w" gegen "W" ausgetauscht und siehe da, es funktioniert - MDoof (in diesem Fall Win2k3 R2 64-bit) läßt grüßen...

Die von Dir angesprochene Vorgehensweise werde ich aber auch nochmal überprüfen und entsprechendes Feedback geben.

Gruß
Noc06


EDIT 1:

So, ich habe gerade mal 2 Tests gemacht:

ad 1: Die von Dir angegebenen Werte bekomme ich ebenfalls zurückgemeldet (sieht bei mir also identisch aus), wenn ich mit Deinem vbs arbeite.
ad 2: Ich habe spaßeshalber in Deinem Originalskript meine Änderungen, also von "cDoW", wieder rückgängig gemacht ("cDow") - der Fehler ließ sich reproduzieren. Alle Werte werden exakt so ausgegeben, wie sie zu erwarten sind - eben bis auf den Klarnamen des Wochentages.

Nachvollziehbar ist das m.E. nicht wirklich, aber was an M$ ist das schon... Mit der Änderung der Groß-/ Kleinschreibung läßt sich das Phänomen jedenfalls beheben. Jetzt muß ich nur noch ein vernünftiges Robocopyskript gebastelt bekommen.

P.S.: (OT) Gibt es eigentlich über die Datumsfunktion auch eine Möglichkeit, den Monatsletzten zu ermitteln?
Biber
Biber 19.06.2009 um 20:36:33 Uhr
Goto Top
Moin Noc06,

na, das ist ja heftig.... ich werde die Redmonder nie verstehen, fürchte ich. *kopfschüttel*

Okay, ich werde (zeitnah) mal die Groß/Kleinschreibung in meinem Beispielschnipsel oben anpassen.

Zu Deiner Zusatzfrage mit dem Monatsletzten... klar geht das.
Is' sich ja irgendetwas logisch Herleitbares und von daher einfach runterzuskripten.
Bzw. wenn Du es schaffst, die logische Berechnung zu formulieren, dann kann es jede/r 11jährige runtertippseln in der Skriptsprache, die gerade am Angesagtesten ist.

Logisch/abstrakt formuliert wäre doch eine der möglichen Berechnungen:
  • nimm das Datum (meinetwegen "10.12.2009" wie im Beispiel)
  • gehe auf den ersten Tag dieses Datums (ist trivial->ist "01." + Monat +Jahr
  • Addiere einen Monat ("01.12.2009" + 1 Monat = "01.01.2010"
  • Ziehe einen Tag ab --> "31.12.2009"

In dem VBSnipsel von vorhin kannst Du ja mal die letzten 2 Zeile ersetzen durch diese:
...
' Monatsletzter:  
Wscript.echo dateAdd ("d", -1, dateAdd("m", 1, "01." & Month(dDate) & "." &year(ddate)))  
und Testen.

>cscript Vbssnippet.vbs 10.12.2009
31.12.09

.....works as designed..

Grüße und danke fürs Mitforschen
Biber
Biber
Biber 20.06.2009 um 01:40:58 Uhr
Goto Top
Moin Noc06,

kleiner und peinlicher Nachtrag der Ordnung halber.
Der RPC (Redmonder PraktikantInnen-Clan) ist in diesem Fall gänzlich unschuldig
Und ich zieh auch meine implizite Behauptung zurück, dass sich die Anzahl der Programmierfehler in M$-Betriebssystemen proportional zur Bit-Breite verhält.

War in diesem Fall ein klarer Schusselfehler von mir... vermutlich bei einer der letzten Aktualisierungen des Sourcecodes reingepfuscht.

Der Blöd-Bug war einfach in der jetzigen Code-Zeile 127, die endet auf:
if defined %%i set %%i|find /i "%%i="
dass dort bis vorhin ein ....find "%%i="... stand. Ohne den Schalter "/i"

Und "find /i" macht nun mal einen Vergleich ohne Berücksichtigung der Klein/Grossschreibung.
Defaultverhalten allerdings -also ohne /i - ist der exakte Vergleich.

Und der geht natürlich bei "Ist denn 'cDow' gleich 'cDoW' ?" in die Grütze.

Oder, um einen forenbekannten Bätcher zu zitieren:
"...works as designed..."

Grüße
Biber
Noc06
Noc06 20.06.2009 um 10:54:04 Uhr
Goto Top
Moin moin,

erstmal Danke für Deinen Tipp bzgl. des Monatsletzten. Stimmt schon, da hätte man eigentlich auch selbst den Geistesblitz haben können...

Bzgl. des fehlenden Schalters: wie gesagt, meine Skriptkenntnisse sind eher rudimentärer Natur, so daß mir ein fehlendes /i im Zweifelsfall auch nach dem x-ten Durchlesen nicht aufgefallen wäre - einfacher hätte ich es mir natürlich machen können, wenn ich das "w" in Deiner Definition einfach klein geschrieben hätte; so waren es halt ein paar mehr "w", die ich groß geschrieben habe. face-wink Aber so lernt man ja auch nie aus...

Gruß und schönes WE
Noc06
NeonZero
NeonZero 26.06.2010 um 10:01:39 Uhr
Goto Top
Hallo Biber. Zu Deinem Datums-Problem von oben, indem Du per %DATE% mal ein Datum mit Wochentag und mal ein Datum ohne erhältst, gibt es eine Lösung. Verwende

    echo %DATE:* =%

Dann hast Du immer ein Datum ohne Wochentag.

Ich hatte nach einer Lösung gesucht, um _per Batch_ den Wochentag eines Datums zu ermitteln. Die Lösung, auf die Du verlinkst und hier als leicht veränderte Kopie darstellst, beinhaltet den folgenden Code (nur ein Ausschnitt):

:: Algorithmus frei nach [http://www.commandline.co.uk/lib/treeview/index.php]
::                                 '-->Klick: Batch Function Library/Date and Time Functions/DateToDOW  
...
set yy=42 & set mm=05 & set dd=23 & set TwoDigitYearMax=70
if 1%yy% LSS 200 if 1%yy% LSS 1%TwoDigitYearMax% (set yy=20%yy%) else (set yy=19%yy%)
...
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
...

Nicht nur, dass der Code seine Grenzen der Wochentagsberechnung nicht offenlegt und auch auf (Denk-)Fehler hin in dieser Form schwer überprüfbar ist, ist er für ein TUT total ungeeignet. Denn er ist für die meisten Leser sicher unverständlich. Das Forum soll doch keine Skriptkiddys heranziehen, die den Code kopieren, aber nicht verstehen, was er tut.

Daher meine Frage: Könntest Du den Code in Deinem TUT bitte derart aufschlüsseln, dass ihn jeder versteht (also erklären, was der Autor dort macht, wie er also auf den Wochentag kommt)? Oder - wen Du keine Zeit dafür hast - soll ich mich da mal dransetzen?

Wenn ich das tun soll, dann bitte unter der Voraussetzung, dass Du den angepassten (entwirrten/erklärenden) Code danach oben in Deinem TUT einfügst, weil er hier unten sicher unterginge, und dann lohnt sich der Aufwand nicht. Was meinst Du?

Bye, nz
NeonZero
NeonZero 27.06.2010 um 08:51:06 Uhr
Goto Top

back-to-topIn Bearbeitung... (fertig)

Ich bekomme den Code nicht mehr aus meinem Kopf. Jetzt muss ich ihn knacken, egal ob der entwirrte Code danach oben im TUT eingefügt wird, oder nicht.

Diesen Beitrag hier nutze ich als Protokoll und zur stückweisen Erklärung des Codes von Ritchie Lawrence. Bis ich damit fertig bin, bleibt der Hinweis "In Bearbeitung" bestehen.

    Quelle des Codes: http://www.commandline.co.uk/lib/treeview/index.php, klick: Batch Function Library/Date and Time Functions/DateToDOW
    Autor: Ritchie Lawrence

Hier eine angepasste Batch, die den Code von Ritchie Lawrence als Batch-Funktion startet, erweitert um die Funktionalität den Wochentag nicht als numerischen Wert sondern als Text auszugeben:
@echo off & setlocal EnableExtensions

::Das Datum "tag.monat.jahr" in seine Bestandteile zerlegen: 
  if "%~1" == "" ( ::kein Parameter angegeben; das aktuelle Datum %DATE% soll verwendet werden...  
    for /f "tokens=1-3 delims=." %%a in ("%DATE:* =%") do set _dd=%%a & set _mm=%%b & set _yy=%%c  
  ) else ( ::das übergebene Datum verwenden...
    for /f "tokens=1-3 delims=." %%a in ("%~1")    do set _dd=%%a & set _mm=%%b & set _yy=%%c  
  )

::Rufen wir den Code von Ritchie Lawrence auf, um den Wochentag des Datums zu berechnen:
  call :DateToDOW %_yy% %_mm% %_dd% _WoTgNr

::Die Wochentagsnummer (1 bis 7) in den Namen des Wochentags umwandeln:
  for /f "tokens=%_WoTgNr%" %%a in ("Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag") do set _Wochentag=%%a  

::Wochentag ausgeben
  echo Der gesuchte Wochentag ist ein: %_Wochentag%

goto :EOF   ::Springe an das Ende der Datei, also verlasse die Batch (weil fertig)

:DateToDOW %yy% %mm% %dd% dow
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: By:   Ritchie Lawrence, 2003-04-29. Version 1.1
:: Func: Creates a day of week number from a calendar date, where 1 = Mon
::       and 7 = Sun. For NT4/2000/XP/2003.
:: Args: %1 year component to be converted, 2 or 4 digits (by val)
::       %2 month component to be converted, leading zero ok (by val)
::       %3 day of month to be converted, leading zero ok (by val)
::       %4 var to receive day of week number, 1 to 7 (by ref)
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  set yy=%1&set mm=%2&set dd=%3
  if 1%yy% LSS 200 if 1%yy% LSS 170 (set yy=20%yy%) else (set yy=19%yy%)
  set /a dd=100%dd%%%100,mm=100%mm%%%100
  set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
  set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
  set %4=%dow%
 exit /b 0
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::  
Aufrufbeispiel:
c:\>MyBatch.cmd 05.05.2005
-------------------------------
  Ausgabe:  "Der gesuchte Wochentag ist ein: Donnerstag"

Stichproben zufolge scheint Ritchie Lawrences Funktion irgendwie zu funktionieren. Nur wie?

Die Beantwortung der Frage ist wichtig, um zu erkennen, wo die Grenzen der Funktion liegen und ob sie womöglich Fehler enthält. Zudem möchte zuminest ich keinen Quellcode verwenden, von dem ich nicht weiß, was er tut. Also dann:

Die erste Codezeile...
    set yy=%1&set mm=%2&set dd=%3
...füttert die Variablen yy (Jahr), mm (Monat) und dd (Tag) mit den Werten der entsprechenden Parameter.

Dann geht es zur nächsten Codezeile:
    if 1%yy% LSS 200 if 1%yy% LSS 170 (set yy=20%yy%) else (set yy=19%yy%)
Wurde das Jahr nur zweistellig (xx) angegeben (if 1xx kleiner als 2xx, was bei einer vierstelligen Zahl, also 1xxxx, nicht erfüllt sein würde), dann macht die Funktion daraus eine vierstellige Zahl. Dabei wird hier die Grenze auf 1970 gesetzt. Gibt jemand also 11 an, dann wird wohl, davon geht die Funktion aus, 2011 gemeint sein. Gibt jemand 70 (oder höher) ein, dann wird wohl 1970 gemeint sein, so die Funktion:
    Mache aus x.x.00 bis x.x.69 ein x.x.2000 bis x.x.2069; mache aus x.x.70 bis x.x.99 ein x.x.1970 bis x.x.1999.
Hier haben wir eine mögliche Fehlerquelle: Was passiert wohl wenn jemand die führende Null wegläßt?: Statt 2001 wird daraus 201. Betrachtet von dem Standpunkt, das dies kein gültiges Datumsformat wäre, ist das allerdings ein Fehler des Anwenders, nicht der Funktion.

Jetzt geht es in die nächste Codezeile
    set /a dd=100%dd%%%100,mm=100%mm%%%100
Er macht beispielsweise aus den Tag "05" die Zahl 10005 und teilt dies dann durch 100. Er verwendet eine besondere Art der Division (% und nicht /), die den Restwert zurück gibt. Eine Restwert-Division xyz % 10 (auch xyz mod 10) liefert z zurück. Eine Restwert-Division xyz % 100 liefert yz zurück (also die letzten beiden Stellen der Zahl). Anmerkung zur in der Funktion verwendeten Schreibweise: Da es sich bei dem %-Zeichen um ein Sonderzeichen handelt, musste dieses Zeichen durch die doppelte Schreibweise entwertet werden (also %% statt %). Also: Er macht aus 05 eine 10005 und will davon die letzten beiden Ziffern haben: 05. In dieser Zeile macht er eigentlich gar nichts, ausser den Effekt zu nutzen, dass set /a die führenden Nullen eliminiert. Ich vermute mal, dass er das nicht wusste, aber die Zeile
    set /a dd"=dd", mm"=mm"
hätte denselben Effekt (und wäre verständlicher).
Nein, damit liege ich falsch. Meine "Lösung" war ein nicht zu Ende gedachter Schnellschuss, der bei den Werten 08 und 09 nicht funktionieren wird, weil diese Werte natürlich als ungültige oktale Werte interpretiert werden würden (siehe die Hilfe zu set; danke an Biber für den Hinweis einen Beitrag drunter). Ritchie Lawrences Code ist in diesem Punkt vollkommen korrekt.

Jetzt nehmen wir uns den ersten Teil seiner nächsten Codezeile vor:
    set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
Das ist nichts weiter als eine mathematische Klausel für
    Wenn mm kleiner oder gleich 2, dann z=1, sonst z=0
Wenn uns der Autor so etwas gänzlich einfaches sagen möchte, warum schreibt er es nicht auch so hin?:
    if mm LEQ 2 ( set "z=1" ) else ( set "z=0" )

Weiter geht's mit dem Rest der zuvor schon betrachteten Codezeile, die im Zusammenhang mit Teilen der darauf folgenden Codezeile betrachtet werden muss
    set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
    set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
Klarer Fall von selbsterklärendem Code. Kommentare im Quelltext werden sowas von überbewertet...
Der Quellcode hat zwar das Potential, als abschreckendes Beispiel in Lehrbücher aufgenommen zu werden, aber Ritchie Lawrences Idee, wie er den Wochentag mit einfachen Batch-Mitteln berechnet, ist echt pfiffig: Er arbeitet offensichtlich mit einem März-bis-Februar-Jahresmodel. Darauf muss man erst einmal kommen. Mit set /a m=mm + 12*z - 3 baut der Autor einen Montasfaktor auf. Da z nur im Jan oder Feb == 1 ist, sonst 0, fällt die Rechnung 12*z für alle anderen Monate weg (sie kürzt sich aus der Rechnung heraus). Das läuft auf das Folgende hinaus:
    set m im März=0 (=Jahresbeginn), Apr=1, Mai=2 … Dez=9, Jan=10, Feb=11 (Jahresende)
Dazu passt die Anweisung set /a dow=(153*m +2)/5. Er muss ja, um den Wochentag zu berechnen, später auf ein bestimmtes Datum zurückgehen, bei dem ihm der Wochentag bekannt ist. Dann braucht er nur noch die Tage von diesem Datum an bis zum gesuchten Zeitpunkt berechen und durch die Wochentage teilen (aber dazu später mehr). Um die verstrichenen Tage zu ermitteln, betrachtet er zunächst das aktuelle Jahr des angegebenen Datums. Das besteht aus vergangenen Monaten (mm) und Monats-Tagen (dd). Hier wendet er sich dem ersten Teil zu und stellt die Frage, wie viele Tage in dem Jahr bereits verstrichen sind, wenn beispielsweise der Monat April angegeben wurde. Die Antwort liefert ihm die obige Formel (set /a dow=(153*m +2)/5), die im Klartext bedeutet:
    set dow im Mär=0, Apr=31, Mai=61, Jun=92, Jul=122, Aug=153, Sep=184, Okt=214, Nov=245, Dez=275, Jan=306, Feb=337
Das nenne ich eine pfiffige Lösung eines nicht trivialen Problems. Hut ab. Warum ist seine Grundidee mit dem März-bis-Februar-Jahresmodel so gut? Das Jahr beginnt in diesem Modell nicht im Januar, sondern im März. Alle Monate haben dann hintereinander weg mal 31 bzw. 30 Tage, wobei nur noch die Sonderstellung Jul/Aug und Dez/Jan mit zweimal hintereinander 31 Tagen beachtet werden muss, was seine mathematische Formel von oben vortrefflich löst. Der schwierige Monat Feb gelangt so an die letzte Stelle mit seinen 28 bzw. 29 Tagen, steht also nicht mehr mittendrin. Es braucht in diesem Fall nicht mehr beachtet zu werden, ob das aktuelle Jahr womöglich ein Schaltjahr ist. Diese Frage wird bereits durch die Angabe des Tages (dd) gelöst, genau dann nämlich, wenn man im Monat Feb des gesuchten Datums den 29. Tag angibt, was in diesem Fall der letzte Tag im Jahr wäre. Anders formuliert: Die Frage nach den verstrichenen Tagen innerhalb des gesuchten Jahres ist jetzt mit dow+dd gelöst (set /a dow=dow + dd).

Aus den Codezeilen eins drüber bleibt noch die Berechnung der verstrichenen Tage aus der angegebenen Jahreszahl übrig. Also die Differenz in Tagen hin zu einem Datum, bei dem uns (oder vielmehr dem Autor) der Wochentag bekannt ist. Aus dieser Differenz + der verstrichenen Tage innerhalb des gesuchten Jahres ließe sich der gesuchte Wochentag berechnen. Wir müssen uns jetzt also der folgenden Berechnung zuwenden:
    set /a y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
    set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
y=yy-z ist klar, denn z ist im Jan/Feb == 1, sonst 0, und in dem zugrunde liegenden März-bis-Februar-Jahresmodel liegen diese beiden Monate nicht am Jahresanfang sondern am Ende des vergangenen Jahres (hier muss also die Jahreszahl um 1 reduziert werden). Aber: Warum nur schickt er uns 4800 Jahre in die Vergangenheit? Soll heißen: In seiner Berechnung sind 4800 Jahre mehr vergangen, als wir im Datum angegeben haben. *grübel*
Na gut, schauen wir uns mal an, was der Code da macht: set /a y"+=4800" - Das rechnerische Jahr 0000 beginnt in diesem Modell also nicht am 1. Januar des Jahres 0000, sondern am 1. März im Jahre -4800. Nun gilt es die verstrichenen Tage bis zum angegebenen Jahr zu berechnen (inklusive der Schaltjahresberechnung, da pro Schaltjahr ein Tag hinzukommt): dow=dow + y+365 /*klar, das Jahr hat 365 Tage*/ + y/4 /*durch vier teilbare Jahre sind Schaltjahre*/ - y/100 /*Jahrhunderjahre wieder abziehen, da sie nicht zwangsläufig Schaltjahre sind*/ + y/400 /*nur Jahrhundertjahre die durch 400 teilbar sind, sind Schaltjahre*/.

Die Variable dow enthält jetzt alle Tage seit dem 1. März im Jahre -4800. Nun verlegt der Autor den Tag 1 auf den 1. März im Jahre -4800 + 2.472.630 Tage. So kommt er auf ein Datum, wo ihm der Wochentag bekannt ist:
    set /a dow=dow/5+dd +y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
Damit wir das später (möglicherweise) herausrechnen können, rechne ich erst einmal, was uns die zusätzlichen 4800 Jahre an Tage eingebrockt haben: 4800 * 365=1.752.000 Tage, + 1.164 Schalttage=1.753.164 Tage gesamt, die durch das obige "y+=4800" hinzugekommen sind. Nun ziehe ich diese von der magischen Nummer des Autors ab: 2.472.630 - 1.753.164=719.466 Tage ab dem rechnerischen 01. März 0000. Dann verbleiben 1969 Jahre (=719.162 Tage inkl. Schalttage) und 304 Tage. Das sind 275 Tage bis Dez 1969 im März-bis-Feb-Jahresmodell, Rest 29 Tage.

    Das gesuchte Datum, auf das diese magische Zahl zurückrechnet, ist der 29.12.1969, ein Montag (vermutlich der Geburtstag des Autors?)

Das weist auf eine Schwachstelle des Codes hin, auf die ich später zu sprechen kommen werde. Zunächst einmal sehen wir uns an, wie dieser Code daraus den gesuchten Wochentag des angegebenen Datums errechnet:
    set /a dow=dow/5+dd+y*365+y/4 -y/100+y/400-2472630,dow%%=7,dow+=1
Die Tage vom 29.12.1969 bis zum angegebenen Datum geteilt durch 7 ergibt einen Rest, deren erste Stelle hinter dem Komma 0=Mo, 1=Di bis 6=So lautet. Diese Restwert-Division (mathematisch dow=dow mod 7; das entspricht dow%%=7, was im Klartext dow=dow %% 7 bedeutet) wird für die Berechnung des gesuchten Wochentages verwendet. Damit das Ergebnis nicht 0 bis 6 lautet, wird das Ergebnis noch um eins erhöht. Fertig.

Da wir nun das magische Ursprungsdatum kennen: Was passiert wohl, wenn wir mit seiner Funktion den Wochentag vom 28.12.1969 oder früher erfragen?

Bevor ich auf Ritchie Lawrences Code weiter eingehe, versuche ich zunächst einmal meine Theorien mit Hilfe einiger quick-and-dirty-Hacks zu überprüfen:
@echo off & setlocal EnableExtensions

call :FncPartDate -f DD/MM/YY -0 _Year _Month _Day %1 &::Das übergebene Datum in seine Bestandteile zerlegen

echo ------------------------------------------------------------------------------
echo #     Wochentagsberechnung stur nach dem Gregorianischen Kalendersystem:     #
echo ------------------------------------------------------------------------------
set /a _WDNr=0
call :DateToDOW %_Year% %_Month% %_Day% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo Der gesuchte Wochentag ist ein: %_WDName%  ^(laut DateToDOW^)

set /a _WDNr=0  
call :FncDateToWeekdayNr_Hack_00 %_Year% %_Month% %_Day% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo Der gesuchte Wochentag ist ein: %_WDName%  ^(laut FncDateToWeekdayNr_Hack_00^)

set /a _WDNr=0  
call :FncDateToWeekdayNr_Hack_01 %_Year% %_Month% %_Day% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo Der gesuchte Wochentag ist ein: %_WDName%  ^(laut FncDateToWeekdayNr_Hack_01^)

set /a _WDNr=0  
call :FncDateToWeekdayNr_Hack_10 %_Year% %_Month% %_Day% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo Der gesuchte Wochentag ist ein: %_WDName%  ^(laut FncDateToWeekdayNr_Hack_10^)

set /a _WDNr=0  
call :FncDateToWeekdayNr_Hack_11 %_Year% %_Month% %_Day% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo Der gesuchte Wochentag ist ein: %_WDName%  ^(laut FncDateToWeekdayNr_Hack_11^)

echo.
echo ------------------------------------------------------------------------------
echo # Berechnung des Julianischen Tages stur nach dem Gregorian. Kalendersystem: #
echo ------------------------------------------------------------------------------
set /a _JDay=0  
call :FncDateToJulianDay_Hack_00 %_Year% %_Month% %_Day% _JDay 
echo Umrechnung Datum nach JulianDay: %_JDay%  ^(laut FncDateToJulianDay_Hack_00^)
set /a _JDay=0  
call :FncDateToJulianDay_Hack_01 %_Year% %_Month% %_Day% _JDay 
echo Umrechnung Datum nach JulianDay: %_JDay%  ^(laut FncDateToJulianDay_Hack_01^)
set /a _JDay=0  
call :FncDateToJulianDay_Hack_10 %_Year% %_Month% %_Day% _JDay 
echo Umrechnung Datum nach JulianDay: %_JDay%  ^(laut FncDateToJulianDay_Hack_10^)
echo.
set /a _WDNr=0
call :FncJulianDayToWeekdayNr %_JDay% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo         Berechneter Wochentag aus JulianDay %_JDay%:       %_WDName%
echo.
echo ------------------------------------------------------------------------------
echo # Julian Day bis 04.10.1582 nach dem Julianischen, dann Gregorian. Kalender: #
echo ------------------------------------------------------------------------------
set /a _JDay=0  
call :FncDateToJulianDay_Hack_11 %_Year% %_Month% %_Day% _JDay 
echo.
echo Umrechnung Datum nach JulianDay: %_JDay%  ^(laut FncDateToJulianDay_Hack_11^)
echo.
set /a _WDNr=0
call :FncJulianDayToWeekdayNr %_JDay% _WDNr
call :FncWeekdayNrToName %_WDNr% _WDName
echo         Berechneter Wochentag aus JulianDay %_JDay%:       %_WDName%

goto :EOF   ::Springe an das Ende der Datei, also verlasse die Batch (weil fertig)

:FncPartDate  {{comment_single_line_remark:2}}
::Options:    -f DD/MM/YY or YY/MM/DD or MM/DD/YY                  /*Input-Date-Format by YY=Year, MM=Month, DD=Day*/
::            -yy <SplitYear>  /*if e.g. SplitYaer==70 then double-digit-year form 00 to 69 make to 20xx, else 19xx*/
::            -0 /* remove leading zeros by returns; on this option the 0xxxx-year can have no more than 5 digits  */
::Parameters: <VarReturnsYear> <VarReturnsMonth> <VarReturnsDay> [date or nothing for current date]
::Example:    call :FncPartDate -f DD/MM/YY -yy 70 -0 _Year _Month _Day "05.05.55"    /*returns _Year=2055, _Day=5 */ 
::            call :FncPartDate -f DD/MM/YY           _Year _Month _Day "05.05.55"    /*returns _Year=55,   _Day=05*/ 
::            call :FncPartDate -f YY/MM/DD -yy 70    _Year _Month _Day "84.05.05"    /*returns _Year=1984, _Day=05*/ 
::            call :FncPartDate -f YY/MM/DD        -0 _Year _Month _Day "84.05.05"    /*returns _Year=84,   _Day=5 */ 
::###################################################################################################################
  setlocal EnableDelayedExpansion
   if "%~1" == "-f"  ( set _#2=%~2 &set "_F1=_!_#2:~0,2!" &set "_F2=_!_#2:~3,2!" &set "_F3=_!_#2:~6,2!" &shift &shift  
   ) else ( set "_F1=_DD" &set "_F2=_MM" &set "_F3=_YY" )  
   if "%~1" == "-yy" ( set "_SpYY=%~2" &shift &shift ) else ( set "_SpYY=" )  
   if "%~1" == "-0"  ( set "_RLZ=on"   &shift        ) else ( set "_RLZ="  )  

   if "%~4" == "" ( set "_MyDate=%DATE:* =%" ) else ( set "_MyDate=%~4" )  
   for /f "tokens=1-3 delims=./ " %%a in ("%_MyDate%") do set "%_F1%=%%a" & set "%_F2%=%%b" & set "%_F3%=%%c"  

   if "%_YY:~0,1%" == "-" (set "_YY=%_YY:~1,100%" &set "_NS=-") else (set "_NS=") &::Vorzeichen entfernen  
   if defined _RLZ ( ::führende Nullen entfernen
     if "%_YY:~0,1%" == "0" set /a _YY"=(100000%_YY%)%%100000"  
     if "%_MM:~0,1%" == "0" set /a _MM"=(100000%_MM%)%%100000"  
     if "%_DD:~0,1%" == "0" set /a _DD"=(100000%_DD%)%%100000"  
   )
   set "_YY=%_NS%%_YY%" &::Vorzeichen wieder einfügen  

   if not defined _NS if defined _SpYY if %_YY% LSS 100 if 1%_YY% LSS 1%_SpYY% (set _YY=20%_YY%) else (set _YY=19%_YY%)
  endlocal & set "%~1=%_YY%" & set "%~2=%_MM%" & set "%~3=%_DD%"  
 exit /b 0

:FncWeekdayNrToName  {{comment_single_line_remark:15}}
::Options:    -s "Monday Tuesday Wednesday Thursday Friday Saturday Sunday unknown" /*use my own weekdayname-string*/ 
::Parameters: <weekday-number form 1(=Monday) to 7(=Sunday)> <VarReturnsWeekdayName>
::Example:    call :FncWeekdayNrToName -s "Mo Tu We Th Fr Sa Su unknown" 5 _Name          /*returns _Name="Fr"     */ 
::            call :FncWeekdayNrToName                                   5 _Name          /*returns _Name="Freitag"*/ 
::###################################################################################################################
  setlocal EnableDelayedExpansion
   if "%~1" == "-s"  ( set "_WNS=%~2" &shift &shift  
   ) else ( set "_WNS=Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag unbekannt" )  
   set /a _WeekdayNumber"=%~1"  
   if %_WeekdayNumber% LSS 1 set /a _WeekdayNumber"=8"  
   if %_WeekdayNumber% GTR 7 set /a _WeekdayNumber"=8"  
   for /f "tokens=%_WeekdayNumber%" %%a in ("%_WNS%") do set _WeekdayName=%%a  
  endlocal & set "%~2=%_WeekdayName%"  
 exit /b 0

:FncDateToWeekdayNr_Hack_00  {{comment_single_line_remark:21}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnWeekdayNr>
::Note:       The date less than 01/01/1970 is invalid
::Example:    call :FncDateToWeekdayNr 5555 05 05 _DayOfWeekNr
::#################### Berechnung des Wochentages vom dd.mm des Jahres 1970 bis 5881579 #######################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
::Algorithmus frei nach http://www.commandline.co.uk/lib/treeview/index.php, Autor: Ritchie Lawrence
  set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3
  if %_MyYear% LSS 1970 exit /b 1                                      &::Berechnung nur bis 29.12.1969 möglich
  ::Mit den vorhandenen Daten frei nach dem Algorithmus von Ritchie Lawrence den Wochentag berechnen:
    if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 ) &::Jan or Feb = 1, sonst 0
    set /a _MonthsFactor"=(12*_bJanOrFeb)+(_MyMonth-3)"                &::im Mär=0, Apr=1 ... Feb=11  
    set /a _ElapsedDaysUntilMyMonths"=(153*_MonthsFactor+2)/5"         &::im Mär=0, Apr=31 ... Feb=337  
    set /a _ElapsedDaysOfMyYear"=_ElapsedDaysUntilMyMonths+_MyDay"     &::+ verstrichene Tage des akt. Monats  
    set /a _MyYear"-=_bJanOrFeb"                                       &::Jan und Feb liegen hier im verg. Jahr  
    set /a _ElapsedDaysUntilMyYear"=_MyYear*365 + (_MyYear/4 - _MyYear/100 + _MyYear/400)"  
    set /a _Weekday"=(_ElapsedDaysUntilMyYear+_ElapsedDaysOfMyYear - 719466)%%7 + 1"  
  set %4=%_Weekday%
 exit /b 0

:FncDateToWeekdayNr_Hack_01 {{comment_single_line_remark:35}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnWeekdayNr>
::Example:    call :FncDateToWeekdayNr 5555 05 05 _DayOfWeekNr
::################ Berechnung des Wochentages vom dd.mm des Jahres -2147483648 bis 5879609 ####################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
  set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3
  
  if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
    echo FncDateToWeekdayNr_Hack01: Error^^!
    echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
    echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
    exit /b 1
  )  1>&2
  if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  
  :FncDateToWeekdayNr_Hack01_YearLoop              &::negative Jahreszahl in eine gültige positive Zahl umwandeln...
    if %_MyYear% LSS 1 set /a _MyYear"+=4000000"   &::...alle 400 J. wiederh. sich die WoTage (*10000 ist schneller)  
   if %_MyYear% LSS 1 goto :FncDateToWeekdayNr_Hack01_YearLoop &::LSS 1 (statt 0) weil Jahr im JanOrFeb sonst -1
   
  ::Im Folgenden gilt es die bereits verstrichenen Tage des Jahres im März-bis-Februar-Jahresmodell zu berechnen.
    if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 ) &::Jan or Feb = 1, sonst 0
    ::Algorithmus frei nach http://www.commandline.co.uk/lib/treeview/index.php, Autor: Ritchie Lawrence
    set /a _MonthsFactor"=(12*_bJanOrFeb)+(_MyMonth-3)"                &::im Mär=0, Apr=1 ... Feb=11  
    set /a _ElapsedDaysUntilMyMonths"=(153*_MonthsFactor+2)/5"         &::im Mär=0, Apr=31 ... Feb=337  
    set /a _ElapsedDaysOfMyYear"=_ElapsedDaysUntilMyMonths+_MyDay"     &::+ verstrichene Tage des akt. Monats  
  ::Nun gilt es die Tage der vergangenen Jahre bis zum rechnerischen Jahr 0000 zurückzuberechenen.
    set /a _MyYear"-=_bJanOrFeb"                                       &::Jan und Feb liegen hier im verg. Jahr  
    set /a _ElapsedDaysUntilMyYear"=_MyYear*365 + (_MyYear/4 - _MyYear/100 + _MyYear/400)"  
  ::Wochentag=(verstrichene Tage + 1 Tag /*damit der Ursprungstag der Berechnung ein Montag ist*/) mod 7 + 1
    set /a _Weekday"=(_ElapsedDaysUntilMyYear+_ElapsedDaysOfMyYear + 1)%%7 + 1"  
  set %4=%_Weekday%
 exit /b 0

:FncDateToWeekdayNr_Hack_10    {{comment_single_line_remark:54}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnWeekdayNr>
::Example:    call :FncDateToWeekdayNr 5555 05 05 _DayOfWeekNr
::############## Berechnung des Wochentages vom dd.mm des Jahres -2147483648 bis 2147483641 ###################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
::Algorithmus frei nach http://de.wikipedia.org/w/index.php?title=Wochentagsberechnung&oldid=75828222
  set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3

  if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
    echo FncDateToWeekdayNr_Hack10: Error^^!
    echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
    echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
    exit /b 1
  )  1>&2
  if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  
  :FncDateToWeekdayNr_Hack10_YearLoop              &::negative Jahreszahl in eine gültige positive Zahl umwandeln...
    if %_MyYear% LSS 1 set /a _MyYear"+=4000000"   &::...alle 400 J. wiederh. sich die WoTage (*10000 ist schneller)  
   if %_MyYear% LSS 1 goto :FncDateToWeekdayNr_Hack10_YearLoop &::LSS 1 (statt 0) weil Jahr im JanOrFeb sonst -1

  set /a _MyDayID"=_MyDay%%7"  &::Tageskennziffer ermitteln  
  for /f "tokens=%_MyMonth%" %%a in ("0 3 3 6 1 4 6 2 5 0 3 5") do set _MyMonthID=%%a  
  
  if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 )
  set /a _MyYear"-=_bJanOrFeb"           &::Im Jan or Feb Schaltjahr nur bis zum verganenen Jahr berechnen  
  set /a _MyLeapYearID"=(_MyYear/4 - _MyYear/100 + _MyYear/400)%%7"    &::Schaltjahreskennziffer ermitteln  

  set /a _Weekday"=(_MyDayID + _MyMonthID + _MyYear+_bJanOrFeb + _MyLeapYearID -2)%%7 + 1" &::Wochentag berechnen  
  set %4=%_Weekday%
 exit /b 0

:FncDateToWeekdayNr_Hack_11    {{comment_single_line_remark:69}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnWeekdayNr>
::Example:    call :FncDateToWeekdayNr 5555 05 05 _DayOfWeekNr
::############## Berechnung des Wochentages vom dd.mm des Jahres -2147483648 bis 1728356768 ###################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
  set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3

  if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
    echo FncDateToWeekdayNr_Hack10: Error^^!
    echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
    echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
    exit /b 1
  )  1>&2
  if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  
  :FncDateToWeekdayNr_Hack11_YearLoop              &::negative Jahreszahl in eine gültige positive Zahl umwandeln...
    if %_MyYear% LSS 1 set /a _MyYear"+=4000000"   &::...alle 400 J. wiederh. sich die WoTage (*10000 ist schneller)  
   if %_MyYear% LSS 1 goto :FncDateToWeekdayNr_Hack11_YearLoop &::LSS 1 (statt 0) weil Jahr im JanOrFeb sonst -1

  ::Verstrichene Monatstage des Jahres mit einem Algorithmus eines März-bis-Februar-Jahresmodells berechnen...
    if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 ) &::Jan or Feb = 1, sonst 0
    ::Algorithmus frei nach http://www.commandline.co.uk/lib/treeview/index.php, Autor: Ritchie Lawrence
    set /a _MonthsFactor"=(12*_bJanOrFeb)+(_MyMonth-3)"                &::im Mär=0, Apr=1  ... Jan=10,  Feb=11  
    set /a _ElapsedDaysUntilMyMonths"=(153*_MonthsFactor+2)/5"         &::im Mär=0, Apr=31 ... Jan=306, Feb=337  
  
  set /a _ElapsedDaysUntilMyMonths"+=59 - 365*_bJanOrFeb"   &::zurückrechnen in das gängige Jahresmodell  
  :: ab März+=59, die Tage Jan+Feb<--'  '-->nur im Jan or Feb: - (59 + 306_MärzBisJanTage_des_März-bis-Feb-Modells) 
  
  set /a _MyYear"-=_bJanOrFeb"                                  &::im Jan/Feb Schaltjahr nur bis Jahr-1 berechnen  
  set /a _MyLeapYears"=(_MyYear/4 - _MyYear/100 + _MyYear/400)" &::Schaltjahre ermitteln  
  
  ::Das rechnerische Jahr 0 beginnt an einem Samstag. Jedes darauf folgende Jahr erhöht den Tag des Jahresbeginns
  ::um 1 Tag. Jedes Schaltjahr erhöht ihn um 1 weiteren Tag. Daher: (Tage+Monatstage + Jahre + Schaltjahre) mod 7
  ::ergibt 0=Sa, 1=So, 2=Mo, etc. Dank -2 steht die 0 für Mo, 1=Di, etc. +1 macht aus 0bis6 ein 1bis7. Fertig.
    set /a _Weekday"=(_MyDay+_ElapsedDaysUntilMyMonths + _MyYear+_bJanOrFeb + _MyLeapYears -2)%%7 + 1"  
  set %4=%_Weekday%
 exit /b 0
 
:FncJulianDayToWeekdayNr
::Parameters: <JulianDayNr> <VarNameToReturnWeekdayNr>
::#############################################################################################################
  setlocal EnableDelayedExpansion
   set _JD=%~1
   set /a _WDNr"=_JD%%7+1"  
   if %_WDNr% LSS 1 set /a _WDNr"+=7" &::für Tage vor dem ersten JulianDay  
  endlocal & set "%~2=%_WDNr%"  
 exit /b 0

:FncDateToJulianDay_Hack_00    {{comment_single_line_remark:94}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnJulianDayNr>
::Example:    call :FncDateToJulianDay 5555 05 05 _JulianDay
::#############################################################################################################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
::Algorithmus frei nach http://docs.kde.org/stable/de/kdeedu/kstars/ai-julianday.html; hier umgesetzt auf batch
  set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3

  if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
    echo FncDateToJulianDay_Hack00: Error^^!
    echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
    echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
    exit /b 1
  )  1>&2
  if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  

  ::das Datum auf den Julianischen Tag umrechnen:
    set /a _JD"=_MyDay-32075 + 1461*(_MyYear+4800 + (_MyMonth-14)/12)/4"  
    set /a _JD"+=367*(_MyMonth-2 - (_MyMonth-14)/12*12)/12"  
    set /a _JD"-=3*((_MyYear + 4900 + (_MyMonth - 14)/12)/100)/4"  
  set /a %~4"=%_JD%" &::Hinweis: JulianDay 0 == 1. Jan. 4713 v.u.Z. julianisch == 24. Nov. 4714 v.u.Z. greogorianisch  
 exit /b 0

:FncDateToJulianDay_Hack_01  {{comment_single_line_remark:94}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnJulianDayNr>
::Example:    call :FncDateToJulianDay 5555 05 05 _JulianDay
::#############################################################################################################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
::Algorithmus frei nach http://www.commandline.co.uk/lib/treeview/index.php, Autor: Ritchie Lawrence
  set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3

  if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
    echo FncDateToJulianDay_Hack01: Error^^!
    echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
    echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
    exit /b 1
  )  1>&2
  if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  

  ::Im Folgenden gilt es die bereits verstrichenen Tage des Jahres im März-bis-Februar-Jahresmodell zu berechnen.
    if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 ) &::Jan or Feb = 1, sonst 0
    set /a _MonthsFactor"=(12*_bJanOrFeb)+(_MyMonth-3)"                &::im Mär=0, Apr=1 ... Feb=11  
    set /a _ElapsedDaysUntilMyMonths"=(153*_MonthsFactor+2)/5"         &::im Mär=0, Apr=31 ... Feb=337  
    set /a _ElapsedDaysOfMyYear"=_ElapsedDaysUntilMyMonths+_MyDay"     &::+ verstrichene Tage des akt. Monats  
  ::Nun gilt es die Tage der vergangenen Jahre bis zum Jahr 0000 zurückzuberechenen.
    set /a _MyYear"-=_bJanOrFeb"                                       &::Jan und Feb liegen hier im verg. Jahr  
    if %_MyYear% LSS 0 set /a _ElapsedDaysOfMyYear"-=1"  
    set /a _ElapsedDaysUntilMyYear"=_MyYear*365 + (_MyYear/4 - _MyYear/100 + _MyYear/400)"  
  set /a _JD"=(_ElapsedDaysUntilMyYear+_ElapsedDaysOfMyYear+1721119)" &::+Tage vom 01. März 0000 bis JulianDay 0  
  set /a %~4"=%_JD%" &::Hinweis: JulianDay 0 == 1. Jan. 4713 v.u.Z. julianisch == 24. Nov. 4714 v.u.Z. greogorianisch  
 exit /b 0

:FncDateToJulianDay_Hack_10  {{comment_single_line_remark:121}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnJulianDayNr>
::Example:    call :FncDateToJulianDay 5555 05 05 _JulianDay
::#############################################################################################################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
::Algorithmus frei nach http://de.wikipedia.org/w/index.php?title=Julianisches_Datum&oldid=76256118#Berechnung_aus_dem_Kalenderdatum
  setlocal EnableDelayedExpansion
   set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3

   if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
     echo FncDateToJulianDay_Hack01: Error^^!
     echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
     echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
     exit /b 1
   )  1>&2
   if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  

   if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 )
   set /a _Y"=_MyYear-_bJanOrFeb"  
   set /a _M"=_MyMonth + (12*_bJanOrFeb) + 1"  
   set /a _JD"=365*(_Y+4716) + (_Y+4716)/4 + 30*_M + 6001*_M/10000 - 1524 + _MyDay + 2 - _Y/100 + _Y/400"  
  endlocal &set "%~4=%_JD%" &::Hinweis: JulianDay 0 == 1. Jan. 4713 v.u.Z. julianisch == 24. Nov. 4714 v.u.Z. greogorianisch  
 exit /b 0

:FncDateToJulianDay_Hack_11  {{comment_single_line_remark:130}}
::Parameters: <Year> <Month> <Day> <VarNameToReturnJulianDayNr>
::Example:    call :FncDateToJulianDay 5555 05 05 _JulianDay
::#############################################################################################################
::Nicht fuer den produktiven Einsatz gedacht und geeignet; nur zum Testen.
::Algorithmus frei nach http://de.wikipedia.org/w/index.php?title=Julianisches_Datum&oldid=76256118#Berechnung_aus_dem_Kalenderdatum
  setlocal EnableDelayedExpansion
   set _MyYear=%1&set _MyMonth=%2&set _MyDay=%3

   if %_MyYear% EQU 0 ( ::Jahr 0000 wurde angegeben; ungültige Jahreszahl...
     echo FncDateToJulianDay_Hack01: Error^^!
     echo On 31.12.0001 before Christ ^(BC^) immediately follows the 01.01.0001 AD ^(CE^).
     echo The year 0000 does not exist. Use -0001 do you mean the year 1 before Christ.
     exit /b 1
   )  1>&2
   if %_MyYear% LSS 0 set /a _MyYear"+=1"           &::Das Jahr -1 liegt rechnerisch im Jahr 0, -2 im Jahr -1, etc.  

   if %_MyMonth% LEQ 2 ( set _bJanOrFeb=1 ) else ( set _bJanOrFeb=0 )
   set /a _Y"=_MyYear-_bJanOrFeb"  
   set /a _M"=_MyMonth + (12*_bJanOrFeb) + 1"  
  
   set /a        _MyCalendar"=_MyYear*100 + _MyMonth*10 + _MyDay"  
   set /a _GregorianCalendar"=   1582*100 +       10*10 +     15"  
   set /a    _JulianCalendar"=   1582*100 +       10*10 +      4"  
   if %_MyCalendar% GEQ %_GregorianCalendar% ( ::ab 15.10.1582 Schaltjahresberechnung nach dem Gregorian. Kal.
     echo         Berechnung nach dem Gregorianischen Kalender...
     set /a _JD"=365*(_Y+4716) + (_Y+4716)/4 + 30*_M + 6001*_M/10000 - 1524 + _MyDay + 2 - _Y/100 + _Y/400"  
   )
   if %_MyCalendar% LSS %_GregorianCalendar% if %_MyCalendar% GTR %_JulianCalendar% (
     echo         FncDateToJulianDay_Hack_11: Error^^!
     echo         Historically the days between 05-14 in October 1582 does not exist.
     exit /b 1
   ) 1>&2
   if %_MyCalendar% LEQ %_JulianCalendar% ( ::bis 04.10.1582 Schaltjahresberechnung nach dem Julian. Kalender
     echo         Berechnung nach dem Julianischen Kalender...
     set /a _JD"=365*(_Y+4716) + (_Y+4716)/4 + 30*_M + 6001*_M/10000 + _MyDay - 1524"  
   )
  endlocal &set "%~4=%_JD%"  
 exit /b 0
 
:DateToDOW %yy% %mm% %dd% dow
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: By:   Ritchie Lawrence, 2003-04-29. Version 1.1
:: Func: Creates a day of week number from a calendar date, where 1 = Mon
::       and 7 = Sun. For NT4/2000/XP/2003.
:: Args: %1 year component to be converted, 2 or 4 digits (by val)
::       %2 month component to be converted, leading zero ok (by val)
::       %3 day of month to be converted, leading zero ok (by val)
::       %4 var to receive day of week number, 1 to 7 (by ref)
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  set yy=%1&set mm=%2&set dd=%3
  ::if 1%yy% LSS 200 if 1%yy% LSS 170 (set yy=20%yy%) else (set yy=19%yy%)
  set /a dd=100%dd%%%100,mm=100%mm%%%100
  set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
  set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
  set %4=%dow%
 exit /b 0
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::   
Aufrufbeispiel:
c:\>MyBatch.cmd 05.05.5555
-------------------------------
  Ausgabe:
------------------------------------------------------------------------------
#     Wochentagsberechnung stur nach dem Gregorianischen Kalendersystem:     #
------------------------------------------------------------------------------
Der gesuchte Wochentag ist ein: Donnerstag  (laut DateToDOW)
Der gesuchte Wochentag ist ein: Donnerstag  (laut FncDateToWeekdayNr_Hack_00)
Der gesuchte Wochentag ist ein: Donnerstag  (laut FncDateToWeekdayNr_Hack_01)
Der gesuchte Wochentag ist ein: Donnerstag  (laut FncDateToWeekdayNr_Hack_10)
Der gesuchte Wochentag ist ein: Donnerstag  (laut FncDateToWeekdayNr_Hack_11)

------------------------------------------------------------------------------
# Berechnung des Julianischen Tages stur nach dem Gregorian. Kalendersystem: #
------------------------------------------------------------------------------
Umrechnung Datum nach JulianDay: 3750106  (laut FncDateToJulianDay_Hack_00)
Umrechnung Datum nach JulianDay: 3750106  (laut FncDateToJulianDay_Hack_01)
Umrechnung Datum nach JulianDay: 3750106  (laut FncDateToJulianDay_Hack_10)

        Berechneter Wochentag aus JulianDay 3750106:       Donnerstag

------------------------------------------------------------------------------
# Julian Day bis 04.10.1582 nach dem Julianischen, dann Gregorian. Kalender: #
------------------------------------------------------------------------------
        Berechnung nach dem Gregorianischen Kalender...

Umrechnung Datum nach JulianDay: 3750106  (laut FncDateToJulianDay_Hack_11)

        Berechneter Wochentag aus JulianDay 3750106:       Donnerstag
Update: Ich habe die nervige Codezeile mit der Umrechnung der zweistelligen Jahreszahl hin zur vierstelligen Zahl aus dem Originalcode herausgenommen und in die Funktion FncPartDate gepackt. Sie ist nicht mehr standardmäßig aktiv, sondern muss nun in Zeile 3 der Batch über den Funktionsparameter -yy <Umschaltjahr> explizit angegeben werden (wer es denn braucht). Das Datum yy weist jetzt also auf das Jahr 00yy, während eine Anpassung der dritten Zeile der Batch hin zu call :FncPartDate -f DD/MM/YY -yy 70 _Year _Month _Day %1 das Jahr yy - wie von Lawrences Funktion gewohnt - nach 19yy umrechnet, wenn yy >= 70, sonst 20yy.
Damit bin ich zwar nicht mehr 100% kompatibel zur ursprünglichen Schnittstelle von Lawrences Funktion, aber in einem quick-and-dirty-Hack darf ich das.

Der Unterschied zwischen den Funktionen:
  1. DateToDOW
    • Originalalgorithmus von Ritchie Lawrence
  2. FncDateToWeekdayNr_Hack_00
    • kommt ohne den y+=4800'er Umweg aus dem Originalalgorithmus aus
  3. FncDateToWeekdayNr_Hack_01
    • kommt zudem ohne die Datumsgrenze 29.12.1969 aus; die Funktion verwendet einen anderen Ansatz bei der Rückrechnung, bei dem nun die Eigenschaft des Gregorianischem Kalenders genutzt wird, dass der 1. Januar eines jeden geraden 10-Jahrtausendjahres (x.x.2000, x.x.4000, etc.) auf einen Samstag fällt (die ungeraden fallen auf einen Mittwoch). Im Klartext: Es können Wochentage ab dem 01.01.0000 damit ermittelt werden. Aber Achtung!: Der Algorithmus rechnet stur nach unserer Zeitrechnung in die Vergangenheit und Zukunft. Historisch gesehen gilt der Gregorianische Kalender aber erst seit dem 15. Oktober 1582. Für sämtliche historische Kalendersysteme gilt zudem der Umstand, dass es kein Jahr 0000 gibt (auf den 31.12.0001 vor unserer Zeitrechnung folgt der 01.01.0001 unserer Zeitrechnung). Der verwendete Algorithmus ignoriert diese Feinheiten bewusst und liefert keine historischen Daten, sondern beantwortet die Frage, welchen Wochentag man erhält, wenn man nach unserer Zeitrechnung einfach immer weiter in die Vergangenheit rechnet. Das Ergebnis einer solchen Funktion ist für Daten vor dem 15.10.1852 also als technisches Umrechnungsergebnis zu betrachten.
    • Update: Ein Teil der Feinheiten wird jetzt doch beachtet. Gibt jemand das Jahr 0 an, wird er darauf hingewiesen, dass es das Jahr 0 nicht gibt. Dafür kann der Algorithmus nun auch ein technisches Umrechnungsergebnis von den Jahren vor unserer Zeitrechnung berechnen (z.B. 05.05. -555).
    • Der Wertebereich der Jahre liegt zwischen -2.147.483.648 bis 5.879.609 (bedingt durch den 32-Bit-Speicherbereich der Variablen und der Art, wie der Algorithmus ihn nutzt).
  4. FncDateToWeekdayNr_Hack_10
    • wie zuvor, jedoch mit einem gänzlich anderen Algorithmus, frei nach Wikipedia (Wochentagsberechnung), umgesetzt auf batch
    • Update: Deren Schaltjahresproblem habe ich jetzt anders gelöst. Das Jahr wird nun einfach nur bis zum Vorjahr auf Schaltjahre hin untersucht, wenn der Monat==Jan or Feb ist.
    • Wertebereich der Jahre: -2.147.483.648 bis 2.147.483.641
  5. FncDateToWeekdayNr_Hack_11
    • Ein eigener Weg. Die Idee ist aus den anderen Ansätzen heraus entstanden
    • Wertebereich der Jahre: -2.147.483.648 bis 1.728.356.768
  6. FncDateToJulianDay_Hack_00
    • Das wird die Astronomen unter euch freuen: Eine Batch-Funktion, die das Datum umrechnet auf den fortlaufenden Julianischen Tag (auch Julianisches Datum; nicht zu verwechseln mit dem Julianischen Kalender), der sich (das passt zu unserem Projekt) leicht in den Wochentag umrechnen läßt (siehe FncJulianDayToWeekdayNr). Frei nach einem Algorithmus von kde.org, aus dem "AstroInfo Projekt", hier umgesetzt auf Batch. Die Seite weist darauf hin, dass ihre Formel nur für Daten zwischen den Jahren 1801 und 2099 funktioniert, aber ich konnte - zumindest in der Batch-Variante - keine Fehler erkennen. Bei den Stichproben lag die Funktion auch weit vor und nach dieser Zeitgrenze absolut richtig (gemessen an dem technischen Umrechnungsergebnis des Gregorianischen Kalenders; will damit nochmals unterstreichen, dass das Ergebnis vor dem 15.10.1582 historisch gesehen nicht mehr korrekt ist). Nur bei negativen Jahreswerten hat sie mitunter Probleme.
  7. FncDateToJulianDay_Hack_01
    • wie zuvor, nur mit einer abgewandelten Form des Algorithmus von Ritchie Lawrence
  8. FncDateToJulianDay_Hack_10
  9. FncDateToJulianDay_Hack_11
    • Was bedeutet das genau, wenn ich schreibe, dass die bisherigen Funktionen keine historischen Daten liefern?: Der Gregorianische Kalender hat damals eine Zeitdifferenz von 10 Tagen korrigiert (auf den 4. Oktober 1582 - julianisch, Donnerstag - folgte unmittelbar der 15. Oktober 1582 - gregorianisch, Freitag), was bedeutet, dass der 14.10.1582 gregorianisch (Do) rechnerisch auf den 04.10.1582 julianisch (Do) fällt, der 13.10 g. (Mi) fällt auf den 03.10 j. (Mi), der 12.10 g. (Di) fällt auf den 02.10 j. (Di) … der 04.10.1582 gregorianisch (Mo) fällt rechnerisch auf den 24.09.1582 julianisch (Mo)...
    • Das Problem: Der 04.10.1582 gregorianisch ist ein Montag, während der 04.10.1582 julianisch ein Donnerstag ist. Die Antwort nach dem Wochentag vom historischen Datum 04.10.1582 und davor ist laut technischem Umrechnungsergebnis also falsch. Zudem ändert sich die Differenz mit den Jahren, weil die Schaltjahresberechnung im Julianischen Kalendersystem eine andere ist, als im Gregorianischen Kalendersystem. Die Differenz hat natürlich nicht nur eine Auswirkung auf den Wochentag. Der Tag 0 des Julianischen Tages zeigt nach dem gregorianischen System beispielsweise nicht auf den 1.1.4713 v.u.Z., sondern auf das technische Umrechnungsergebnis 24. Nov. 4714 v.u.Z.
    • Man kann, wenn man will, ja auf diese Weise in die Vergangenheit rechnen, aber bezogen auf den Julianischen Tag ist das unüblich und das Ergebnis somit falsch. Hier ist es üblich, bis zum 04. Oktober 1582 nach dem julianischen System zu rechnen und ausgehend von diesen Daten ab dem 15. Oktober 1582 nach dem gregorianischen System fortzufahren (Schaltjahresberechnung, etc.). Die Tage 05. bis 14. Oktober 1582 existieren dort nicht. Diese Funktion hier berücksichtigt die Besonderheiten und berechnet den korrekten Julianischen Tag auch für ein Datum, dass vor dem 04. Oktober 1582 liegt.
  10. FncJulianDayToWeekdayNr
    • Umrechnungsfunktion die den Wochentag zum Julianischen Tag ausgibt, und zwar auch dann, wenn man den Julianischen Tag 0 unterschreitet (z.B. -42).

Thesen zur Jahr+=4800’er Operation:
Da Lawrence keine Quellen im Code angegeben hat, gehe ich davon aus, dass es seine Algorithmen sind. Hier stellt sich die Frage, was er mit der Jahr+=4800’er Operation bewirken wollte: Alle 400 Jahre wiederholt sich die Abfolge des Wochentags im Jahr. 400 x 12 = 4800 Monate. Doch er hat die 4800 zum Jahr dazu addiert, nicht zum Monat. Das kann also nicht der Grund seiner Handlung sein. Die 4800 kann andererseits unter bestimmten Umständen auch eine Rolle bei der Berechnung des Julianischen Datums (Julian Day) spielen. Siehe beispielsweise hier. Nur passt der Rest seiner Berechnung nicht dazu. Auch läßt sich die 4800-Berechnung problemlos aus seinem Algorithmus herauskürzen. Könnte es sein, dass er hier etwas durcheinander gebracht hat? Schwer vorstellbar; wer in der Lage ist, solche Algorithmen zu entwickeln (siehe sein Jahresmodell und die Monatstageberechung), der weiß, was er tut. Bleibt noch die Möglichkeit, dass er die 4800 hinzugezogen hat, um den Algorithmus konfuser zu gestalten und somit den Weg, wie er auf den Wochentag kommt, zu verschleiern. Das passt auch beispielsweise zur Berechnung von z und dem allgemeinen Aufbau (oder besser der irritierenden Zerstückelung) seiner Rechenwege.

Fazit zu Lawrences Algorithmus:
Quellcode ist Prosa, manchmal sogar Lyrik, die uns eine mal mehr und mal weniger spannende Geschichte erzählt. Lawrences Geschichte hätte das Zeug dazu, richtig spannend zu werden. Ein cooler Hack als einfache und elegante Lösung eines nichttrivialen Problems. Doch was macht Lawrence stattdessen? Er zerschneidet den roten Faden seiner Geschichte, wo er nur kann. Sie wird nicht nur unattraktiv zu lesen, sie enthält auch für den Anwender wichtige Grenzen, die der Autor vor ihm verschweigt; schlimmer noch setzt er alles daran, um diese Grenzen seines Codes zu verschleiern.

Einmal abgesehen davon, dass sein Quellcode an entscheidenden Stellen undokumentiert ist, wurde er so verfasst, dass zusammenhängende Operationen verstreut hinterlegt wurden. Spätestens hier fragt man sich frei nach fefe, was der Programmierer eigentlich beruflich macht. Denn selbst wenn Lawrence ein Freund von kurzem Code ist, hätte er ihn wenigstens lesbar formatieren können. Ein Beispiel soll verdeutlichen, was ich meine:
Original-Code:
  set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,dow=153*m+2
  set /a dow=dow/5+dd+y*365+y/4-y/100+y/400-2472630,dow%%=7,dow+=1
Dieser Code nur anders formatiert:
  set /a z=14-mm, z/=12, m"=(12*z) + (mm-3)", dow"=(153*m + 2)/5"  
  set /a y=yy-z + 4800, dow"=(dow + dd + y*365 + (y/4 - y/100 + y/400) -2472630)%%7 + 1"  
So sind beispielsweise die Klammern bei m=(…) rein rechnerisch zwar vollkommen überflüssig, sie dienen aber der besseren Lesbarkeit des Codes. Dagegen unterstreichen die extra kompliziert formulierte „z=14-mm,z/=12“-Klausel sowie die überflüssige y+=4800 Operation nebst der Zerstückelung des Codes meine These, dass es Lawrences Absicht war, möglichst kompliziert aussehenden Code zu produzieren.

Möglich dass er damit Coolness demonstrieren wollte, aber der Preis war hoch und das Ziel verfehlt. Denn aus solch einem Code kann niemand etwas lernen, weil ihn (das ist ja von dem Autor so beabsichtigt) schlicht niemand versteht. So gut sein Lösungsansatz auch ist (davor ziehe ich meinen Hut), der Code ist grottenschlecht. Der darin enthaltene 1969'er-Fehler rührt _vermutlich_ genau daher, dass er den Code extra unverständlich geschrieben hat. So hat er wohl in der Version 1.1 (?) unter den Argumenten der Funktion angegeben, dass man das Jahr nicht mehr nur zweistellig sondern nun auch vierstellig übergeben kann. Hier wirkt seine 1970’er Grenze der zweistelligen Jahreszahl nicht mehr. Vermutlich hat er es nicht mehr gewusst und seinen Code nach kurzer Zeit auch selbst nicht mehr verstanden, so dass diese Grenze von ihm übersehen wurde (die alternative Vorstellung, dass dies schon seit Version 1.0 so gewesen könnte, ist schlimmer).

Es gibt Wettbewerbe für möglichst unverständlichen Code. Das mache ich auch gerne. Das ist eine Art Knobelspiel für Programmierer und dient der Entspannung. Kein Programmierer würde ein solches Knobelspiel in eine Funktion packen, die er dem Anwender ernsthaft als produktive Variante für die Wochentagsberechnung anbietet. Schon gar nicht ohne die Grenzen seiner Funktion zu benennen. WTF? Es kann durchaus Gründe für unverständlichen Code geben. Steve Wozniak (The Woz) beispielsweise hat Code geschrieben, der die Eigenheiten der Hardware in besonderer Weise ausnutzt, um effizient zu sein. Für jemanden ohne diese intimen Kenntnisse ist der Code von Woz schlicht unverständlich und somit schwer zu pflegen. Im Vergleich dazu kann ich bei dem Algorithmus von Lawrence keinen rationalen Grund erkennen, warum der Code derart unverständlich sein muss. Hätte er den Code als Aufgabe präsentiert, frei nach dem Motto "finde heraus wann ich Geburtstag habe", oder ähnliches, wäre ich hellauf begeistert davon. Aber so liefert uns sein Code ein abschreckendes Beispiel dafür, wie man es nicht machen sollte.

Wenn wir seine Routine so lassen, wie er es sich vorgestellt hat, dann ist das selbst in der entwirrten Form keine gute Lösung für das Wochentagsproblem. Eine andere Umsetzung, zumindest für die Rückrechnung, muss her, die keine Fehler produziert, wenn das Datum vor dem 29.12.1969 liegt. Hier liefert die Funktion FncDateToWeekdayNr_Hack_11 den besten Ansatz unter den Hacks, nicht weil sie den beiden vorhergehenden Funktionen gegenüber technisch überlegen wäre, sondern weil der Lösungsweg hier für den Leser gut nachvollziehbar ist.

back-to-topIn Bearbeitung... (fertig)

Biber
Biber 27.06.2010 um 14:31:29 Uhr
Goto Top
Moin NeonZero,

seinerzeit Anno 2005 als ich dieses Tut geschrieben habe, war ich auch manchmal in dieser Phase "dieser Algorithmus lässt mich nicht los", ähnlich wie du es beschreibst.

Und das lag vor allem damals an der Erkenntnis (die ich tagelang einfach nicht glauben wollte)
"Hey, 5 Jahre nach diesem Y2K-Desaster, wo ALLE gelobt hatten 'Ab sofort gehen wir vernünftiger mit der Speicherung von Datumswerten um - wir haben verstanden!' - 5 Jahre danach ist kein Excel, kein Oracle und kein Linux dieser Welt dazu in der Lage auszurechnen, ob die Santa Maria an einen Dienstag oder einem Donnerstag vor Amerika gelandet ist.
Weil die alle nur in einem putziputzikleinen Zeitfensterchen "richtig" rechnen können.
Und wer die Frage beantworten will, an welchem Wochentag der Stapellauf von Noahs Arche war, der kommt mit dem Talmud zum Ziel, nicht aber mit irgendwelchen 32- oder 64-Bit-Klamotten."


Das, was (vermutlich) in der Kernelfunktionen der OSe implementiert ist, wird mit kleinen Unschärfen der folgende Algorithmus sein.
der oben von dir gerade seziert wird und der beispielsweise hier in einer PHP-Variante diskutiert wird.


Zitat eines Kommentars von Christian Seiler, 02. 10. 2007, 09:40

 function wochentag ($jahr, $monat, $tag, $g = true) {
 if ($monat <= 2) {
  $jahr--;
  $monat += 12;
 }
 if ($g) {
  $jahrhundert = (int) (($jahr + 4800) / 100) - 48;
  $korrektur = 2 - $jahrhundert + (int) (($jahrhundert + 48) / 4) - 12;
 } else {
  $korrektur = 0;
 }
 $jul_tag_zahl = (int) (365.25 * ($jahr + 4716))+ (int) (30.6001 * ($monat + 1)) + $tag + $korrektur - 1524;
 return $jul_tag_zahl % 7;
}

Dies dürfte auch die effizienteste korrekte Implementierung in PHP sein,
ist etwa 1,3x so schnell wie Deine Lösung,
unterstützt sowohl den gregorianischen als auch den julianischen Kalender
und ist korrekt für alle Daten ab dem 1. Januar 4713 v.U.Z. (das war vor etwa 6720 Jahren).

Quelle: Datums- und Zeitberechnung mit PHP, Schaltjahre, Zeitzonen

In den Kernelfunktionen gibt es keine Notwendigkeit wie im Batch, sich auf Integerwerte und/oder Ganzzahlen zu beschränken - dort dürfte natürlich auch mit "365,25 Tagen pro Jahr" etc gerechnet werden.

Aber bei jeder Vereinfachung auf "Rechnen nur mit Ganzzahlen" ergibt sich zwangsläufig dieser merkwürdige Wert "4800 Jahre" aus der Zeile
set /a y=yy+4800-z ....
die du ganz unten zitierst.

--> Kurzer Sinn der ganzen Rede:

  • dein Hinweis auf eine Vereinfachung des Abschneidens eines Textteils mit "Set bla=%bla:* =%" ist gut und hilfreich.
  • der Algorithmus von Ritchie Lawrence ist eine Spur zu effekthascherisch und kommentararm. Nicht verständlich und transparent. Gebe ich dir Recht.
  • der Algorithmus von Ritchie Lawrence ist allerdings dennoch richtig.
  • die Codezeile "set /a dd=100%dd%%%100,mm=100%mm%%%100" ist NICHT ersetzbar durch deine Alternative, da R.L. auch beiläufig eben auch auch den Monats/Tagwerten "08" und "09", die als Hex-Werte fehlinterpretiert werden würden, die Dez-Zahlen 8 und 9 macht.

Aber im Sinne eines Tutorials zu dem Thema finde ich deine Hinweise und Hinterfragungen durchaus richtig.

Grüße und schon jetzt danke
Biber
NeonZero
NeonZero 27.06.2010 um 15:22:41 Uhr
Goto Top
Zitat von @Biber:
365,25 Tagen pro Jahr

Ja, klar, man mulitpliziert die Jahre und erhält alle 4 Jahre einen Tag dazu. Viele Wege führen nach Rom; die Schaltjahre lassen sich ebenso gut mit einer Ganzzahlenberechnung erfassen. Das sollte hier also keine große Rolle spielen.

Zitat von @Biber:
Aber bei jeder Vereinfachung auf "Rechnen nur mit Ganzzahlen" ergibt sich zwangsläufig dieser merkwürdige Wert
"4800 Jahre" aus der Zeile
> set /a y=yy+4800-z ....


Zwangsläufig? Weshlab? Läuft doch auch ohne die 4800 Jahre alles im 32-Bit-Wertebereich der Ganzzahlen ab. Ausserdem wird der genutzte Wertebereich durch diese Aktion größer und nicht kleiner. Womöglich sehe ich jetzt nicht was Du meinst und begebe mich auf einen Holzweg? Wäre nett, wenn Du das näher ausführen könntest.

Zitat von @Biber:
  • der Algorithmus von Ritchie Lawrence ist allerdings dennoch richtig.

Glaube ich erst, wenn ich ihn auseinandergenommen und überprüft habe. ;)

Zitat von @Biber:
  • die Codezeile "set /a dd=100%dd%%%100,mm=100%mm%%%100" ist NICHT ersetzbar durch deine Alternative (set /a dd=dd), da R.L. auch
beiläufig eben auch auch den Monats/Tagwerten "08" und "09", die als Hex-Werte fehlinterpretiert werden würden, die Dez-Zahlen 8 und 9 macht.

Mist. Mein Fehler. Verdammter Schnellschuss. Nicht richtig überlegt. Du meinst, dass set Zahlen mit führender 0 als oktale Werte interpretiert, die bei 08 und 09 ungültig wären. Daran hatte ich nicht gedacht. Danke für den Hinweis.

Bye, nz
Biber
Biber 30.06.2010 um 20:47:37 Uhr
Goto Top
Moin NeonZero,

ganz, ganz dickes Lob und Anerkennung für die Auseinandersetzung mit den Datums/Kalender/Wochentagsalgorithmen.
Und deiner Art, das zu tun.

Ich denke, es dürfte auch in etwas weiterem Umkreis kein vollständigerer und auf die Herleitung der Algorithmen bedachter Artikel zu diesem Thema zu finden sein.

Faszinierend finde ich auch, dass - wenn es um konkrete Algorithmen und Lösungsstrategien geht -
selbst ein für viele vorsintflutliches und kantiges Steinzeitwerkzeug wie ein CMD-Batchscript
plötzlich ungeahnte Passgenauigkeit und spielerische Leichtigkeit gewinnt

Wenn jemand damit umgehen kann jedenfalls.

Deine Ausführungen wären auf jeden Fall ein eigenes Tutorial wert. Gefällt mir sehr gut

Danke
Biber
NeonZero
NeonZero 01.07.2010 um 10:27:52 Uhr
Goto Top
Danke für Dein Feedback.

Zitat von @Biber:
Deine Ausführungen wären auf jeden Fall ein eigenes Tutorial wert.

Habe ich mir auch schon überlegt. Denn es genügt nicht, wie ürsprünglich vorgesehen, Ritchie Lawrences Code ein wenig zu entwirren, um ihn allgemeinverständlich umzusetzen. Wenn wir die Routine so lassen, wie er es sich vorgestellt hat, dann ist das keine gute Lösung für das Wochentagsproblem. Ein anderer Ansatz, zumindest für die Rückrechnung, muss her. In diesem Fall passt das natürlcih besser in ein eigenes TUT zum Thema "Wochentagsberechnung unter Batch" oder ähnlichem. Zudem lassen sich dann auch die alternativen Lösungswege besser zusammenfassen und das Thema über das Wochentagsproblem hinaus ausbauen (JulianDay, etc.).

Wenn es Dir recht ist, beende ich mein Protokoll erst einmal in aller Ruhe hier in diesem Thread, wo ich es angefangen hatte, und mache dann erst ein eigenes TUT dazu auf. Das Thema wird dann zwar zum Teil doppelt behandelt, aber einfach jetzt hier aufzuhören oder das Protokoll gar zu löschen, macht diesen Thread hier unsauber. Dann versteht ja keiner mehr, worüber wir uns hier eigentlch unterhalten...

Bye, nz
pieh-ejdsch
pieh-ejdsch 19.02.2012 um 17:24:36 Uhr
Goto Top
moin Biber,

es sind zwar schon einige Jahreszeiten bisher vergangen seit diesem Beitrag: Batch am Monatsende ausführen,
wo es unter anderem um diese Ausgabe ging.
Gjeldende dato er: 04.03.2011
Skriv ny dato: (dd.mm.åå)
---
04.03.2011
[OT]
Da bin ich mir aber ziemlich sicher, der TE hat einen copy&paste Fehler Fabriziert, der Punkt war nacher mit drin.
[/OT]

aber nun wollte ich mal ein klein wenig Optimum für die GetAllDateTimeInfos.bat loswerden.

warum eigentlich .bat und nicht .CMD? Bat ist zwar als Abkürzung für Batch besser zu verstehen, aber da der Batch eh nur ab OS Windows_NT läuft wäre doch die Endung .CMD passender. Vor und in 98SE gabs doch keine *.CMD.

So nun mal zu den Kleinigkeiten.
Zeile 51.
     FOR /F "tokens=2-4 delims=/-,() skip=1" %%a in ('echo ^|date') do (
da ist hinter dem Echo kein PlatzhalterZeichen und führt Praktisch zu der Ausgabe:
Geben Sie das neue Datum ein: (TT-MM-JJ) ECHO ist eingeschaltet (ON).
Eingegebenes Datum kann nicht übernommen werden.
Geben Sie das neue Datum ein: (TT-MM-JJ)
ist zwar nicht weiter Tragisch, aber die Variablen werden so zweimal gesetzt.
Und oben die Ausgabe hat ja die Punkte im Datumsformat. Dieser müsste dann bei delims auch hinein in Zeile 51:
     FOR /F "tokens=2-4 delims=./-,() skip=1" %%a in ('echo(^|date') do (  

Und weil dieses DatumsFormat keinen Wert für YY oder für JJ liefert in den meisten Spracheinstellungen MM und DD oder TT gefunden werden aber der Wert fürs Jahr irgendetwas anderes ist und am Ende Trotzdem nicht ausgegeben werden, sind mir 3 Zeilen zur Verbesserung eingefallen:
als neue zusätzliche Zeile 55:
FOR /F "delims=DTMYJ-dtmyj" %%i in ("%DateOrder%") do set "Other=%%i"&if not defined JJ call set "JJ=%%%%i%%"  
Hiermit wird Dir ein Wert für JJ aus einer Bestehenden unbekannten Variablen ertmal Bekannt gemacht (Other) und als JJ, falls noch nicht gesetzt, gesetzt.
JJ aus dem Grund: In Zeile 120. wird es ausgeschlossen dieses JJ zu verwenden wenn schon YY besteht.
In Zeile 127. wird dazu auch diese jetzt bekannte Variable abgerufen:
For %%i in (INDate INTime %Other% %AllDateTimeVars%) do @if defined %%i set %%i|find /i "%%i="  
Sowie für den cleanUp zum löschen in Zeile 130:
For %%i in (vbssnippet INTime INDate %Other% Other) Do Set "%%i="  



Gruß Phil
Biber
Biber 20.02.2012 um 07:44:04 Uhr
Goto Top
Moin Phil,

vielen Dank für deine Hinweise und Ergänzungen.

Ich habe alles oben in eine angepasste Version v0.11 eingebaut mit Verweis auf deinen Kommentar.
Die Zeilennummern haben sich (durch eine neue Kommentarzeile) um +1 verschoben, wenn ich jetzt gleich die Änderung in Zeile 51 anspreche, steht es jetzt in Zeile 52 usw.

Eine kleine Änderung habe ich gemacht.
Das überflüssige bzw eigentlich auch falsche Leerzeichen zwischen "echo" und "|" in der Zeile 51
 FOR /F "tokens=2-4 delims=./-,() skip=1" %%a in ('echo(^|date') do (  

--> hab ich so geändert
 FOR /F "tokens=2-4 delims=./-,() skip=1" %%a in ('date^<nul') do (  
--> auch damit ist gewährleistet, dass der auf eine Eingabe wartende Befehl "date" mit einem (Leer-)Input bedient wird.
Und ein Befehl "date<nul" ist für die Nicht-so-regelmäßig-Bätscher vielleicht noch einen Hauch nachvollziehbarer als deine Variante "echo(|date".
Mag ja vom Effekt gleichbedeutend sein, aber deine ist ziemlich schwer über Suchmaschinen zu finden face-wink

Mit übernommen wider innere Überzeugung habe ich in ebendieser Zeile den zusätzlichen Delimeter "." [Punkt] für den Fall,
in irgendeinem Land/irgendeiner Sprachregion würden in der Eingabeaufforderung
Aktuelles Datum: 20.02.2012
Geben Sie das neue Datum ein: (TT-MM-JJ)
.. in der zweiten Zeile die TTs und MMs und JJs mit Punkten getrennt angezeigt.
Das kann ich mir nicht vorstellen. Auch weil Spontaneität und Kreativität zwei der letzten Eigenschaften sind, die ich den Redmonder PraktikantInnen unterstellen würde.

Aber egal, dieser Punkt ist ja nicht der springende. Also ist er jetzt drin.

Auch die Umbenamsung von .bat in .cmd habe ich oben übernommen.

Vielen Dank & Grüße
Biber
pieh-ejdsch
pieh-ejdsch 20.02.2012 um 12:55:10 Uhr
Goto Top
Ja eigentlich hatte ich gestern Nachmittag nur nichts zu tun.

.. in der zweiten Zeile die TTs und MMs und JJs mit Punkten getrennt angezeigt.
Das kann ich mir nicht vorstellen.
wollt ich auch erst nicht, doch ich hab mir Testhalber die Sprache:
norsk (bokmål)
installiert.
date^<nul
wie immer - kurz und knackig

Gruß Phil
RobSei
RobSei 10.10.2013 um 11:00:27 Uhr
Goto Top
Hallo,
ich habe hier ein chinesisches Datumsformat “2013-10-10 星期四“, welches durch das Skript wohl noch nicht unterstützt wird. Vielleicht hat ja jemand einen Tipp für mich.
Gruß RobSei
Biber
Biber 10.10.2013 um 23:33:27 Uhr
Goto Top
Moin RobSei,

willkommen im Forum.

Ich finde es ja toll, dass dieses kleine Tutorial immer noch Anregungen zum Verfeinern bietet.

Aber bei deiner Anforderung werde ich wohl nicht viel unterstützen können.

Die nächsten zwei Mannmonate bin ich noch in ein einem Projekt namens "Maya-Kalender" gebunden (da ist auch irgendein Bug im Datumsberechnungsalgorithmus, das ursprüngliche Entwicklerteam ist nicht mehr verfügbar und eigentlich sollte alles schon seit Dezember letzten Jahres vom Tisch sein) .

Danach wollte ich eine kleine GetAllDateTimeVars-Variante für die nach Qatar verhökerte Fussball-WM schreiben - allerdings von rechts nach links.
Das kostet mich doch mehr Zeit als gedacht. face-sad

Und ich muss endlich meinen Wochentagskonverter Vogonisch-Klingonisch fertigstellen - diese Kunden möchte ich ungern warten lassen.

Nein, im Ernst: ich habe nicht die Möglichkeit, mit chinesischem Datumsformat zu experimentieren.
Und vor allem nicht die nötige Sprach- und Sachkenntnis.

Ich würde mich allerdings ebensosehr wie du freuen, wenn eine/r der hier im deutschsprachigen Raum tätigen KollegInnen aus 中国 hier unterstützen könnten.
Ggf. auch via PN - ich werde Ergebnisse natürlich hier bereitstellen.

Ansonsten viel Glück.

Grüße
Biber.
RobSei
RobSei 11.10.2013 aktualisiert um 14:57:04 Uhr
Goto Top
Hallo Biber,

ich habe etwas rumgespielt und den Code auch auf den chinesischen PCs ziemlich umkonventionell (bin auch nicht so fit in Catch Programmierung) zu Laufen gebracht. Ich habe die Zeile
FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"   
durch folgendes ersetzt:
REM Datumsformat “Sa 01.10.2005“
If "%indate:~5,1%" == ":" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "/" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "." FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "-" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~5,1%" == "," FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
REM Datumsformat “Fri 10/11/2013"  
If "%indate:~6,1%" == ":" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "/" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "." FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "-" FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~6,1%" == "," FOR /F "tokens=2" %%i in ("%INDate%") do Set "INDate=%%i"  
REM Datumsformat “2013-10-10 ???“ (??? sind 3 chinesische Zeichen)
If "%indate:~4,1%" == ":" FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "/" FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "." FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "-" FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
If "%indate:~4,1%" == "," FOR /F "tokens=1" %%i in ("%INDate%") do Set "INDate=%%i"  
Gruß RobSei
Biber
Biber 11.10.2013 aktualisiert um 15:36:21 Uhr
Goto Top
Moin RobSei,

vielen Dank!
Ich habe deine Erweiterung sogleich in den oben im Tutorial stehenden Sourcecode übernommen, damit auch andere davon profitieren können.

Ich wünsche dir ein schönes Wochenende
Biber

P.S.
Die blöden Klingonen haben 12zeilige Wochentagsabkürzungen, aber keine Monate und Jahre.
Dafür muss ich später eine Erweiterung einbauen - ich belasse es heute erstmal bei Version 012.