Autor Téma: BatchMove niečo ako UDF  (Přečteno 10344 krát)

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
BatchMove niečo ako UDF
« kdy: 19-08-2018, 01:36:49 »
Riešenie, ak by sa našlo, by sa obecne mohlo hodiť aj na iné prípady.

Moja aktuálna požiadavka:
Import csv súboru do databázy (SQLite. Podstatné je len to, že v nej využívam formát času ako má Delphi)
Potreboval by som zmeniť formát Time na sekundy.
Hlavne kvôli faktu, že Time, resp DateTime nemôže v Delphi byť záporný.
V reálnom živote však existuje. Napríklad meškanie autobusu. Môže byť kladné, ale aj záporné.
Mám podklad s hodnotami napr.:
00:01:00  tj. mešká 60 sekúnd.
-00:00:20 tj. prišiel skôr o 20 sekúnd.
Ak by tento druhý tvar fungoval v DateTime, nie je čo riešiť. Ale pre DateTime je oblasť 0 až -1 nedefinovaná. Ak sa tam aj nejak nastrkajú dáta medzi 0 až -1, výsledok sa aj tak ukáže ako kladný. Škoda.
Takže niečo, ako premena '00:01:10' na sekundy ( 70 sekúnd )
Matematiku zvládam, ale rád by som tú matiku vložil do BatchMove. Počas výpočtu, à la UDF.
Ostatné polia mi štandardne vyhovujú, ale to jedno by som potreboval ovplyvniť už počas importu CSV.
« Poslední změna: 19-08-2018, 01:48:55 od Miroslav Baláž »

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3506
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:BatchMove niečo ako UDF
« Odpověď #1 kdy: 19-08-2018, 08:01:24 »
Hlavne kvôli faktu, že Time, resp DateTime nemôže v Delphi byť záporný.
V reálnom živote však existuje. Napríklad meškanie autobusu. Môže byť kladné, ale aj záporné.
Proc by nemohl byt zaporny? Vzdyt je to hodnota typu double, kde cela cast je pocet dni od 31.12.1899 a desetinna je pomerna cas dne (typicky pocitana s presnosti 1/(24*60*60*1000)). A je jen na programatorovi, jak hodnotu interpretuje, jestli jako abs. nebo relativni cas. Abs. cas je vyjadren relativne k 31.12.1899 00:00, takze -365.25 bude 31.12.1898 6:00. A pokud ma hodnota reprezentovat relativni cas jako zpozdeni, tak -00:00:20 je -2,314814...e-4

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #2 kdy: 19-08-2018, 10:08:20 »
O dátumoch typu datetime viem hodne už dlho. Písalo sa o tom aj tu:
https://forum.delphi.cz/index.php/topic,15955.msg98401.html#msg98401
V Delphi, v databáze a aj v Exceli to bohužiaľ nefunguje. Bolo by krásne, ak by fungovali aj záporné.
Realita je iná. Tu je oficiálny odkaz:
http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.TDateTime
Citujem: "There are no TDateTime values from –1 through 0."

Ide aj o to, ako sa dátum zobrazí v dbGride. No a tam sa práve aj záporné číslo (v tvare Time) 0>x >-1 zobrazí ako kladná hodnota. Dokonca nie je garancia, že systém neodstráni ten mínus už v priebehu výpočtov. Musel by som vyslovene počítať s čistým double, ten by mi nič neukradol. Lenže musel by som spraviť všetku réžiu okolo dátumov sám. A to by bolo nadlho.

Excel dokonca záporný čas vykrížikuje, nech bunku roztiahneš aj na celú obrazovku..
Skúšal som kadečo, ba skôr všetko možné aj nemožné. Tade cesta nevedie. Rozhodne nie kompatibilná cesta.


Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3506
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:BatchMove niečo ako UDF
« Odpověď #3 kdy: 19-08-2018, 10:58:20 »
Citujem: "There are no TDateTime values from –1 through 0."
Ale to rika jenom to, ze desetinna cast je vzdy pomerna cas dne bez znamenka, coz je logicke, protoze asi malokdo by chtel, aby mu to pocitalo s doplnkem... A znamenko je dano poctem celych dni...

Citace
Ide aj o to, ako sa dátum zobrazí v dbGride.
Problem je jen a jen reprezentace dat v gridu. Vsechny vypocty lze delat bez problemu s TDateTime a vysledky odpovidajicim zpusobem renderovat. 
A to lze i v tom Excelu viz obrazek, kde jsou odecteny A1-B1 a B1-A1 a pro zaporne hodnoty vysledek vhodne naformatovan - je jen na tobe, jestli chces psat zaporny rozdil casu nebo jako zpozdeni nebo jako buhvi co
[/quote]

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #4 kdy: 19-08-2018, 15:27:52 »
Ide mi o import kladnych a zapornych hodnot casu z CSV pomocou FireDAC.
Príklad záporného času.:-00:20:00
Ak by FireDAC s takým importom nemal problém, už dávno to riešenie používam.
Veľmi dobre viem, ako datetime funguje a ako s ním možno pracovať.
Otázku som jasne formuloval. Ak by to fungovalo, nepýtam sa.
Mimochdom vid aj: "Compiler always appears to treat TDateTime as positive when doing numerical operations on it. Try this:" na stránke:
https://stackoverflow.com/questions/49201389/delphi-displays-strange-results-for-operations-with-negative-values-to-tdatetime


Nech je ako chce, stále trvám na svojej pôvodnej otázke.
« Poslední změna: 19-08-2018, 15:33:54 od Miroslav Baláž »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #5 kdy: 19-08-2018, 15:38:03 »
..
A to lze i v tom Excelu viz obrazek..
Podľa všetkého, aby Excel korektne zobrazoval záporný dátum, tak ako na priloženom obrázku k citovanému príspevku, musí byť Excel prestavený zo štandardného časového systému na systém ..1904..
Viď, napríklad:
https://www.extendoffice.com/documents/excel/1423-excel-display-show-negative-time.html
V opačnom prípade bude vidieť len chybová hláška:
"###############################"
« Poslední změna: 19-08-2018, 15:39:45 od Miroslav Baláž »

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3506
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:BatchMove niečo ako UDF
« Odpověď #6 kdy: 19-08-2018, 16:33:48 »
V opačnom prípade bude vidieť len chybová hláška:
"###############################"
Jak vidis, tak neni: nic jsem neprestavoval, jen hodnotu vhodne vyrenderoval

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #7 kdy: 19-08-2018, 19:50:47 »
V opačnom prípade bude vidieť len chybová hláška:
"###############################"
Jak vidis, tak neni: nic jsem neprestavoval, jen hodnotu vhodne vyrenderoval
Neviem, potom sa napríklad tento odkaz venuje neexistujúcemu problému?
https://www.extendoffice.com/documents/excel/1423-excel-display-show-negative-time.html
K tejto odbočke od témy, už viac nebudem reagovať. Rozhodne nevedie k akceptovateľnému riešeniu.

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 7174
  • Karma: 44
    • Verze Delphi: W10 + D11.1
Re:BatchMove niečo ako UDF
« Odpověď #8 kdy: 20-08-2018, 08:23:49 »
Podľa Tvojho príspevku máš dátum a čas v CSV oddelené.
Keďže DateTime nemôže mať záporný čas, tak ja osobne vidím len jedno riešenie. Pridať si do tabuľky pole, ktoré bude určovať či sa jedná o kladný či záporný čas.

Ak to čítam zle, tak má na celej čiare pravdu pf1957.

Neviem ako Ty, ale ja FireDAC batch používam vždy v cykle, prípadne vnorené cykly, keď robím čosi, čo sa v DB realizovať nedá. V tom čase nechávam DB na pokoji - čo sa týka Insert/Update. Po ukončení výpočtov/prípravy dát spustím batch. Tam sa už vykonáva jedine update DB. Takže žiadnu UPF či SP nepotrebujem. Čas strávený prípravou dát ma netrápi, keďže s DB nekomunikujem!
A na takéto použitie je ten batch určený.
Win11 64b, Delphi 11.1, FireBird 4.01
Expert na kladenie nejasne formulovaných otázok.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #9 kdy: 20-08-2018, 09:52:46 »
V tomto prípade pf1957 pravdu nemá.
Prečo by inak tejto témy (záporného času v DateTime), boli plné fóra?

Ale hlavne, to bolo to prvé, čo ma napadlo, už pred rokmi. Po podrobnej analýze som to musel aj teraz znovu zavrhnúť. Dávno pred jeho príspevkom. Celkovo som tomu venoval dlhé hodiny, pretože je to typ údajov, ktorý považujem za významný pre svoje projekty.
Áno na prvý pohľad to vyzerá veľmi logicky.
Reálnou pravdou zostáva, múdri ľudia to zrejme vydedukovali, že uplatnenie záporného času sa pre systém DateTime nehodí. Preto tú oblasť vylúčili. Nechce sa mi skúmať ich presné dôvody.
Delphi a aj databáza Access dávajú jasne najavo, že toto nie je cesta. Tým, že jednoducho výsledok neposkytnú, v tvare, aký by človek rád očakával.
Platí to aj pre Excel, aj keď pf1957 tvrdí niečo iné.. Uviedol som link, kde je to zdokumentované aj s obrázkami..
 
Takže, použiť niečo nesystémové (s neistým správaním) a aj tak nekompletné (viď nižšie), by mohlo v budúcnosti napáchať nečakané škody.
Okrem toho, nehodlám venovať čas, niečomu, čo je zavrhnuté a s neistým chovaním. A naviac robí nekorektné zobrazenia (napr. dbGrid. Bola by riadna fuška prepracovať dbAware komponenty)
Okrem toho potrebujem len čas, teda mi stačí integer (4B), Oproti tomu typ Time, aka double zaberá 8B. Na 36 miliónoch riadkov aj ten rozdiel poteší.

A rozhodne nemienim vytvárať paralelný datový systém (s kompletnými dôsledkami), ktorý bude presne kopírovať chovanie DateTime, len s tou výnimkou, že pokryje oblasť 0>x>-1.
Sú aj ďalšie veci, ale neotváram ich, pretože touto cestou sa uberať určite nechcem
Toto je riadna odbočka od otázky, na ktorú som sa pôvodne pýtal v tomto vlákne.
Otázku som precízne sformuloval.
Takto by príspevky nemali fungovať.
« Poslední změna: 20-08-2018, 10:04:40 od miroB »

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #10 kdy: 20-08-2018, 09:58:37 »
..
Neviem ako Ty, ale ja FireDAC batch používam vždy v cykle, prípadne vnorené cykly, keď robím čosi, čo sa v DB realizovať nedá. V tom čase nechávam DB na pokoji - čo sa týka Insert/Update. Po ukončení výpočtov/prípravy dát spustím batch. Tam sa už vykonáva jedine update DB. Takže žiadnu UPF či SP nepotrebujem. Čas strávený prípravou dát ma netrápi, keďže s DB nekomunikujem!
A na takéto použitie je ten batch určený.
Mal som na mysli BatchMove, ako niečo, čím môžem na jeden šup, bez cyklov stiahnuť celý csv.
Môžeš mi dať odkaz, alebo krátky kód, kde by sa nejaký import robil v cykle?
Tým by sa nahradila požiadavka na UDF, resp. SP.

Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #11 kdy: 20-08-2018, 14:54:27 »
Vyriešené,
Stano ma nakopol, nech to naštudujem a vyskúšam.
"UDF" náhrada, čo som po nej pátral, je skrytá v TBatchMove, udalosť: OnWriteValue.
Riešenie (aj keď v ňom nie je všetko potrebné), som postavil na báze dotazu na SO:
https://stackoverflow.com/questions/39987523/importing-a-csv-file-using-a-fdbatchmove-into-a-fdtabletask
Aspoň som nemusel ísť úplne od nuly.

Kód: Delphi [Vybrat]
  1. procedure TForm1.FDBatchMove1WriteValue(ASender: TObject; AItem: TFDBatchMoveMappingItem; var AValue: Variant);
  2. var
  3.  p_unichar, pUColon1, pUColon2 : PChar;
  4.  iHour, iMinute, iSecond       : Integer;
  5.  sSV                           : String;
  6. begin    // Tu je ten môj dotazovaný "kvázi UDF"
  7.   with AItem do
  8.     if ( SourceFieldName = 'TimeInSeconds' ) and not VarIsNull( SourceValue ) then
  9.       begin  
  10.       sSV              := SourceValue;
  11.       if sSV[ 1 ] = '-' then
  12.         p_unichar      := PChar( sSV ) + 1
  13.       else
  14.         p_unichar      := PChar( sSV );
  15.       pUColon1         := StrScan( p_unichar + 1, ':' );
  16.       if pUColon1 <> nil then
  17.         begin
  18.         pUColon2       := StrScan( pUColon1 + 1, ':' );
  19.         if pUColon2 <> nil then
  20.           begin
  21.           pUColon1^    := #0;
  22.           pUColon2^    := #0;
  23.           if  TryStrToInt( p_unichar, iHour ) and ( iHour in [ 0 .. 60 ] ) and
  24.               TryStrToInt( pUColon1 + 1, iMinute ) and ( iMinute in [ 0 .. 60 ] ) and
  25.               TryStrToInt( pUColon2 + 1, iSecond ) and ( iSecond in [ 0 .. 60 ] ) then
  26.             if sSV[ 1 ] = '-' then
  27.               AValue   := - ( iHour * 3600 + iMinute * 60 + iSecond )
  28.             else
  29.               AValue   := iHour * 3600 + iMinute * 60 + iSecond;
  30.           pUColon1^    := ':';
  31.           pUColon2^    := ':';
  32.           end;
  33.         end;
  34.       end;
  35. end;
Ak by niekto chcel celý postup, zbalím kód do prílohy a pridám

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3506
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:BatchMove niečo ako UDF
« Odpověď #12 kdy: 21-08-2018, 09:00:04 »
V tomto prípade pf1957 pravdu nemá.
Prečo by inak tejto témy (záporného času v DateTime), boli plné fóra?
Protoze je spousta lidi, kteri michaji jablka s hruskama a nejsou schopni pracovat s pomerne inteligentni formou kodovani casu, ktera ho vyjadruje jako pocet dni.
Programator vetsinou umi ocenit, ze pri vypoctech muze pouzit jeden datovy typ pro operace s kalendarnim i relativnim casem.
Samozrejme, ze lze zakodovat zaporny cas vyjadreny v poctech dnu v intervalu -1..0 napr. 20180821T060000 - 20180921T120000 = -1/4 dne

Co nelze, je interpretovat to jako kalendarni datum, kdyz jim neni. A i v tom odkazovanem navodu k excelu krome nejakeho exotickeho posouvani roku uvadeji jasny navod, jak hodnotu vyrenderovat, coz jsem udelal. Ostatne myslence oddeleni prezentace hodnot navenek, ktera se objevuje v ruznych MVC patternech, uz jsou desitky let


Offline miroB

  • Guru
  • *****
  • Příspěvků: 594
  • Karma: 17
    • Verze Delphi: D1,2,3,4,7,2005,2009, XE8,S,B,T10.2.2 Pro
Re:BatchMove niečo ako UDF
« Odpověď #13 kdy: 21-08-2018, 10:34:43 »
Tak ja neviem, nikto tu nehovorí o tom, že matematika DateTime, alebo Time, vrátane záporných hodnôt Time nie je správna. Ani o tom, že by mi nevyhovovala, ak by bola riadne a do dôsledkov podporovaná.
Dokonca moja prvá verzia importu používala Time s tým, že mínus sa vopred odtrhol, aby konverzia prebehla. Potom som to prenásobil -1 a bolo. Problémy nastali až potom.
1. Interpretácia v dbaware nie je podporovaná /VEĽMI ZLÉ: To je to, čo určite nechcem nahrádazať/
2. Ak by sa takýto nepodporovaný formát použil vo výpočtoch, nebol by garantovaný výsledok (skrytá zmena - na + .. To minimálne, môžu byť aj horšie veci. Dúfam je všetkým jasné, čo znamená v programovaní nedefinované chovanie) . Takže počas celého života premennej by som ju musel každú sekundu strážiť či je v poriadku.
3. A hlavne som už s tou verziou pracoval a bolo to len ZLE v praktickom využití.
AJ PRETO SOM UŽ V POVODNOM DOTAZE TÚTO MOŽNOSŤ VYLÚČIL.
Pýtal som sa teda na iné konkrétne riešenie problému.
To nie je dosť jasné?

Ale nedbám, dal by som sa presvedčiť, keby mi to ponúkané riešenie prinieslo výhody.
Presviedčaš ma tu o niečom, čo som už dávno vedel. Lenže to v praxi nefunguje a ani nebude. Teda bez toho, že by mi to pridalo kopu starostí. A k tomu navyše neistotu.
Ak to ponechám ako typ Time, nikdy nebudem mať garantované, či sa mínus v pozadí pri opakovaných výpočtoch nezmení na plus.
Ak by som to celé riešil po svojom ako double, tak som si vytvoril vlastný datový typ a môžem si to celé programovať ako nový systém sám..
V totmo si vážim Delfína, povie zhruba:
dalo by sa to lepšie radšej takto. Ale ak chceš nedbám a ukážem ti aj cestu, ktorou chceš ísť ty.
Obaja ste v programovaní vzdelaní, máte skúsenosti. Lenže Delfínov prístup si vážim.

A čo je najlepšie. Požadované riešenie som našiel. Vďaka obyčajnej Stanovej poznámke. Už som ho zverejnil vyššie.
« Poslední změna: 21-08-2018, 10:39:06 od miroB »

Offline JaroB

  • Guru
  • *****
  • Příspěvků: 1114
  • Karma: 29
    • Verze Delphi: XE8, Sydney
Re:BatchMove niečo ako UDF
« Odpověď #14 kdy: 21-08-2018, 12:22:00 »
Potřebujete zobrazení datumu na časové ose v lidsky čitelném formátu nebo potřebujete vyčíslení časového úseku (který teprve bude nebo už minul k nějakému myšlenému datumu)?

Tohle asi funguje správně, jsme na časové ose

Kód: Delphi [Vybrat]
  1.   ShowMessage(DateTimeToStr(2246.258954)); // 23.2.1906 6:12:53
  2.   ShowMessage(DateTimeToStr(-2246.258954)); // 5.11.1893 6:12:53

Tohle je ale jen plynutí času neuměle naznačené

Kód: Delphi [Vybrat]
  1. const
  2.   ph = 24;
  3.   phm = 24 * 60;
  4.   phms = 24 * 60 * 60;
  5. var
  6.   AYear, AMonth, ADay, AHour, AMinute, ASecond, Ams: Word;
  7.   D: Double;
  8.   s: string;
  9. begin
  10.   D := -2246.258954; // jen representuje časový úsek
  11.   s := 'úsek ';
  12.   if D < 0 then s := s + 'před ';
  13.   D := Abs(D);
  14.   AYear := Trunc(D / 365); D := D - 365 * Trunc(D / 365);
  15.   AMonth := Trunc(D / 30); D := D - 30 * Trunc(D / 30);
  16.   ADay := Trunc(D); D := Frac(D);
  17.   AHour := Trunc(D * ph); D := D - Trunc(D * ph) / ph;
  18.   AMinute := Trunc(D * phm); D := D - Trunc(D * phm) / phm;
  19.   ASecond := Trunc(D * phms); D := D - Trunc(D * phms) / phms;
  20.   Ams := Trunc(D * phms * 1000);
  21.   ShowMessage(
  22.     s +
  23.     IntToStr(AYear) + 'roků ' +
  24.     IntToStr(AMonth) + 'měsíců ' +
  25.     IntToStr(ADay) + 'dnů ' +
  26.     IntToStr(AHour) + 'hodin ' +
  27.     IntToStr(AMinute) + 'minut ' +
  28.     IntToStr(ASecond) + 'sec '
  29.   );
  30.   //zapsatelný jako úsek
  31.   ShowMessage(Format(s + '%.2d r %.2d m %.2d d %.2d:%.2d:%.2d', [AYear, AMonth, ADay, AHour, AMinute, ASecond]));

Záleží na interpretaci, co to číslo znamená.