MacOS supports the date as timeIntervalSince1970 as a double in seconds.
Windows supports the date as VariantTime as a double in days.
This can be converted well with an offset and factor, as well as conversion from and to SystemTime.
The features also include functions for get and set the CalendarGadget.
Update v1.02.0
- Added Linux
Update v1.03.0
- Change Linux to API (Date limits on x86 ca. 1902 to 2038)
Update v2.01.0
- Added: GetDateYear, GetDateMonth, etc
Update v2.02.1
- Added: GetDateFromValue(Year, ...)
- Change: GetDateNow remove parameter local.
- Change: GetStringFromDate parameter order. Local as last parameter.
- Change: GetDateFromString parameter order. Local as last parameter.
- Bugfix: Get/SetCalendarDate to valid local date.
- Fix: Calculation of date double at windows. Rounding error from windows to the result from macOS or Linux result.
Update v2.02.2
- Update Set/GetCalendarDate
Update v2.02.4
- Bugfix Linux SetCalendarDate
Update v2.03.1
- Added: FormatString with Milliseconds ("%hh:%ii:%ss.%nnn")
- Fix Linux: GetDateNow() with Milliseconds
Modul_DateTime.pb
Code: Select all
;-TOP
; Comment : Module DateTime, Date as Double UTC (Big Date Range)
; Author  : mk-soft
; Version : v2.03.1
; create  : 27.03.2021
; Update  : 19.11.2022
;
; OS      : Window, MacOS, Linux
; Link    : https://www.purebasic.fr/english/viewtopic.php?f=12&t=76983
; *******
DeclareModule DateTime
  
  Declare.s GetStringFromDate(Date.d, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
  Declare.d GetDateFromString(Date.s, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
  Declare.d GetDateFromValue(Year, Month, Day, Hour, Minute, Second, Local = #True)
  Declare.d GetDateNow()
  Declare   GetSecondsFromGMT()
  
  Declare   GetDateYear(Date.d, Local = #True)
  Declare   GetDateMonth(Date.d, Local = #True)
  Declare   GetDateDay(Date.d, Local = #True)
  Declare   GetDateHour(Date.d, Local = #True)
  Declare   GetDateMinute(Date.d, Local = #True)
  Declare   GetDateSecond(Date.d, Local = #True)
  Declare   GetDateDayOfWeek(Date.d, Local = #True)
      
  Declare.d GetCalendarDate(Gadget)
  Declare   SetCalendarDate(Gadget, Date.d)
  
EndDeclareModule
Module DateTime
  
  EnableExplicit
  
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_MacOS
      
      ;- OS MacOS
      
      ; Mask:
      ;   Year   = yyyy
      ;   Month  = MM (1..12), MMM (Short Name), MMMM (Long Name)
      ;   Day    = dd
      ;   Hour   = HH (0..23), hh (0..11); a = period (AM, PM)
      ;   Minute = mm
      ;   Second = ss
      ;   
      ;   Day ot the year   = DDD (1..3)
      ;   Week of Year      = ww
      ;   Week of Month     = W
      
      ; ----
      
      #kCFCalendarUnitEra = (1 << 1)
      #kCFCalendarUnitYear = (1 << 2) 
      #kCFCalendarUnitMonth = (1 << 3) 
      #kCFCalendarUnitDay = (1 << 4)
      #kCFCalendarUnitHour = (1 << 5)
      #kCFCalendarUnitMinute = (1 << 6)
      #kCFCalendarUnitSecond = (1 << 7)
      #kCFCalendarUnitWeek = (1 << 8)
      #kCFCalendarUnitWeekDay = (1 << 9)
      #kCFCalendarUnitWeekDayOrginal = (1 << 10)
      
      #NSCalendarUnitEra = #kCFCalendarUnitEra
      #NSCalendarUnitYear = #kCFCalendarUnitYear
      #NSCalendarUnitMonth = #kCFCalendarUnitMonth
      #NSCalendarUnitDay = #kCFCalendarUnitDay
      #NSCalendarUnitHour = #kCFCalendarUnitHour
      #NSCalendarUnitMinute = #kCFCalendarUnitMinute
      #NSCalendarUnitSecond = #kCFCalendarUnitSecond
      #NSCalendarUnitWeek = #kCFCalendarUnitWeek
      #NSCalendarUnitWeekDay = #kCFCalendarUnitWeekDay
      #NSCalendarUnitWeekDayOrdinal = #kCFCalendarUnitWeekDayOrginal
      
      ; -- Intern
      
      Macro CocoaString(NSString)
        PeekS(CocoaMessage(0, NSString, "UTF8String"), -1, #PB_UTF8)
      EndMacro
      
      ; ----
      
      Procedure GetDateComponent(Component, Date.d, Local = #True)
        Protected r1, NSPool, NSDate, NSDate2, NSCalendar, NSTimeZone, NSDateComponents
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        
        NSDate = CocoaMessage(0, 0, "NSDate new")
        NSDate2 = CocoaMessage(0, NSDate, "initWithTimeIntervalSince1970:@", @Date)
        
        NSCalendar = CocoaMessage(0, 0, "NSCalendar currentCalendar")
        If Not Local
          NSTimeZone = CocoaMessage(0, 0, "NSTimeZone timeZoneWithName:$", @"UTC")
          CocoaMessage(0, NSCalendar, "setTimeZone:", NSTimeZone)
        EndIf
        Select Component
          Case #PB_Date_Year
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitYear, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "year")
          Case #PB_Date_Month
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitMonth, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "month")
          Case #PB_Date_Day
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitDay, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "day")
          Case #PB_Date_Hour
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitHour, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "hour")
          Case #PB_Date_Minute
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitMinute, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "minute")
          Case #PB_Date_Second
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitSecond, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "second")
          Case #PB_Date_Week
            NSDateComponents = CocoaMessage(0, NSCalendar, "components:", #NSCalendarUnitWeekDay, "fromDate:", NSDate2)
            r1 = CocoaMessage(0, NSDateComponents, "weekday") - 1
        EndSelect
        
        CocoaMessage(0, NSPool, "release")
        
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      ;-- Date functions
      
      Procedure.s GetStringFromDate(Date.d, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
        Protected NSPool, NSDate, NSDate2, NSTimeZone, NSDateFormatter, NSString
        Protected r1.s
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        
        NSDateFormatter = CocoaMessage(0, 0, "NSDateFormatter new")
        ; Convert PB date format
        If Mask = ""
          Mask = "%yyyy-%mm-%dd %hh:%ii:%ss"
        EndIf
        If FindString(Mask, "%")
          mask = LCase(mask)
          mask = ReplaceString(mask, "m", "M")
          mask = ReplaceString(mask, "i", "m")
          mask = ReplaceString(mask, "h", "H")
          mask = ReplaceString(mask, "n", "S")
          mask = RemoveString(mask, "%")
        EndIf
        NSDate = CocoaMessage(0, 0, "NSDate new")
        NSDate2 = CocoaMessage(0, NSDate, "initWithTimeIntervalSince1970:@", @Date)
        If Not Local
          NSTimeZone = CocoaMessage(0, 0, "NSTimeZone timeZoneWithName:$", @"UTC")
          CocoaMessage(0, NSDateFormatter, "setTimeZone:", NSTimeZone)
        EndIf
        CocoaMessage(0, NSDateFormatter, "setDateFormat:$", @Mask)
        NSString = CocoaMessage(0, NSDateFormatter, "stringFromDate:@", @NSDate2)
        r1 = CocoaString(NSString)
        
        CocoaMessage(0, NSDateFormatter, "release")
        CocoaMessage(0, NSPool, "release")
        
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.d GetDateFromString(Date.s, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
        Protected NSPool, NSDate, NSTimeZone, NSDateFormatter
        Protected r1.d
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        
        NSDateFormatter = CocoaMessage(0, 0, "NSDateFormatter new")
        ; Convert PB date format
        If Mask = ""
          Mask = "%yyyy-%mm-%dd %hh:%ii:%ss"
        EndIf
        If FindString(Mask, "%")
          mask = LCase(mask)
          mask = ReplaceString(mask, "m", "M")
          mask = ReplaceString(mask, "i", "m")
          mask = ReplaceString(mask, "h", "H")
          mask = ReplaceString(mask, "n", "S")
          mask = RemoveString(mask, "%")
        EndIf
        ; Date from string
        If Not Local
          NSTimeZone = CocoaMessage(0, 0, "NSTimeZone timeZoneWithName:$", @"UTC")
          CocoaMessage(0, NSDateFormatter, "setTimeZone:", NSTimeZone)
        EndIf
        CocoaMessage(0, NSDateFormatter, "setDateFormat:$", @Mask)
        NSDate = CocoaMessage(0, NSDateFormatter, "dateFromString:$", @Date)
        CocoaMessage(@r1, NSDate, "timeIntervalSince1970")
        CocoaMessage(0, NSDateFormatter, "release")
        
        CocoaMessage(0, NSPool, "release")
        
        ProcedureReturn r1
      EndProcedure
      
      ; -- Date function
      
      Procedure.d GetDateFromValue(Year, Month, Day, Hour, Minute, Second, Local = #True)
        Protected r1.d, NSPool, NSDate, NSCalendar, NSTimeZone, NSDateComponents
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        
        NSCalendar = CocoaMessage(0, 0, "NSCalendar currentCalendar")
        If Not Local
          NSTimeZone = CocoaMessage(0, 0, "NSTimeZone timeZoneWithName:$", @"UTC")
          CocoaMessage(0, NSCalendar, "setTimeZone:", NSTimeZone)
        EndIf
        
        NSDateComponents = CocoaMessage(0, 0, "NSDateComponents new")
        CocoaMessage(0, NSDateComponents, "setYear:", Year)
        CocoaMessage(0, NSDateComponents, "setMonth:", Month)
        CocoaMessage(0, NSDateComponents, "setDay:", Day)
        CocoaMessage(0, NSDateComponents, "setHour:", Hour)
        CocoaMessage(0, NSDateComponents, "setMinute:", Minute)
        CocoaMessage(0, NSDateComponents, "setSecond:", Second)
        NSDate = CocoaMessage(0, NSCalendar, "dateFromComponents:", NSDateComponents)
        CocoaMessage(@r1, NSDate, "timeIntervalSince1970")
        
        CocoaMessage(0, NSPool, "release")
        
        ProcedureReturn r1
      
      EndProcedure
      
      Procedure.d GetDateNow()
        Protected NSPool, NSDate
        Protected r1.d
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        NSDate = CocoaMessage(0, 0, "NSDate now")
        CocoaMessage(@r1, NSDate, "timeIntervalSince1970")
        CocoaMessage(0, NSPool, "release")
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure GetSecondsFromGMT()
        Protected NSPool, NSTimeZone
        Protected r1
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        NSTimeZone = CocoaMessage(0, 0, "NSTimeZone localTimeZone")
        r1 = CocoaMessage(0, NSTimeZone, "secondsFromGMT")
        CocoaMessage(0, NSPool, "release")
        ProcedureReturn r1
      EndProcedure
      
      ;-- Calendar functions
      
      Procedure.d GetCalendarDate(Gadget)
        Protected NSPool, NSDate
        Protected r1.d
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        If GadgetType(Gadget) = #PB_GadgetType_Calendar
          NSDate = CocoaMessage(0, GadgetID(Gadget), "dateValue")
          CocoaMessage(@r1, NSDate, "timeIntervalSince1970")
        EndIf
        CocoaMessage(0, NSPool, "release")
        
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure SetCalendarDate(Gadget, Date.d)
        Protected NSPool, NSDate, NSDate2
        
        NSPool = CocoaMessage(0, 0, "NSAutoreleasePool new")
        If GadgetType(Gadget) = #PB_GadgetType_Calendar
          NSDate = CocoaMessage(0, 0, "NSDate new")
          NSDate2 = CocoaMessage(0, NSDate, "initWithTimeIntervalSince1970:@", @Date)
          CocoaMessage(0, GadgetID(Gadget), "setDateValue:", NSDate2)
        EndIf  
        CocoaMessage(0, NSPool, "release")
        
      EndProcedure
      
      ; ----
      
    CompilerCase #PB_OS_Windows
      
      ;- OS Windows
      
      Import ""
        SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation, lpUniversalTime, lpLocaleTime)
        TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation, lpLocaleTime, lpUniversalTime)
      EndImport
      
      #DT_BASE_1970 = 25569.0
      
      ; -- Intern
      
      Procedure GetDateComponent(Component, Date.d, Local = #True)
        Protected r1, vTime.d 
        Static LastDate, Time.SystemTime
        
        If Date <> LastDate
          LastDate = Date
          ; Date to VariantTime
          vtime = (Date / 86400.0) + #DT_BASE_1970
          If Not VariantTimeToSystemTime_(vTime, @Time)
            ProcedureReturn -1
          EndIf
          ; UTC to Local
          If Local
            SystemTimeToTzSpecificLocalTime(0, Time, Time)
          EndIf
        EndIf
        Select Component
          Case #PB_Date_Year
            ProcedureReturn Time\wYear
          Case #PB_Date_Month
            ProcedureReturn Time\wMonth
          Case #PB_Date_Day
            ProcedureReturn Time\wDay
          Case #PB_Date_Hour
            ProcedureReturn Time\wHour
          Case #PB_Date_Minute
            ProcedureReturn Time\wMinute
          Case #PB_Date_Second
            ProcedureReturn Time\wSecond
          Case #PB_Date_Week
            ProcedureReturn Time\wDayOfWeek
        EndSelect
      EndProcedure
      
      ;-- Date functions
      
      Procedure.s GetStringFromDate(Date.d, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
        Protected r1.s, cnt, vTime.d, Time.SystemTime, milliseconds.d
        
        milliseconds = date - Round(date, #PB_Round_Down)
        ; Date to VariantTime
        vtime = ((Date - milliseconds) / 86400.0) + #DT_BASE_1970
        If Not VariantTimeToSystemTime_(vTime, @Time)
          ProcedureReturn ""
        EndIf
        ; UTC to Local
        If Local
          SystemTimeToTzSpecificLocalTime(0, Time, Time)
        EndIf
        ; SystemTime to String
        If Mask = ""
          Mask = "%yyyy-%mm-%dd %hh:%ii:%ss"
        EndIf
        mask = LCase(mask)
        cnt = CountString(mask, "y")
        r1 = RemoveString(Mask, "%")
        If cnt = 2
          r1 = ReplaceString(r1, "yy", RSet(Str(Time\wYear % 100), 4, "0"))
        Else
          r1 = ReplaceString(r1, "yyyy", RSet(Str(Time\wYear), 4, "0"))
        EndIf
        r1 = ReplaceString(r1, "mm", RSet(Str(Time\wMonth), 2, "0"))
        r1 = ReplaceString(r1, "dd", RSet(Str(Time\wDay),2, "0"))
        r1 = ReplaceString(r1, "hh", RSet(Str(Time\wHour),2, "0"))
        r1 = ReplaceString(r1, "ii", RSet(Str(Time\wMinute),2, "0"))
        r1 = ReplaceString(r1, "ss", RSet(Str(Time\wSecond),2, "0"))
        r1 = ReplaceString(r1, "nnn", Right(StrD(milliseconds, 3), 3))
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.d GetDateFromString(Date.s, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
        Protected r1.d, pos, cnt, vtime.d, Time.SystemTime, milliseconds.d
        
        ; String to SystemTime
        If Mask = ""
          Mask = "%yyyy-%mm-%dd %hh:%ii:%ss"
        EndIf
        mask = LCase(mask)
        mask = RemoveString(mask, "%")
        cnt = CountString(mask, "y")
        pos = FindString(mask, "y")
        If pos
          Time\wYear = Val(Mid(Date, pos, cnt))
        EndIf
        pos = FindString(mask, "m")
        If pos
          Time\wMonth = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "d")
        If pos
          Time\wDay = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "h")
        If pos
          Time\wHour = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "i")
        If pos
          Time\wMinute = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "s")
        If pos
          Time\wSecond = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "n")
        If pos
          milliseconds = ValD(Mid(Date, pos, 3))
        EndIf
        If Local
          TzSpecificLocalTimeToSystemTime(0, Time, Time)
        EndIf
        
        SystemTimeToVariantTime_(Time, @vTime)
        r1 = Round((vtime - #DT_BASE_1970) * 86400000.0 + milliseconds, #PB_Round_Nearest) * 0.001
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.d GetDateFromValue(Year, Month, Day, Hour, Minute, Second, Local = #True)
        Protected r1.d, vtime.d, Time.SystemTime
        
        Time\wYear = Year
        Time\wMonth = Month
        Time\wDay = Day
        Time\wHour = Hour
        Time\wMinute = Minute
        Time\wSecond = Second
        If Local
          TzSpecificLocalTimeToSystemTime(0, Time, Time)
        EndIf
        
        SystemTimeToVariantTime_(Time, @vTime)
        r1 = Round((vtime - #DT_BASE_1970) * 86400000.0 + Time\wMilliseconds, #PB_Round_Nearest) * 0.001
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.d GetDateNow()
        Protected r1.d, vTime.d, Time.SystemTime
        
        GetSystemTime_(@Time)
        SystemTimeToVariantTime_(Time, @vTime)
        r1 = Round((vtime - #DT_BASE_1970) * 86400000.0 + Time\wMilliseconds, #PB_Round_Nearest) * 0.001
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure GetSecondsFromGMT()
        Protected r1.d, vTime.d, TimeZone.TIME_ZONE_INFORMATION
        
        GetTimeZoneInformation_(@TimeZone)
        With TimeZone
          r1 = (\Bias + \DaylightBias) * -60 
        EndWith
         
        ProcedureReturn r1
      EndProcedure
      
      
      ;-- Calendar functions
      
      Procedure.d GetCalendarDate(Gadget)
        Protected r1.d, vTime.d, Time.SystemTime
        SendMessage_(GadgetID(Gadget), #DTM_GETSYSTEMTIME, 0, Time)
        TzSpecificLocalTimeToSystemTime(0, Time, Time)
        SystemTimeToVariantTime_(Time, @vTime)
        r1 = (vtime - #DT_BASE_1970) * 86400.0
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure SetCalendarDate(Gadget, Date.d)
        Protected vTime.d, Time.SystemTime
        ; Date to VariantTime
        vTime = (Date / 86400.0) + #DT_BASE_1970
        If Not VariantTimeToSystemTime_(vTime, @Time)
          ProcedureReturn 0
        EndIf
        SystemTimeToTzSpecificLocalTime(0, Time, Time)
        SendMessage_(GadgetID(Gadget), #DTM_SETSYSTEMTIME, #GDT_VALID, Time)
      EndProcedure
      
    CompilerCase #PB_OS_Linux
      
      ;- OS Linux
      
      CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
        CompilerWarning "Linux Date Limit from 1901-12-14 to 2038-04-19"
      CompilerEndIf
      
      ; ----
      
      Structure tm Align #PB_Structure_AlignC
        tm_sec.l    ; 0 to 59 or up to 60 at leap second
        tm_min.l    ; 0 to 59
        tm_hour.l   ; 0 to 23
        tm_mday.l   ; Day of the month: 1 to 31
        tm_mon.l    ; Month: 0 to 11 (0 = January)
        tm_year.l   ; Number of years since the year 1900
        tm_wday.l   ; Weekday: 0 to 6, 0 = Sunday
        tm_yday.l   ; Days since the beginning of the year: 0 to 365 (365 is therefore 366 because after 1. January is counted)
        tm_isdst.l  ; Is summer time? tm_isdst > 0 = Yes
                    ;                             tm_isdst = 0 = No
                    ;                             tm_isdst < 0 = Unknown
        CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
          tm_gmtoff.l ; Offset of UTC in seconds
          *tm_zone    ; Abbreviation of the time zone
        CompilerElse
          tm_zone.l   ; Placeholder
          tm_gmtoff.l ; Offset of UTC in seconds
          *tm_zone64  ; Abbreviation of the time zone
        CompilerEndIf
        
      EndStructure
      
      ; -- Intern
      
      Procedure secondsFromGMT(utc.q)
        Protected local.q, tm_local.tm
        
        If localtime_r_(@utc, @tm_local) <> 0
          local = timegm_(@tm_local)
          ProcedureReturn local - utc
        Else
          ProcedureReturn 0
        EndIf
      EndProcedure
      
      ; ----
      
      Procedure DateToTimeInfo(Date.q, *TimeInfo.tm, Local = #False)
        Protected r1
        
        If Local
          r1 = localtime_r_(@Date, *TimeInfo)
        Else
          r1 = gmtime_r_(@Date, *TimeInfo)
        EndIf
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.q TimeInfoToDate(*TimeInfo.tm, Local = #False)
        Protected r1.q, seconds.q
        r1 = timegm_(*TimeInfo)
        If Local
          seconds = secondsFromGMT(r1)
          r1 - seconds
        EndIf
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure GetDateComponent(Component, Date.d, Local = #True)
        Protected r1, vTime.d 
        Static LastDate, Time.tm
        
        If Date <> LastDate
          LastDate = Date
          DateToTimeInfo(Date, Time, Local)
        EndIf
        Select Component
          Case #PB_Date_Year
            ProcedureReturn Time\tm_year + 1900
          Case #PB_Date_Month
            ProcedureReturn Time\tm_mon
          Case #PB_Date_Day
            ProcedureReturn Time\tm_mday
          Case #PB_Date_Hour
            ProcedureReturn Time\tm_hour
          Case #PB_Date_Minute
            ProcedureReturn Time\tm_min
          Case #PB_Date_Second
            ProcedureReturn Time\tm_sec
          Case #PB_Date_Week
            ProcedureReturn Time\tm_wday
        EndSelect
      EndProcedure
      
      ;-- Date functions
      
      Procedure.s GetStringFromDate(Date.d, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
        Protected r1.s, cnt, vTime.d, Time.tm, milliseconds.d
        
        milliseconds = date - Round(date, #PB_Round_Down)
        DateToTimeInfo(Date, Time, Local)
        ; SystemTime to String
        If Mask = ""
          Mask = "%yyyy-%mm-%dd %hh:%ii:%ss"
        EndIf
        mask = LCase(mask)
        cnt = CountString(mask, "y")
        r1 = RemoveString(Mask, "%")
        If cnt = 2
          r1 = ReplaceString(r1, "yy", RSet(Str(Time\tm_year % 100), 4, "0"))
        Else
          r1 = ReplaceString(r1, "yyyy", RSet(Str(Time\tm_year + 1900), 4, "0"))
        EndIf
        r1 = ReplaceString(r1, "mm", RSet(Str(Time\tm_mon + 1), 2, "0"))
        r1 = ReplaceString(r1, "dd", RSet(Str(Time\tm_mday),2, "0"))
        r1 = ReplaceString(r1, "hh", RSet(Str(Time\tm_hour),2, "0"))
        r1 = ReplaceString(r1, "ii", RSet(Str(Time\tm_min),2, "0"))
        r1 = ReplaceString(r1, "ss", RSet(Str(Time\tm_sec),2, "0"))
        r1 = ReplaceString(r1, "nnn", Right(StrD(milliseconds, 3), 3))
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.d GetDateFromString(Date.s, Mask.s = "%yyyy-%mm-%dd %hh:%ii:%ss", Local = #True)
        Protected r1.d, pos, cnt, vTime.q, Time.tm, milliseconds.d
        
        ; String to SystemTime
        If Mask = ""
          Mask = "%yyyy-%mm-%dd %hh:%ii:%ss"
        EndIf
        mask = LCase(mask)
        mask = RemoveString(mask, "%")
        cnt = CountString(mask, "y")
        pos = FindString(mask, "y")
        If pos
          Time\tm_year = Val(Mid(Date, pos, cnt)) - 1900
        EndIf
        pos = FindString(mask, "m")
        If pos
          Time\tm_mon = Val(Mid(Date, pos, 2)) - 1
        EndIf
        pos = FindString(Mask, "d")
        If pos
          Time\tm_mday = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "h")
        If pos
          Time\tm_hour = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "i")
        If pos
          Time\tm_min = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "s")
        If pos
          Time\tm_sec = Val(Mid(Date, pos, 2))
        EndIf
        pos = FindString(Mask, "n")
        If pos
          milliseconds = ValD(Mid(Date, pos, 3))
        EndIf
        
        vtime = TimeInfoToDate(Time, Local) 
        r1 = vtime + (milliseconds * 0.001)
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure.d GetDateFromValue(Year, Month, Day, Hour, Minute, Second, Local = #True)
        Protected r1.d, vTime.q, Time.tm
        
        Time\tm_year = Year - 1900
        Time\tm_mon = Month - 1
        Time\tm_mday = Day
        Time\tm_hour = Hour
        Time\tm_min = Minute
        Time\tm_sec = Second
        vtime = TimeInfoToDate(Time, Local) 
        r1 = vtime
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      #CLOCK_REALTIME = 0
      
      Structure timespec
        tv_sec.q
        tv_nsec.l
      EndStructure
      
      Import ""
        clock_gettime(clk_id, tv)
        clock_getres(clk_id, res);
      EndImport
      
      Procedure.d GetDateNow()
        Protected r1.d, tv.timespec, vtime.q
        clock_gettime(#CLOCK_REALTIME , @tv)
        vtime = tv\tv_sec * 1000 + ( tv\tv_nsec + 500000 ) / 1000000;
        r1 = vtime * 0.001
        ProcedureReturn r1
      EndProcedure
      
      Procedure.d GetDateNow_Old()
        Protected r1.d, vTime.q
        
        vtime = time_(0)
        r1 = vTime
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure GetSecondsFromGMT()
        Protected r1, vTime.q
        
        vtime = time_(0)
        r1 = secondsFromGMT(vTime)
        ProcedureReturn r1
      EndProcedure
      
      ;-- Calendar functions
      
      Procedure.d GetCalendarDate(Gadget)
        Protected r1.d, vTime.d, Time.tm, now.tm
        Protected year, month, day
        
        DateToTimeInfo(time_(0), now, #True)
        gtk_calendar_get_date_(GadgetID(Gadget), @year, @month, @day)
        Time\tm_year = year - 1900
        Time\tm_mon = month
        Time\tm_mday = day
        Time\tm_hour = now\tm_hour
        Time\tm_min = now\tm_hour
        Time\tm_sec = now\tm_sec
        vTime = TimeInfoToDate(@Time, #True)
        r1 = vTime
        ProcedureReturn r1
      EndProcedure
      
      ; ----
      
      Procedure SetCalendarDate(Gadget, Date.d)
        Protected vTime.d, Time.tm
        
        DateToTimeInfo(Date, @Time, #True)
        gtk_calendar_select_month_(GadgetID(Gadget), Time\tm_mon, Time\tm_year + 1900)
        gtk_calendar_select_day_(GadgetID(Gadget), Time\tm_mday)
        
      EndProcedure
      
  CompilerEndSelect
  
  ;-> Os All
  
  Procedure GetDateYear(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Year, Date, Local)
  EndProcedure
  
  Procedure GetDateMonth(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Month, Date, Local)
  EndProcedure
  
  Procedure GetDateDay(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Day, Date, Local)
  EndProcedure
  
  Procedure GetDateHour(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Hour, Date, Local)
  EndProcedure
  
  Procedure GetDateMinute(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Minute, Date, Local)
  EndProcedure
  
  Procedure GetDateSecond(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Second, Date, Local)
  EndProcedure
  
  Procedure GetDateDayOfWeek(Date.d, Local = #True)
    ProcedureReturn GetDateComponent(#PB_Date_Week, Date, Local)
  EndProcedure
  
EndModule
;- Example 1
CompilerIf #PB_Compiler_IsMainFile
  
  UseModule DateTime
  
  
  Debug "--------"
  Date.d = GetDateNow()
  Debug "Get Date Value Now: " + StrD(date, 3)
  Debug "Get Date Value PB: " + Date()
  sDate.s = GetStringFromDate(Date, "", #False)
  Debug "Get String Now (UTC): " + sDate
  sDate.s = GetStringFromDate(Date, "%yyyy-%mm-%dd %hh:%ii:%ss.%nnn")
  Debug "Get String Now (Local): " + sDate
  Debug "Local to UTC: " + GetSecondsFromGMT()
  Debug "--------"
  Date = Date + (1.0 * 86400.0)
  Debug "One Day Later (Local): " + GetStringFromDate(date)
  Debug "--------"
  Date = GetDateFromString("1865-08-20 00:00:00.100", "%yyyy-%mm-%dd %hh:%ii:%ss.%nnn", #False)
  Debug "Date Value from String (UTC): " + date
  Debug "Date String from Date (UTC): " + GetStringFromDate(Date, "%yyyy-%mm-%dd %hh:%ii:%ss.%nnn", #False)
  Debug "--------"
  Date = GetDateFromString("2065-08-20 00:00:00", "", #False)
  Debug "Date Value from String (UTC): " + date
  Debug "Date String from Date (UTC): " + GetStringFromDate(Date, "", #False)
  Debug "--------"
  Date = GetDateFromString("1965-08-20 00:00:00")
  Debug "Date Value from String (Local): " + date
  Debug "Date String from Date (UTC): " + GetStringFromDate(Date, "", #False)
  Debug "Date String from Date (Local): " + GetStringFromDate(Date)
  Debug "--------"
  Date.d = GetDateNow()
  sDate.s = GetStringFromDate(Date)
  Debug "Get String Now (Local): " + sDate
  Debug "- Year: " + GetDateYear(Date)
  Debug "- Month: " + GetDateMonth(Date)
  Debug "- Day: " + GetDateDay(Date)
  Debug "- Hour: " + GetDateHour(Date)
  Debug "- Minute: " + GetDateMinute(Date)
  Debug "- Secund: " + GetDateSecond(Date)
  Debug "--------"
  Debug "Day Of Week"
  sDate = "2022-08-06 00:00:00"
  Date = GetDateFromString(sDate)
  Debug "Date: " + sDate
  Debug "- PB: " + DayOfWeek(Date(2022,08,06,00,00,00))
  Debug "- MK: " + GetDateDayOfWeek(Date)
  sDate = "2022-08-06 12:00:00"
  Date = GetDateFromString(sDate)
  Debug "Date: " + sDate
  Debug "- PB: " + DayOfWeek(Date(2022,08,06,12,00,00))
  Debug "- MK: " + GetDateDayOfWeek(Date)
  sDate = "2022-08-07 00:00:00"
  Date = GetDateFromString(sDate)
  Debug "Date: " + sDate
  Debug "- PB: " + DayOfWeek(Date(2022,08,07,00,00,00))
  Debug "- MK: " + GetDateDayOfWeek(Date)
  sDate = "2022-08-07 12:00:00"
  Date = GetDateFromString(sDate)
  Debug "Date: " + sDate
  Debug "- PB: " + DayOfWeek(Date(2022,08,07,12,00,00))
  Debug "- MK: " + GetDateDayOfWeek(Date)
  sDate = "2022-08-08 00:00:00"
  Date = GetDateFromString(sDate)
  Debug "Date: " + sDate
  Debug "- PB: " + DayOfWeek(Date(2022,08,08,00,00,00))
  Debug "- MK: " + GetDateDayOfWeek(Date)
  sDate = "2022-08-08 12:00:00"
  Date = GetDateFromString(sDate)
  Debug "Date: " + sDate
  Debug "- PB: " + DayOfWeek(Date(2022,08,08,12,00,00))
  Debug "- MK: " + GetDateDayOfWeek(Date)
  
  Debug "--------"
  sDate = "2022-08-08 12:00:00"
  Date1.d = GetDateFromString(sDate)
  Date2.d = GetDateFromValue(2022,08,08,12,00,00)
  Debug Date1
  Debug Date2
  Debug GetStringFromDate(Date2)
  
CompilerEndIf



