[NEWSboard IBMi Forum]

Hybrid View

  1. #1
    Registriert seit
    Aug 2001
    Beiträge
    2.934
    Zitat Zitat von Fuerchau Beitrag anzeigen
    Im ILERPG gehts mit %CHAR auch ohne SQL.

    %replace(%char(MyTimeStamp:*iso):' ':'T') + 'Z';
    Bist Du sicher, dass Du damit genau 3 Positionen nach dem letzten Punkt hast? M.E. hast Du 6!
    Birgitta Hauser

    Anwendungsmodernisierung, Beratung, Schulungen, Programmierung im Bereich RPG, SQL und Datenbank
    IBM Champion seit 2020 - 5. Jahr in Folge
    Birgitta Hauser - Modernization - Education - Consulting on IBM i

  2. #2
    Registriert seit
    Feb 2001
    Beiträge
    20.748
    Ja, das kannst du dann auch noch mit %subst() kürzen.
    Aber das weiß ja nun jeder;-).

    Ich wollte ja nur die Frage bzgl. ILERPG beantworten, dass du da kein SQL brauchst.
    Dienstleistungen? Die gibt es hier: http://www.fuerchau.de
    Das Excel-AddIn: https://www.ftsolutions.de/index.php/downloads
    BI? Da war doch noch was: http://www.ftsolutions.de

  3. #3
    Registriert seit
    Oct 2019
    Beiträge
    16
    Hier noch eine Lösung die beliebige RPG Timestamps zu ISO8601 konvertieren kann.

    Code:
    **free
    
    ctl-opt dftactgrp(*no);
    ctl-opt option(*nodebugio: *srcstmt: *nounref);
    ctl-opt main(main);
    
    
    dcl-s time_t int(20) template;
    
    dcl-pr time like(time_t) extproc(*cwiden: 'time64');
      timeval  like(time_t) options(*omit);
    end-pr;
    
    dcl-ds struct_tm qualified template;
      tm_sec   int(10);  // seconds after the minute (0-61)
      tm_min   int(10);  // minutes after the hour (0-59)
      tm_hour  int(10);  // hours since midnight (0-23)
      tm_mday  int(10);  // day of the month (1-31)
      tm_mon   int(10);  // months since January (0-11)
      tm_year  int(10);  // years since 1900
      tm_wday  int(10);  // days since Sunday (0-6)
      tm_yday  int(10);  // days since January 1 (0-365)
      tm_isdst int(10);  // Daylight Saving Time flag
    end-ds;
    
    dcl-pr gmtime_r pointer extproc(*cwiden: 'gmtime64_r');
      timer  like(time_t) const;
      tm     like(struct_tm);
    end-pr;
    
    dcl-pr mktime like(time_t) extproc(*cwiden: 'mktime64');
      tm  like(struct_tm);
    end-pr;
    
    dcl-pr localtime_r pointer extproc(*cwiden: 'localtime64_r');
      timer  like(time_t) const;
      tm     like(struct_tm);
    end-pr;
    
    
    dcl-proc main;
      dsply time(*omit);
      dsply timestampToEpoch(%timestamp);
    
      dsply %timestamp(*sys: 0);
      dsply epochToTimestamp(timestampToEpoch(%timestamp));
    
      // 1 Stunde UTC-Offset für Winterzeit
      dsply timestampToISO8601(z'2021-01-01-14.00.00');
    
      // 2 Stunde UTC-Offset für Sommerzeit
      dsply timestampToISO8601(z'2021-08-01-14.00.00');
    
      // Timestamp innerhalb der überlappung von Sommer- und
      // Winterzeit wird als Winterzeit interpretiert
      dsply timestampToISO8601(z'2021-10-31-02.30.00');
    
      // Timestamp innerhalb der übersprungenen
      // Stunde wird als Winterzeit interpretiert
      dsply timestampToISO8601(z'2021-03-28-02.30.00');
    end-proc;
    
    
    // Konvertiert Unix Epoch Timestamp zu ISO8601 Timestamp
    dcl-proc epochToISO8601 export;
      dcl-pi *n char(20);
        timeval  like(time_t) const;
      end-pi;
      dcl-ds tm likeds(struct_tm) inz;
    
      // tm struct befüllen
      gmtime_r(timeval: tm);
    
      // ISO8601 Timestamp zusammensetzen
      return
        %subst(%editc(tm.tm_year + 1900: 'X'): 7: 4) + '-' +
        %subst(%editc(tm.tm_mon + 1: 'X'): 9: 2) + '-' +
        %subst(%editc(tm.tm_mday: 'X'): 9: 2) +  'T' +
        %subst(%editc(tm.tm_hour: 'X'): 9: 2) +  ':' +
        %subst(%editc(tm.tm_min: 'X'): 9: 2) +  ':' +
        %subst(%editc(tm.tm_sec: 'X'): 9: 2) + 'Z';
    end-proc;
    
    // Konvertiert RPG Timestamp zu ISO8601 Timestamp
    dcl-proc timestampToISO8601 export;
      dcl-pi *n char(20);
        timestamp timestamp(0) const;
        isdst     ind          const options(*nopass);
      end-pi;
      if %parms = 2;
        return epochToISO8601(timestampToEpoch(timestamp: isdst));
      else;
        return epochToISO8601(timestampToEpoch(timestamp));
      endif;
    end-proc;
    
    
    // konvertiert UNIX Epoch zu RPG Timestamp
    dcl-proc epochToTimestamp export;
      dcl-pi *n timestamp(0);
        time like(time_t) value;
      end-pi;
      dcl-ds tm likeds(struct_tm) inz;
    
      localtime_r(time: tm);
    
      return %timestamp(
        %subst(%editc(tm.tm_year + 1900: 'X'): 7: 4) +
        %subst(%editc(tm.tm_mon + 1: 'X'): 9: 2) +
        %subst(%editc(tm.tm_mday: 'X'): 9: 2) +
        %subst(%editc(tm.tm_hour: 'X'): 9: 2) +
        %subst(%editc(tm.tm_min: 'X'): 9: 2) +
        %subst(%editc(tm.tm_sec: 'X'): 9: 2):
        *iso0: 0
      );
    end-proc;
    
    
    // konvertiert RPG Timestamp zu UNIX Epoch
    dcl-proc timestampToEpoch export;
      dcl-pi *n like(time_t);
        timestamp timestamp(0) const;
        isdst     ind          const options(*nopass);
      end-pi;
      dcl-ds tm likeds(struct_tm) inz;
    
      tm.tm_year = %subdt(timestamp: *years) - 1900;
      tm.tm_mon  = %subdt(timestamp: *months) - 1;
      tm.tm_mday = %subdt(timestamp: *days);
      tm.tm_hour = %subdt(timestamp: *hours);
      tm.tm_min  = %subdt(timestamp: *minutes);
      tm.tm_sec  = %subdt(timestamp: *seconds);
    
      if %parms = 2;
        if isdst;
          tm.tm_isdst = 1;
        else;
          tm.tm_isdst = 0;
        endif;
      else;
        tm.tm_isdst = -1;
      endif;
    
      // convert to unix timestamp
      return mktime(tm);
    end-proc;

  4. #4
    Registriert seit
    Feb 2001
    Beiträge
    20.748
    Warum so kompliziert?
    Ein Timestamp wird in der Variablen als CHAR(22) im ISO-Format gespeichert.
    Dieses brauchst du dann nur per Redefine mit Zoned-Feldern zu deklarieren und kannst deinen Code um ganz viel kürzen:

    dcl-ds TSConv;
    dsTimestamp timestamp;
    dsYear zoned(2) pos(1);
    dsMonth zoned(2) pos(4);
    ....
    chYear char(2) pos(1);
    chMonth char(2) pos(4);
    ....
    end-ds;

    Damit kannst du den ganzen %subdt()-Kram abschaffen.
    Das Ganze gilt auch z.B. für ISO-Date und ISO-Time.
    Dienstleistungen? Die gibt es hier: http://www.fuerchau.de
    Das Excel-AddIn: https://www.ftsolutions.de/index.php/downloads
    BI? Da war doch noch was: http://www.ftsolutions.de

  5. #5
    Registriert seit
    Oct 2019
    Beiträge
    16
    Wenn die ISO8601 Timestamp auf 'Z' endet bedeutet dass das die Timestamp in UTC Offset 0 ist. Wenn man also so eine ISO8601 Timestamp für beliebige RPG Timestamps erzeugen will und das System auf CET eingestellt ist muss man dank der Sommer/Winterzeit die Timestamp um ein variables UTC Offset korrigieren. mktime verwendet die TZ Database und kann darum ermitteln was das UTC Offset war als die Timestamp entstanden ist (außer bei der Stunde Überlappung beim Übergang von Sommer- zu Winterzeit). Wenn mans also richtig machen will, reicht simples formatieren der Timestamp nicht.

  6. #6
    Registriert seit
    Sep 2005
    Beiträge
    426
    Hier hat Birgitta schon einmal was zu den Zeitzohnen gesagt:
    #3

  7. #7
    Registriert seit
    Feb 2001
    Beiträge
    20.748
    Da man das mit der Zeitzone ja weiß, konnte man dies ja schon füher in der DB durchaus berücksichtigen, wenn diese Info benötigt wurde.
    Ein Timestamp kann schon immer mittel QUTCOFFSET aus der Systemzeit die UTC-Zeit errechnen und in der DB speichern. Das geht nun mal nicht mit "current timestamp" per SQL oder %Timestamp().
    Die jeweils aktuelle Zeitzone konnte man ebenso separat speichern.
    Dies nun als spezielles Timestampwithzone-Feld zu entwickeln halte ich persönlich für nicht erforderlich, da es damit auch zu Inkompatibilitäten kommen kann.
    Bei vernünftigem Design (wie Dieter immer schön sagt) hätte man das Problem nie gehabt;-).
    Einfach nur UTC-Zeit sowie UTCOFFSET speichern und alles ist ok.
    Auch mit %Minutes() und %Hours() lässt sich aus QUTC dann die UTC-Zeit errechnen

    In Unix z.B. gibt die Zeitfunktion immer die Anzahl Sekunden seit 1.1.1970 in UTC zurück, also damals schon berücksichtigt.
    Ich habe allerdings auch AS/400-Systeme gesehen wo QUTCOFFSET immer 00:00 war und die Systemzeit halt immer umgestellt wird. Da bring so ein Timezone-Timestamp nun auch nichts.

    Hat SQL denn schon die Funktionen "LocalTime" oder UTCTime realisiert?
    Kann die AS/400 ähm IBM i inzwischen mehrere Zeitzonen (ohne Aufwand) auf derselben Maschine?
    Ich habe dies für Kunden schon realisieren müssen.
    Dienstleistungen? Die gibt es hier: http://www.fuerchau.de
    Das Excel-AddIn: https://www.ftsolutions.de/index.php/downloads
    BI? Da war doch noch was: http://www.ftsolutions.de

  8. #8
    Registriert seit
    Oct 2019
    Beiträge
    16
    Stimmt schon, wenn man beim Erstellen der Timestamp das UTC Offset mitspeichert ist das relativ einfach zu handhaben. Wenn man aber nur die Timestamp hat und im Nachhinein das UTC Offset wissen will, braucht man z.B. mktime. Leider ist das mit dem vernünftigen Design nicht unbedingt immer gegeben...

  9. #9
    Registriert seit
    Feb 2001
    Beiträge
    20.748
    "Wenn man aber nur die Timestamp hat und im Nachhinein das UTC Offset wissen will, braucht man z.B. mktime"???
    Erfindest du das Offset dann?

    C-Funktionen wie mktime, localtime usw. setzen eine Umgebungsvariable TZ (Timezone) voraus, sonst funktionieren sie nicht. Der Systemwert QUTCOFFSET wird allerdings nicht verwendet.
    Außerdem:

    The C library function time_t mktime(struct tm *timeptr) converts the structure pointed to by timeptr into a time_t value according to the local time zone.
    M.a.W.:
    - jeder beliebige Zeitstempel muss bereits als UTC vorliegen.
    - Die Umrechnung erfolgt mit der aktuellen TZ, also nicht der vergangenen TZ

    Per Locale-Einstellung (Userprofíl) kann die Variabel TZ jobspezifisch gesetzt werden.
    Hast du das mal geprüft?

    Wie gesagt, ich musst das mal realisieren, da unterschiedliche Zeitzonen (alleine schon wegen GB) benötigt wurden.
    Dienstleistungen? Die gibt es hier: http://www.fuerchau.de
    Das Excel-AddIn: https://www.ftsolutions.de/index.php/downloads
    BI? Da war doch noch was: http://www.ftsolutions.de

  10. #10
    Registriert seit
    Oct 2019
    Beiträge
    16
    "Erfindest du das Offset dann?"
    Nö, mktime versucht es zu ermitteln. Wie schon gesagt, das funktioniert gut außer bei der Stunde Überlappung beim Übergang von Sommer- zu Winterzeit, die defaulted immer zum Offset der Winterzeit.

    A negative value for tm_isdst causes mktime() to attempt to determine whether DST is in effect for the specified time.

    Code:
      // 1 Stunde UTC-Offset für Winterzeit
      dsply timestampToISO8601(z'2021-01-01-14.00.00');
    
      // 2 Stunde UTC-Offset für Sommerzeit
      dsply timestampToISO8601(z'2021-08-01-14.00.00');
    Das sollte auf einem System in der CET folgendes ausgeben:
    Code:
    DSPLY 2021-01-01T13:00:00Z
    DSPLY 2021-08-01T12:00:00Z
    Es wird also jeweils das richtige UTC Offset ermittelt.

  11. #11
    Registriert seit
    Feb 2001
    Beiträge
    20.748
    Dann verstehe ich die Beschreibung allerdings nicht:
    https://www.ibm.com/docs/en/i/7.4?to...ert-local-time
    Laut Beschreibung wird die aktuelle Zeitzone zur Errechnung der UTC verwendet.

    "For this conversion, mktime() checks the current locale setting for local time zone and daylight saving time (DST). If these values are not set in the current locale, mktime() gets the local time zone and daylight saving time settings from the current job."

    D.h. Wenn ich einen Timestamp, also Localtime, aus der Zeitzone USA nehme wird er mit der aktuellen Zeitzone meines Jobs/Systems in UTC umgerechnet.
    Ebenso entscheidet die aktuelle Einstellung von DST ob nun Sommer/Winterzeit berücksichtigt wird.
    Dies ist i.Ü. auch meine Erfahrung.
    Dienstleistungen? Die gibt es hier: http://www.fuerchau.de
    Das Excel-AddIn: https://www.ftsolutions.de/index.php/downloads
    BI? Da war doch noch was: http://www.ftsolutions.de

Similar Threads

  1. Format von PF ändern
    By _MG_ in forum NEWSboard Programmierung
    Antworten: 8
    Letzter Beitrag: 21-04-17, 14:09
  2. RDI - /End-Exec im festen Format
    By homue in forum NEWSboard Programmierung
    Antworten: 8
    Letzter Beitrag: 25-07-16, 15:27
  3. Feldgruppen im free-Format
    By kretzsch in forum NEWSboard Programmierung
    Antworten: 3
    Letzter Beitrag: 14-08-14, 13:02
  4. XML-Ausgabe in Cobol im Format UTF-8
    By Günter in forum NEWSboard Programmierung
    Antworten: 10
    Letzter Beitrag: 26-06-14, 15:10
  5. JDBC Datum Format EUR
    By camouflage in forum NEWSboard Java
    Antworten: 1
    Letzter Beitrag: 02-12-13, 16:58

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • You may not post attachments
  • You may not edit your posts
  •