Date64 - Unixtime 64bit

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Date64 - Unixtime 64bit

Beitrag von Sicro »

Hallo NicTheQuick,

bezüglich der langsamen While-Schleifen hast du mich ja schon in einem anderem Thread hingewiesen. Ja, sie sind langsam. Als ich den Code programmiert habe, war es so verständlicher für mich und optimieren kann man später noch, dachte ich. Nun hatte ich genug Zeit, die geplanten Änderungen umzusetzen.

Warum ich bei der Korrektur der negativen Werte "59" statt "60" geschrieben habe, lag daran, dass bei einer älteren Code-Version sonst falsche Werte zurückgegeben wurden. Die aktuelle Code-Version läuft nun anscheinend fehlerfrei mit "60".

Vielen Dank für deine Hinweise!

Letzter Code wurde auf den aktuellen Stand gebracht.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Date64 - Unixtime 64bit

Beitrag von Sicro »

So, ich habe mich nochmal an den Code ran gemacht. :)

Änderungen (18.03.2016):
  • AllocateMemory() entfernt. Normale Variable reicht.
  • mktime() durch timegm() ersetzt
  • Procedure "LocalDateAsUTC()" entfernt, weil alle Funktionen UTC zurückgeben
  • Test-Code prüft nun alle Rückgaben selbstständig
  • Kommentar zur maximalen Grenze bei Windows angepasst
  • Code läuft nun auch unter Linux fehlerfrei :D (getestet unter Xubuntu_x86, LinuxMint_x64 und WindowsXP)
Änderungen (19.03.2016):
  • Doppelter Code per Macros reduziert (Code-Zeilen-Einsparung: 140 Zeilen)

Code: Alles auswählen

DeclareModule Date64
  Declare.i IsLeapYear(Year.i)
  Declare.i DaysInMonth(Year.i, Month.i)
  Declare.q Date64(Year.i=-1, Month.i=1, Day.i=1, Hour.i=0, Minute.i=0, Second.i=0)
  Declare.i Year64(Date.q)
  Declare.i Month64(Date.q)
  Declare.i Day64(Date.q)
  Declare.i Hour64(Date.q)
  Declare.i Minute64(Date.q)
  Declare.i Second64(Date.q)
  Declare.i DayOfWeek64(Date.q)
  Declare.i DayOfYear64(Date.q)
  Declare.q AddDate64(Date.q, Type.i, Value.i)
  Declare.s FormatDate64(Mask$, Date.q)
  Declare.q ParseDate64(Mask$, Date$)
EndDeclareModule

Module Date64
  EnableExplicit
  
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS And #PB_Compiler_Processor = #PB_Processor_x86
    CompilerError "32-Bit not supported on MacOS"
  CompilerEndIf
  
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
    ImportC ""
      gmtime_r_(*timep, *result) As "gmtime_r"
    EndImport
  CompilerEndIf
  
  ; == Windows ==
  ; >> Minimum: 01.01.1601 00:00:00
  ; >> Maximum: 31.12.9999 23:59:59
  
  ; == Linux ==
  ; 32-Bit:
  ; >> Minimum: 01.01.1902 00:00:00
  ; >> Maximum: 18.01.2038 23:59:59
  ; 64-Bit:
  ; >> Minimum: 01.01.0000 00:00:00
  ; >> Maximum: 31.12.9999 23:59:59
  
  ; == MacOS ==
  ; wie bei Linux?
  
  #SecondsInOneHour = 60 * 60
  #SecondsInOneDay  = #SecondsInOneHour * 24
  
  #HundredNanosecondsInOneSecond               = 10000000
  #HundredNanosecondsFrom_1Jan1601_To_1Jan1970 = 116444736000000000
  
  ;{ Struktur-Definition für "tm"
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      If Not Defined(tm, #PB_Structure)
        Structure tm Align #PB_Structure_AlignC
          tm_sec.l    ; 0 bis 59 oder bis 60 bei Schaltsekunde
          tm_min.l    ; 0 bis 59
          tm_hour.l   ; 0 bis 23
          tm_mday.l   ; Tag des Monats: 1 bis 31
          tm_mon.l    ; Monat: 0 bis 11 (Monate seit Januar)
          tm_year.l   ; Anzahl der Jahre seit dem Jahr 1900
          tm_wday.l   ; Wochentag: 0 bis 6, 0 = Sonntag
          tm_yday.l   ; Tage seit Jahresanfang: 0 bis 365 (365 ist also 366, da nach 1. Januar gezählt wird)
          tm_isdst.l  ; Ist Sommerzeit? tm_isdst > 0 = Ja
                      ;                             tm_isdst = 0 = Nein
                      ;                             tm_isdst < 0 = Unbekannt
          CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
            tm_gmtoff.l ; Offset von UTC in Sekunden
            *tm_zone    ; Abkürzungsname der Zeitzone
          CompilerElse
            tm_zone.l   ; Platzhalter
            tm_gmtoff.l ; Offset von UTC in Sekunden
            *tm_zone64  ; Abkürzungsname der Zeitzone
          CompilerEndIf
          
        EndStructure
      EndIf
    CompilerCase #PB_OS_MacOS
      If Not Defined(tm, #PB_Structure)
        Structure tm Align #PB_Structure_AlignC
          tm_sec.l    ; 0 bis 59 oder bis 60 bei Schaltsekunde
          tm_min.l    ; 0 bis 59
          tm_hour.l   ; 0 bis 23
          tm_mday.l   ; Tag des Monats: 1 bis 31
          tm_mon.l    ; Monat: 0 bis 11 (Monate seit Januar)
          tm_year.l   ; Anzahl der Jahre seit dem Jahr 1900
          tm_wday.l   ; Wochentag: 0 bis 6, 0 = Sonntag
          tm_yday.l   ; Tage seit Jahresanfang: 0 bis 365 (365 ist also 366, da nach 1. Januar gezählt wird)
          tm_isdst.l  ; Ist Sommerzeit? tm_isdst > 0 = Ja
                      ;                             tm_isdst = 0 = Nein
                      ;                             tm_isdst < 0 = Unbekannt
          tm_zone.l   ; Abkürzungsname der Zeitzone (Auch bei 64bit ein 32bit Wert)
          tm_gmtoff.l ; Offset von UTC in Sekunden
          *tm_zone64  ; Abkürzungsname der Zeitzone
        EndStructure
      EndIf
  CompilerEndSelect
  ;}
  
  Procedure.i IsLeapYear(Year.i)
    If Year < 1600
      ; vor dem Jahr 1600 sind alle Jahre Schaltjahre, die durch 4 restlos teilbar sind
      ProcedureReturn Bool(Year % 4 = 0)
    Else
      ; ab dem Jahr 1600 sind alle Jahre Schaltjahre, die folgende Bedingungen erfüllen:
      ; => restlos durch 4 teilbar, jedoch nicht restlos durch 100 teilbar
      ; => restlos durch 400 teilbar
      ProcedureReturn Bool((Year % 4 = 0 And Year % 100 <> 0) Or Year % 400 = 0)
    EndIf
  EndProcedure
  
  Procedure.i DaysInMonth(Year.i, Month.i)
    While Month > 12
      Year  + 1
      Month - 12
    Wend
    While Month < 0
      Year  - 1
      Month + 13
    Wend
    If Month = 0
      Month = 1
    EndIf
    
    Select Month
      Case 1, 3, 5, 7, 8, 10, 12: ProcedureReturn 31
      Case 4, 6, 9, 11:           ProcedureReturn 30
      Case 2:                     ProcedureReturn 28 + IsLeapYear(Year) ; Februar hat im Schaltjahr ein Tag mehr
    EndSelect
  EndProcedure
  
  Procedure.q Date64(Year.i=-1, Month.i=1, Day.i=1, Hour.i=0, Minute.i=0, Second.i=0)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Protected.SYSTEMTIME st
      Protected.FILETIME   ft, ft2
      Protected.i          DaysInMonth
      
      If Year > -1 ; Gültiges Datum
        
        ; Angaben evtl. korrigieren
        
        Minute + Second/60
        Second % 60
        If Second < 0
          Minute - 1
          Second + 60
        EndIf
        
        Hour + Minute/60
        Minute % 60
        If Minute < 0
          Hour   - 1
          Minute + 60
        EndIf
        
        Day + Hour/24
        Hour % 24
        If Hour < 0
          Day  - 1
          Hour + 24
        EndIf
        
        While Month > 12
          Year  + 1
          Month - 12
        Wend
        If Month = 0
          Month = 1
        EndIf
        
        DaysInMonth = DaysInMonth(Year, Month)
        While Day > DaysInMonth
          Day - DaysInMonth
          Month + 1
          If Month > 12
            Year  + 1
            Month - 12
          EndIf
          DaysInMonth = DaysInMonth(Year, Month)
        Wend
        
        If Day < 0
          Month - 1
          If Month = 0
            Year  - 1
            Month = 12
          EndIf
          Day + DaysInMonth(Year, Month)
        EndIf
        
        st\wYear   = Year
        st\wMonth  = Month
        st\wDay    = Day
        st\wHour   = Hour
        st\wMinute = Minute
        st\wSecond = Second
        
        ; Konvertiert Systemzeit (UTC) zu Dateizeit (UTC)
        SystemTimeToFileTime_(@st, @ft)
        
        ; UTC-Zeit in Sekunden umrechnen
        ProcedureReturn (PeekQ(@ft) - #HundredNanosecondsFrom_1Jan1601_To_1Jan1970) / #HundredNanosecondsInOneSecond
      Else
        ; Kein gültiges Datum. Lokale Systemzeit wird ermittelt
        GetLocalTime_(@st)
        SystemTimeToFileTime_(@st, @ft) ; "st" wird als UTC gelesen und zu Dateizeit konvertiert
        
        ; UTC-Zeit in Sekunden umrechnen
        ProcedureReturn (PeekQ(@ft) - #HundredNanosecondsFrom_1Jan1601_To_1Jan1970) / #HundredNanosecondsInOneSecond
      EndIf
    CompilerElse ; Linux oder Mac
      Protected.tm tm
      Protected.q time
      
      If Year > -1 ; Gültiges Datum
        tm\tm_year  = Year - 1900 ; Jahre ab 1900
        tm\tm_mon   = Month - 1   ; Monate ab Januar
        tm\tm_mday  = Day
        tm\tm_hour  = Hour
        tm\tm_min   = Minute
        tm\tm_sec   = Second
        
        ; mktime korrigiert die Angaben selber und liefert bereits Sekunden
        time = timegm_(@tm) ; Konvertiert UTC-Zeit zu UTC-Zeit

        ProcedureReturn time ; UTC-Zeit in Sekunden
      Else
        ; Kein gültiges Datum. Systemzeit wird ermittelt
        time = time_(0)
        If localtime_r_(@time, @tm) <> 0
          time = timegm_(@tm)
        EndIf
        
        ProcedureReturn time  ; UTC-Zeit in Sekunden
      EndIf
    CompilerEndIf
  EndProcedure
  
  Macro Windows_ReturnDatePart(Type)
    Protected.SYSTEMTIME st
      
    Date = Date * #HundredNanosecondsInOneSecond + #HundredNanosecondsFrom_1Jan1601_To_1Jan1970
    FileTimeToSystemTime_(@Date, @st)
    
    ProcedureReturn st\Type
  EndMacro
  
  Macro LinuxMac_ReturnDatePart(Type, ReturnCode)
    Protected.tm tm
    Protected.i  Value
      
    If gmtime_r_(@Date, @tm) <> 0 ; Per Memory ist es thread-sicher
      Value = tm\Type
      ProcedureReturn ReturnCode
    EndIf
  EndMacro
  
  Procedure.i Year64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wYear)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_year, Value + 1900)
    CompilerEndIf
  EndProcedure
  
  Procedure.i Month64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wMonth)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_mon, Value + 1)
    CompilerEndIf
  EndProcedure
  
  Procedure.i Day64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wDay)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_mday, Value)
    CompilerEndIf
  EndProcedure
  
  Procedure.i Hour64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wHour)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_hour, Value)
    CompilerEndIf
  EndProcedure
  
  Procedure.i Minute64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wMinute)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_min, Value)
    CompilerEndIf
  EndProcedure
  
  Procedure.i Second64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wSecond)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_sec, Value)
    CompilerEndIf
  EndProcedure
  
  Procedure.i DayOfWeek64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wDayOfWeek)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_wday, Value)
    CompilerEndIf
  EndProcedure
  
  Procedure.i DayOfYear64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Protected.q TempDate
      
      TempDate = Date64(Year64(Date))
      ProcedureReturn (Date - TempDate) / #SecondsInOneDay + 1
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_yday, Value + 1)
    CompilerEndIf
  EndProcedure
  
  Procedure.q AddDate64(Date.q, Type.i, Value.i)
    Protected.i Day, Month, Year
    
    Select Type
      Case #PB_Date_Year:   ProcedureReturn Date64(Year64(Date) + Value, Month64(Date), Day64(Date), Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Month
        Day   = Day64(Date)
        Month = Month64(Date) + Value
        Year  = Year64(Date)
        
        If Day > DaysInMonth(Year, Month)
          ; mktime korrigiert das zwar auch, wendet dabei aber eine andere Methode als PB-AddDate an:
          ; >> mktime:     31.03.2004 => 1 Monat später => 01.05.2004
          ; >> PB-AddDate: 31.03.2004 => 1 Monat später => 30.04.2004
          
          ; Setzte Tag auf das Maximum des neuen Monats
          Day = DaysInMonth(Year, Month)
        EndIf
        
        ProcedureReturn Date64(Year64(Date), Month, Day, Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Week:   ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date) + Value * 7, Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Day:    ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date) + Value, Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Hour:   ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date), Hour64(Date) + Value, Minute64(Date), Second64(Date))
      Case #PB_Date_Minute: ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date), Hour64(Date), Minute64(Date) + Value, Second64(Date))
      Case #PB_Date_Second: ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date), Hour64(Date), Minute64(Date), Second64(Date) + Value)
    EndSelect
  EndProcedure
  
  Procedure.s FormatDate64(Mask$, Date.q)
    Protected Result$
    
    Result$ = ReplaceString(Mask$,   "%yyyy", RSet(Str(Year64(Date)),           4, "0"))
    Result$ = ReplaceString(Result$, "%yy",   RSet(Right(Str(Year64(Date)), 2), 2, "0"))
    Result$ = ReplaceString(Result$, "%mm",   RSet(Str(Month64(Date)),          2, "0"))
    Result$ = ReplaceString(Result$, "%dd",   RSet(Str(Day64(Date)),            2, "0"))
    Result$ = ReplaceString(Result$, "%hh",   RSet(Str(Hour64(Date)),           2, "0"))
    Result$ = ReplaceString(Result$, "%ii",   RSet(Str(Minute64(Date)),         2, "0"))
    Result$ = ReplaceString(Result$, "%ss",   RSet(Str(Second64(Date)),         2, "0"))
    
    ProcedureReturn Result$
  EndProcedure
  
  Macro ReadMaskVariable(MaskVariable, ReturnVariable)
    If Mid(Mask$, i, 3) = MaskVariable
      IsVariableFound = #True
      ReturnVariable = Val(Mid(Date$, DatePos, 2))
      DatePos + 2 ; Die 2 Nummern der Zahl überspringen
      i + 2       ; Die 3 Zeichen der Variable überspringen
      Continue
    EndIf
  EndMacro
  
  Procedure.q ParseDate64(Mask$, Date$)
    Protected.i i, DatePos = 1, IsVariableFound, Year, Month = 1, Day = 1, Hour, Minute, Second
    Protected MaskChar$, DateChar$
    
    For i = 1 To Len(Mask$)
      MaskChar$ = Mid(Mask$, i, 1)
      DateChar$ = Mid(Date$, DatePos, 1)
      
      If MaskChar$ <> DateChar$
        If MaskChar$ = "%" ; Vielleicht eine Variable?
          If Mid(Mask$, i, 5) = "%yyyy"
            IsVariableFound = #True
            Year = Val(Mid(Date$, DatePos, 4))
            DatePos + 4 ; Die 4 Nummern der Jahreszahl überspringen
            i + 4       ; Die 5 Zeichen der Variable "%yyyy" überspringen
            Continue
          ElseIf Mid(Mask$, i, 3) = "%yy"
            IsVariableFound = #True
            Year = Val(Mid(Date$, DatePos, 2))
            DatePos + 2 ; Die 2 Nummern der Jahreszahl überspringen
            i + 2       ; Die 3 Zeichen der Variable "%yy" überspringen
            Continue
          EndIf
          
          ReadMaskVariable("%mm", Month)
          ReadMaskVariable("%dd", Day)
          ReadMaskVariable("%hh", Hour)
          ReadMaskVariable("%ii", Minute)
          ReadMaskVariable("%ss", Second)
                    
          If Not IsVariableFound
            ProcedureReturn 0
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      EndIf
      
      DatePos + 1
    Next
    
    ProcedureReturn Date64(Year, Month, Day, Hour, Minute, Second)
  EndProcedure
EndModule

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
  
  ;-Test
  
  UseModule Date64
    
  Define.i Year, Month, Day, Hour, Minute, Second, Result, Result64
  Define.q Date, Date64
  Define   Date$, Date64$, Result64$
  
  Debug "Kleiner Kompatibilitäts-Test - Fehler:"
  For Year = 1970 To 2037
    For Month = 1 To 12
      For Day = 1 To 28
        For Hour = 0 To 23
          ;For Minute = 0 To 59
            ;For Second = 0 To 59
            Date = Date(Year, Month, Day, Hour, Minute, Second)
            Date64 = Date64(Year, Month, Day, Hour, Minute, Second)
            
            If Date <> Date64
              Debug "Date() <> Date64()"
              Debug Date
              Debug Date64
              Debug ""
            EndIf
            
            Date$ = FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", Date)
            Date64$ = FormatDate64("%yyyy.%mm.%dd %hh:%ii:%ss", Date64)
            If Date$ <> Date64$
              Debug "FormatDate() <> FormatDate64()"
              Debug Date$
              Debug Date64$
              Debug ""
            EndIf
            
            Result = ParseDate("%yyyy.%mm.%dd %hh:%ii:%ss", Date$)
            Result64 = ParseDate64("%yyyy.%mm.%dd %hh:%ii:%ss", Date64$)
            If Result <> Result64
              Debug "ParseDate() <> ParseDate64()"
              Debug Result
              Debug Result64
              Debug ""
            EndIf
            
            Result = DayOfWeek(Date)
            Result64 = DayOfWeek64(Date64)
            If Result <> Result64
              Debug "DayOfWeek() <> DayOfWeek64()"
              Debug Result
              Debug Result64
              Debug ""
            EndIf
            
            Result = DayOfYear(Date)
            Result64 = DayOfYear64(Date64)
            If Result <> Result64
              Debug "DayOfYear() <> DayOfYear64()"
              Debug Result
              Debug Result64
              Debug ""
            EndIf
            
            ;Next Second
          ;Next Minute
        Next Hour
      Next Day
    Next Month
  Next Year
  
  If Date() <> Date64()
    Debug "Date() <> Date64()"
  EndIf
  
  Macro AddDateTest(Type, TypeS)
    If AddDate(Date(), #PB_Date_#Type, 1) <> AddDate64(Date64(), #PB_Date_#Type, 1)
      Debug "AddDate(Date(), #PB_Date_" + TypeS + ", 1) <> AddDate64(Date64(), #PB_Date_" + TypeS + ", 1)"
    EndIf
    If AddDate(Date(), #PB_Date_#Type, -1) <> AddDate64(Date64(), #PB_Date_#Type, -1)
      Debug "AddDate(Date(), #PB_Date_" + TypeS + ", -1) <> AddDate64(Date64(), #PB_Date_" + TypeS + ", -1)"
    EndIf
  EndMacro
  
  AddDateTest(Year,   "Year")
  AddDateTest(Month,  "Month")
  AddDateTest(Day,    "Day")
  AddDateTest(Hour,   "Hour")
  AddDateTest(Minute, "Minute")
  AddDateTest(Second, "Second")
  AddDateTest(Week,   "Week")
  
  Macro TestDateLimits(Minimum, Maximum)
    Date64$ = Minimum
    Date64 = ParseDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64$)
    Result64$ = FormatDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64)
    If Date64$ <> Result64$
      Debug "Minimum stimmt nicht:"
      Debug "> Erwartet wurde: " + Date64$
      Debug "> Zurückgegeben wurde: " + Result64$
    EndIf
    
    Date64$ = Maximum
    Date64 = ParseDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64$)
    Result64$ = FormatDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64)
    If Date64$ <> Result64$
      Debug "Maximum stimmt nicht:"
      Debug "> Erwartet wurde: " + Date64$
      Debug "> Zurückgegeben wurde: " + Result64$
    EndIf
  EndMacro
  
  Debug "---------------------"
  Debug "Test der Datum-Grenzen - Fehler:"
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    TestDateLimits("01.01.1601 00:00:00", "31.12.9999 23:59:59")
  CompilerElse ; Linux oder Mac
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
      TestDateLimits("01.01.1902 00:00:00", "18.01.2038 23:59:59")
    CompilerElse
      TestDateLimits("01.01.0000 00:00:00", "31.12.9999 23:59:59")
    CompilerEndIf
  CompilerEndIf
  
  Debug "---------------------"
  Debug "Test wurde durchgeführt"
CompilerEndIf
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
mk-soft
Beiträge: 3695
Registriert: 24.11.2004 13:12
Wohnort: Germany

Re: Date64 - Unixtime 64bit

Beitrag von mk-soft »

Läuft nicht unter Mac
Undefined symbols for architecture x86_64:
"gmtime_r", referenced from:
_Procedure16.ClearLoop in purebasic.o
_Procedure8.ClearLoop in purebasic.o
_Procedure10.ClearLoop in purebasic.o
_Procedure12.ClearLoop in purebasic.o
_Procedure18.ClearLoop in purebasic.o
...
clang: error: linker command failed with exit code 1 (use -v to see invocation)
:cry:
Alles ist möglich, fragt sich nur wie...
Projekte ThreadToGUI / EventDesigner V3 / OOP-BaseClass-Modul
Downloads auf MyWebspace / OneDrive
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Date64 - Unixtime 64bit

Beitrag von Sicro »

gmtime_r() müsste es aber eigentlich unter Mac geben:
https://developer.apple.com/search/?q=gmtime_r

Vielleicht hilft das:

Code: Alles auswählen

ImportC "-lc"
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Date64 - Unixtime 64bit

Beitrag von Sicro »

Code im CodeArchiv unter Time_and_Date/Date64[WIN,LIN].pbi aktualisiert.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Lord
Beiträge: 313
Registriert: 21.01.2008 19:11

Re: Date64 - Unixtime 64bit

Beitrag von Lord »

Hallo!

Ich möchte doch darauf hinweisen, daß das Modul so nicht ohne weiteres
grundsätzlich einsetzbar ist.
So ist zum Beispiel im Programm definiert:

Code: Alles auswählen

#HundredNanosecondsFrom_1Jan1601_To_1Jan1970 = 116444736000000000
Dieser Wert ist nur teilweise gültig.
Es wird nicht berücksichtigt, daß in großen Teilen Deutschlands/Europas/der Welt
der Gregorianische Kalender zu unterschiedlichen Zeiten umgesetzt wurde.
Es muß somit immer der Ort, für den eine Zeitrechnung durcheführt werden
soll, berücksichtigt werden.
Für den Zeitraum 1601 bis 1700 fehlen in vielen Gebieten die Tage 19.02 bis
28.02.1700, weil die Umstellung erst da erfolgte.
In der VR China erfolgte die Umstellung erst 1949, im KR Griechenland erst 1923,
in der UdSSR erst 1922, ... usw.
So gesehen ist diese Routine so nicht brauchbar, da sie fehlerhafte Ergebnisse
liefern kann.
Es müßte also ein gebietsspezifischer Korrekturwert eingeführt werden.
Bild
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Date64 - Unixtime 64bit

Beitrag von Sicro »

Hallo Lord,

vielen Dank für den Hinweis.
Sobald ich genug Zeit habe, werde ich mir das mal ansehen und versuchen, es im Code umzusetzen.

Auf die Schnelle habe ich folgende Seite für die unterschiedlichen Einführungszeiten gefunden:
http://www.kalenderlexikon.de/anzeigen. ... gGregorKal
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Date64 - Unixtime 64bit

Beitrag von Sicro »

Ich habe nun "gmtime_r" wieder zu "gmtime" geändert, um die Unterstützung für MacOS wiederherzustellen.
Der minimale Datumsbereich bei MacOS wurde ebenfalls korrigiert.

https://github.com/SicroAtGit/PureBasic ... Mac%5D.pbi

Code: Alles auswählen

;   Description: Support for enlarged date range (64 bit unix timestamp)
;        Author: mk-soft (Windows); Sicro (Windows, Linux, Mac; converted to module; and more); ts-soft (fixed structures for Windows, Linux and Mac)
;          Date: 2017-07-30
;            OS: Windows, Linux, Mac
; English-Forum: 
;  French-Forum: 
;  German-Forum: http://www.purebasic.fr/german/viewtopic.php?p=335727#p335727
; -----------------------------------------------------------------------------

DeclareModule Date64
  Declare.i IsLeapYear64(Year.i)
  Declare.i DaysInMonth64(Year.i, Month.i)
  Declare.q Date64(Year.i=-1, Month.i=1, Day.i=1, Hour.i=0, Minute.i=0, Second.i=0)
  Declare.i Year64(Date.q)
  Declare.i Month64(Date.q)
  Declare.i Day64(Date.q)
  Declare.i Hour64(Date.q)
  Declare.i Minute64(Date.q)
  Declare.i Second64(Date.q)
  Declare.i DayOfWeek64(Date.q)
  Declare.i DayOfYear64(Date.q)
  Declare.q AddDate64(Date.q, Type.i, Value.i)
  Declare.s FormatDate64(Mask$, Date.q)
  Declare.q ParseDate64(Mask$, Date$)
EndDeclareModule

Module Date64
  EnableExplicit

  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS And #PB_Compiler_Processor = #PB_Processor_x86
    CompilerError "32-Bit not supported on MacOS"
  CompilerEndIf
  
  ; !!! >>> WARNUNG <<< !!!
  ; Der gregorianische Kalender wurde in vielen Gebieten zu unterschiedlichen Zeiten eingeführt.
  ; Dieses Modul verwendet die API-Datumsfunktionen des Betriebssystems und diese haben eine
  ; vereinheitlichte Einführungszeit einprogrammiert, wodurch Datumsberechnungen vor Einführung
  ; des gregorianischen Kalenders meistens falsche Ergebnisse liefern.

  ; == Windows ==
  ; >> Minimum: 01.01.1601 00:00:00
  ; >> Maximum: 31.12.9999 23:59:59

  ; == Linux ==
  ; 32-Bit:
  ; >> Minimum: 01.01.1902 00:00:00
  ; >> Maximum: 18.01.2038 23:59:59
  ; 64-Bit:
  ; >> Minimum: 01.01.0000 00:00:00
  ; >> Maximum: 31.12.9999 23:59:59

  ; == MacOS ==
  ; >> Minimum: 31.12.1969 23:59:59
  ; >> Maximum: 31.12.9999 23:59:59

  #SecondsInOneHour = 60 * 60
  #SecondsInOneDay  = #SecondsInOneHour * 24

  #HundredNanosecondsInOneSecond               = 10000000
  #HundredNanosecondsFrom_1Jan1601_To_1Jan1970 = 116444736000000000

  ;{ Struktur-Definition für "tm"
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Linux
      If Not Defined(tm, #PB_Structure)
        Structure tm Align #PB_Structure_AlignC
          tm_sec.l    ; 0 bis 59 oder bis 60 bei Schaltsekunde
          tm_min.l    ; 0 bis 59
          tm_hour.l   ; 0 bis 23
          tm_mday.l   ; Tag des Monats: 1 bis 31
          tm_mon.l    ; Monat: 0 bis 11 (Monate seit Januar)
          tm_year.l   ; Anzahl der Jahre seit dem Jahr 1900
          tm_wday.l   ; Wochentag: 0 bis 6, 0 = Sonntag
          tm_yday.l   ; Tage seit Jahresanfang: 0 bis 365 (365 ist also 366, da nach 1. Januar gezählt wird)
          tm_isdst.l  ; Ist Sommerzeit? tm_isdst > 0 = Ja
                      ;                             tm_isdst = 0 = Nein
                      ;                             tm_isdst < 0 = Unbekannt
          CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
            tm_gmtoff.l ; Offset von UTC in Sekunden
            *tm_zone    ; Abkürzungsname der Zeitzone
          CompilerElse
            tm_zone.l   ; Platzhalter
            tm_gmtoff.l ; Offset von UTC in Sekunden
            *tm_zone64  ; Abkürzungsname der Zeitzone
          CompilerEndIf

        EndStructure
      EndIf
    CompilerCase #PB_OS_MacOS
      If Not Defined(tm, #PB_Structure)
        Structure tm Align #PB_Structure_AlignC
          tm_sec.l    ; 0 bis 59 oder bis 60 bei Schaltsekunde
          tm_min.l    ; 0 bis 59
          tm_hour.l   ; 0 bis 23
          tm_mday.l   ; Tag des Monats: 1 bis 31
          tm_mon.l    ; Monat: 0 bis 11 (Monate seit Januar)
          tm_year.l   ; Anzahl der Jahre seit dem Jahr 1900
          tm_wday.l   ; Wochentag: 0 bis 6, 0 = Sonntag
          tm_yday.l   ; Tage seit Jahresanfang: 0 bis 365 (365 ist also 366, da nach 1. Januar gezählt wird)
          tm_isdst.l  ; Ist Sommerzeit? tm_isdst > 0 = Ja
                      ;                             tm_isdst = 0 = Nein
                      ;                             tm_isdst < 0 = Unbekannt
          tm_zone.l   ; Abkürzungsname der Zeitzone (Auch bei 64bit ein 32bit Wert)
          tm_gmtoff.l ; Offset von UTC in Sekunden
          *tm_zone64  ; Abkürzungsname der Zeitzone
        EndStructure
      EndIf
  CompilerEndSelect
  ;}

  Procedure.i IsLeapYear64(Year.i)
    If Year < 1600
      ; vor dem Jahr 1600 sind alle Jahre Schaltjahre, die durch 4 restlos teilbar sind
      ProcedureReturn Bool(Year % 4 = 0)
    Else
      ; ab dem Jahr 1600 sind alle Jahre Schaltjahre, die folgende Bedingungen erfüllen:
      ; => restlos durch 4 teilbar, jedoch nicht restlos durch 100 teilbar
      ; => restlos durch 400 teilbar
      ProcedureReturn Bool((Year % 4 = 0 And Year % 100 <> 0) Or Year % 400 = 0)
    EndIf
  EndProcedure

  Procedure.i DaysInMonth64(Year.i, Month.i)
    While Month > 12
      Year  + 1
      Month - 12
    Wend
    While Month < 0
      Year  - 1
      Month + 13
    Wend
    If Month = 0
      Month = 1
    EndIf

    Select Month
      Case 1, 3, 5, 7, 8, 10, 12: ProcedureReturn 31
      Case 4, 6, 9, 11:           ProcedureReturn 30
      Case 2:                     ProcedureReturn 28 + IsLeapYear64(Year) ; Februar hat im Schaltjahr ein Tag mehr
    EndSelect
  EndProcedure

  Procedure.q Date64(Year.i=-1, Month.i=1, Day.i=1, Hour.i=0, Minute.i=0, Second.i=0)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Protected.SYSTEMTIME st
      Protected.FILETIME   ft, ft2
      Protected.i          DaysInMonth

      If Year > -1 ; Gültiges Datum

        ; Angaben evtl. korrigieren

        Minute + Second/60
        Second % 60
        If Second < 0
          Minute - 1
          Second + 60
        EndIf

        Hour + Minute/60
        Minute % 60
        If Minute < 0
          Hour   - 1
          Minute + 60
        EndIf

        Day + Hour/24
        Hour % 24
        If Hour < 0
          Day  - 1
          Hour + 24
        EndIf

        While Month > 12
          Year  + 1
          Month - 12
        Wend
        If Month = 0
          Month = 1
        EndIf

        DaysInMonth = DaysInMonth64(Year, Month)
        While Day > DaysInMonth
          Day - DaysInMonth
          Month + 1
          If Month > 12
            Year  + 1
            Month - 12
          EndIf
          DaysInMonth = DaysInMonth64(Year, Month)
        Wend

        If Day < 0
          Month - 1
          If Month = 0
            Year  - 1
            Month = 12
          EndIf
          Day + DaysInMonth64(Year, Month)
        EndIf

        st\wYear   = Year
        st\wMonth  = Month
        st\wDay    = Day
        st\wHour   = Hour
        st\wMinute = Minute
        st\wSecond = Second

        ; Konvertiert Systemzeit (UTC) zu Dateizeit (UTC)
        SystemTimeToFileTime_(@st, @ft)

        ; UTC-Zeit in Sekunden umrechnen
        ProcedureReturn (PeekQ(@ft) - #HundredNanosecondsFrom_1Jan1601_To_1Jan1970) / #HundredNanosecondsInOneSecond
      Else
        ; Kein gültiges Datum. Lokale Systemzeit wird ermittelt
        GetLocalTime_(@st)
        SystemTimeToFileTime_(@st, @ft) ; "st" wird als UTC gelesen und zu Dateizeit konvertiert

        ; UTC-Zeit in Sekunden umrechnen
        ProcedureReturn (PeekQ(@ft) - #HundredNanosecondsFrom_1Jan1601_To_1Jan1970) / #HundredNanosecondsInOneSecond
      EndIf
    CompilerElse ; Linux oder Mac
      Protected.tm tm
      Protected.q time

      If Year > -1 ; Gültiges Datum
        tm\tm_year  = Year - 1900 ; Jahre ab 1900
        tm\tm_mon   = Month - 1   ; Monate ab Januar
        tm\tm_mday  = Day
        tm\tm_hour  = Hour
        tm\tm_min   = Minute
        tm\tm_sec   = Second

        ; mktime korrigiert die Angaben selber und liefert bereits Sekunden
        time = timegm_(@tm) ; Konvertiert UTC-Zeit zu UTC-Zeit

        ProcedureReturn time ; UTC-Zeit in Sekunden
      Else
        ; Kein gültiges Datum. Systemzeit wird ermittelt
        time = time_(0)
        If localtime_r_(@time, @tm) <> 0
          time = timegm_(@tm)
        EndIf

        ProcedureReturn time  ; UTC-Zeit in Sekunden
      EndIf
    CompilerEndIf
  EndProcedure

  Macro Windows_ReturnDatePart(Type)
    Protected.SYSTEMTIME st

    Date = Date * #HundredNanosecondsInOneSecond + #HundredNanosecondsFrom_1Jan1601_To_1Jan1970
    FileTimeToSystemTime_(@Date, @st)

    ProcedureReturn st\Type
  EndMacro

  Macro LinuxMac_ReturnDatePart(Type, ReturnCode)
    Protected.tm *tm
    Protected.i  Value

    *tm = gmtime_(@Date)
    If *tm
      Value = *tm\Type
    EndIf
    ProcedureReturn ReturnCode
  EndMacro

  Procedure.i Year64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wYear)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_year, Value + 1900)
    CompilerEndIf
  EndProcedure

  Procedure.i Month64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wMonth)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_mon, Value + 1)
    CompilerEndIf
  EndProcedure

  Procedure.i Day64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wDay)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_mday, Value)
    CompilerEndIf
  EndProcedure

  Procedure.i Hour64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wHour)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_hour, Value)
    CompilerEndIf
  EndProcedure

  Procedure.i Minute64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wMinute)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_min, Value)
    CompilerEndIf
  EndProcedure

  Procedure.i Second64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wSecond)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_sec, Value)
    CompilerEndIf
  EndProcedure

  Procedure.i DayOfWeek64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Windows_ReturnDatePart(wDayOfWeek)
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_wday, Value)
    CompilerEndIf
  EndProcedure

  Procedure.i DayOfYear64(Date.q)
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      Protected.q TempDate

      TempDate = Date64(Year64(Date))
      ProcedureReturn (Date - TempDate) / #SecondsInOneDay + 1
    CompilerElse ; Linux oder Mac
      LinuxMac_ReturnDatePart(tm_yday, Value + 1)
    CompilerEndIf
  EndProcedure

  Procedure.q AddDate64(Date.q, Type.i, Value.i)
    Protected.i Day, Month, Year

    Select Type
      Case #PB_Date_Year:   ProcedureReturn Date64(Year64(Date) + Value, Month64(Date), Day64(Date), Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Month
        Day   = Day64(Date)
        Month = Month64(Date) + Value
        Year  = Year64(Date)

        If Day > DaysInMonth64(Year, Month)
          ; mktime korrigiert das zwar auch, wendet dabei aber eine andere Methode als PB-AddDate an:
          ; >> mktime:     31.03.2004 => 1 Monat später => 01.05.2004
          ; >> PB-AddDate: 31.03.2004 => 1 Monat später => 30.04.2004

          ; Setzte Tag auf das Maximum des neuen Monats
          Day = DaysInMonth64(Year, Month)
        EndIf

        ProcedureReturn Date64(Year64(Date), Month, Day, Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Week:   ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date) + Value * 7, Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Day:    ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date) + Value, Hour64(Date), Minute64(Date), Second64(Date))
      Case #PB_Date_Hour:   ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date), Hour64(Date) + Value, Minute64(Date), Second64(Date))
      Case #PB_Date_Minute: ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date), Hour64(Date), Minute64(Date) + Value, Second64(Date))
      Case #PB_Date_Second: ProcedureReturn Date64(Year64(Date), Month64(Date), Day64(Date), Hour64(Date), Minute64(Date), Second64(Date) + Value)
    EndSelect
  EndProcedure

  Procedure.s FormatDate64(Mask$, Date.q)
    Protected Result$

    Result$ = ReplaceString(Mask$,   "%yyyy", RSet(Str(Year64(Date)),           4, "0"))
    Result$ = ReplaceString(Result$, "%yy",   RSet(Right(Str(Year64(Date)), 2), 2, "0"))
    Result$ = ReplaceString(Result$, "%mm",   RSet(Str(Month64(Date)),          2, "0"))
    Result$ = ReplaceString(Result$, "%dd",   RSet(Str(Day64(Date)),            2, "0"))
    Result$ = ReplaceString(Result$, "%hh",   RSet(Str(Hour64(Date)),           2, "0"))
    Result$ = ReplaceString(Result$, "%ii",   RSet(Str(Minute64(Date)),         2, "0"))
    Result$ = ReplaceString(Result$, "%ss",   RSet(Str(Second64(Date)),         2, "0"))

    ProcedureReturn Result$
  EndProcedure

  Macro ReadMaskVariable(MaskVariable, ReturnVariable)
    If Mid(Mask$, i, 3) = MaskVariable
      IsVariableFound = #True
      ReturnVariable = Val(Mid(Date$, DatePos, 2))
      DatePos + 2 ; Die 2 Nummern der Zahl überspringen
      i + 2       ; Die 3 Zeichen der Variable überspringen
      Continue
    EndIf
  EndMacro

  Procedure.q ParseDate64(Mask$, Date$)
    Protected.i i, DatePos = 1, IsVariableFound, Year, Month = 1, Day = 1, Hour, Minute, Second
    Protected MaskChar$, DateChar$

    For i = 1 To Len(Mask$)
      MaskChar$ = Mid(Mask$, i, 1)
      DateChar$ = Mid(Date$, DatePos, 1)

      If MaskChar$ <> DateChar$
        If MaskChar$ = "%" ; Vielleicht eine Variable?
          If Mid(Mask$, i, 5) = "%yyyy"
            IsVariableFound = #True
            Year = Val(Mid(Date$, DatePos, 4))
            DatePos + 4 ; Die 4 Nummern der Jahreszahl überspringen
            i + 4       ; Die 5 Zeichen der Variable "%yyyy" überspringen
            Continue
          ElseIf Mid(Mask$, i, 3) = "%yy"
            IsVariableFound = #True
            Year = Val(Mid(Date$, DatePos, 2))
            DatePos + 2 ; Die 2 Nummern der Jahreszahl überspringen
            i + 2       ; Die 3 Zeichen der Variable "%yy" überspringen
            Continue
          EndIf

          ReadMaskVariable("%mm", Month)
          ReadMaskVariable("%dd", Day)
          ReadMaskVariable("%hh", Hour)
          ReadMaskVariable("%ii", Minute)
          ReadMaskVariable("%ss", Second)

          If Not IsVariableFound
            ProcedureReturn 0
          EndIf
        Else
          ProcedureReturn 0
        EndIf
      EndIf

      DatePos + 1
    Next

    ProcedureReturn Date64(Year, Month, Day, Hour, Minute, Second)
  EndProcedure
EndModule

;-Example
CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
  
  ;-Test
  
  UseModule Date64
  
  Define.i Year, Month, Day, Hour, Minute, Second, Result, Result64
  Define.q Date, Date64
  Define   Date$, Date64$, Result64$
  
  Debug "Kleiner Kompatibilitäts-Test - Fehler:"
  For Year = 1970 To 2037
    For Month = 1 To 12
      For Day = 1 To 28
        For Hour = 0 To 23
          ;For Minute = 0 To 59
          ;For Second = 0 To 59
          Date = Date(Year, Month, Day, Hour, Minute, Second)
          Date64 = Date64(Year, Month, Day, Hour, Minute, Second)
          
          If Date <> Date64
            Debug "Date() <> Date64()"
            Debug Date
            Debug Date64
            Debug ""
          EndIf
          
          Date$ = FormatDate("%yyyy.%mm.%dd %hh:%ii:%ss", Date)
          Date64$ = FormatDate64("%yyyy.%mm.%dd %hh:%ii:%ss", Date64)
          If Date$ <> Date64$
            Debug "FormatDate() <> FormatDate64()"
            Debug Date$
            Debug Date64$
            Debug ""
          EndIf
          
          Result = ParseDate("%yyyy.%mm.%dd %hh:%ii:%ss", Date$)
          Result64 = ParseDate64("%yyyy.%mm.%dd %hh:%ii:%ss", Date64$)
          If Result <> Result64
            Debug "ParseDate() <> ParseDate64()"
            Debug Result
            Debug Result64
            Debug ""
          EndIf
          
          Result = DayOfWeek(Date)
          Result64 = DayOfWeek64(Date64)
          If Result <> Result64
            Debug "DayOfWeek() <> DayOfWeek64()"
            Debug Result
            Debug Result64
            Debug ""
          EndIf
          
          Result = DayOfYear(Date)
          Result64 = DayOfYear64(Date64)
          If Result <> Result64
            Debug "DayOfYear() <> DayOfYear64()"
            Debug Result
            Debug Result64
            Debug ""
          EndIf
          
          ;Next Second
          ;Next Minute
        Next Hour
      Next Day
    Next Month
  Next Year
  
  If Date() <> Date64()
    Debug "Date() <> Date64()"
  EndIf
  
  Macro AddDateTest(Type, TypeS)
    If AddDate(Date(), #PB_Date_#Type, 1) <> AddDate64(Date64(), #PB_Date_#Type, 1)
      Debug "AddDate(Date(), #PB_Date_" + TypeS + ", 1) <> AddDate64(Date64(), #PB_Date_" + TypeS + ", 1)"
    EndIf
    If AddDate(Date(), #PB_Date_#Type, -1) <> AddDate64(Date64(), #PB_Date_#Type, -1)
      Debug "AddDate(Date(), #PB_Date_" + TypeS + ", -1) <> AddDate64(Date64(), #PB_Date_" + TypeS + ", -1)"
    EndIf
  EndMacro
  
  AddDateTest(Year,   "Year")
  AddDateTest(Month,  "Month")
  AddDateTest(Day,    "Day")
  AddDateTest(Hour,   "Hour")
  AddDateTest(Minute, "Minute")
  AddDateTest(Second, "Second")
  AddDateTest(Week,   "Week")
  
  Macro TestDateLimits(Minimum, Maximum)
    Date64$ = Minimum
    Date64 = ParseDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64$)
    Result64$ = FormatDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64)
    If Date64$ <> Result64$
      Debug "Minimum stimmt nicht:"
      Debug "> Erwartet wurde: " + Date64$
      Debug "> Zurückgegeben wurde: " + Result64$
    EndIf
    
    Date64$ = Maximum
    Date64 = ParseDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64$)
    Result64$ = FormatDate64("%dd.%mm.%yyyy %hh:%ii:%ss", Date64)
    If Date64$ <> Result64$
      Debug "Maximum stimmt nicht:"
      Debug "> Erwartet wurde: " + Date64$
      Debug "> Zurückgegeben wurde: " + Result64$
    EndIf
  EndMacro
  
  Debug "---------------------"
  Debug "Test der Datum-Grenzen - Fehler:"
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      TestDateLimits("01.01.1601 00:00:00", "31.12.9999 23:59:59")
    CompilerCase #PB_OS_Linux
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        TestDateLimits("01.01.1902 00:00:00", "18.01.2038 23:59:59")
      CompilerElse
        TestDateLimits("01.01.0000 00:00:00", "31.12.9999 23:59:59")
      CompilerEndIf
    CompilerCase #PB_OS_MacOS
      TestDateLimits("31.12.1969 23:59:59", "31.12.9999 23:59:59")
  CompilerEndSelect
  
  Debug "---------------------"
  Debug "Test wurde durchgeführt"
CompilerEndIf
Edit: Warnhinweis für Datumsberechnungen vor dem gregorianischen Kalender zum Code hinzugefügt
Zuletzt geändert von Sicro am 30.07.2017 19:43, insgesamt 1-mal geändert.
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
Lord
Beiträge: 313
Registriert: 21.01.2008 19:11

Re: Date64 - Unixtime 64bit

Beitrag von Lord »

Hallo Sicro!

Darf ich darauf hinweisen, daß
Debug DaysInMonth64(1700, 2)
weiterhin für große Teile Deutschlands/Europas/der Welt fehlerhafte Werte liefert?

Danke!
Bild
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Date64 - Unixtime 64bit

Beitrag von Nino »

Lord hat geschrieben:Es wird nicht berücksichtigt, daß in großen Teilen Deutschlands/Europas/der Welt
der Gregorianische Kalender zu unterschiedlichen Zeiten umgesetzt wurde.
Es muß somit immer der Ort, für den eine Zeitrechnung durcheführt werden
soll, berücksichtigt werden.
Für den Zeitraum 1601 bis 1700 fehlen in vielen Gebieten die Tage 19.02 bis
28.02.1700, weil die Umstellung erst da erfolgte.
In der VR China erfolgte die Umstellung erst 1949, im KR Griechenland erst 1923,
in der UdSSR erst 1922, ... usw.
So gesehen ist diese Routine so nicht brauchbar, da sie fehlerhafte Ergebnisse
liefern kann.
Es müßte also ein gebietsspezifischer Korrekturwert eingeführt werden.
Und mancherorts ist der Gregorianisache Kalender wahrscheinlich bis heute nicht eingeführt ...
Dieser Code stellt Berechnungen mit dem Gregorianischen Kalender an. Derjenige, der dieses Modul in einem Programm verwendet, ist letztlich selbst dafür verantwortlich dass Berechnungen im Zusammenhang seines Programms sinnvoll sind.

Sicher wäre so eine Liste o. Ä. wie du vorschlägst hilfreich, aber viele Benutzer des Moduls brauchen das gar nicht, da für die Zeitberechnungen die sie durchführen immer der Gregorianische Kalender gilt.
Diejenigen welche die genannten zusätzlichen Informationen benötigen, müssen sich momentan halt selbst darum bemühen und Zeitdifferenzen u. Ä. ggf. entsprechend korrigieren.
Antworten