'http://msdn.microsoft.com/en-us/library/windows/desktop/ms724390(v=vs.85).aspx
'http://msdn.microsoft.com/en-gb/library/windows/desktop/dd405535(v=vs.85).aspx

'https://msdn.microsoft.com/en-us/library/windows/desktop/ms682485(v=vs.85).aspx
'https://msdn.microsoft.com/en-us/library/windows/desktop/dd743609(v=vs.85).aspx


type SYSTEMTIME 
  word wYear,
       wMonth,
       wDayOfWeek,
       wDay,
       wHour,
       wMinute,
       wSecond,
       wMilliseconds
end type

extern lib "kernel32.dll"

  ! Sleep (sys msec)
  ! GetSystemTime (SYSTEMTIME*lpSystemTime)
  ! GetLocalTime  (SYSTEMTIME*lpSystemTime)
  ! GetTickCount() as sys
 '! GetTickCount64() as sys 'ulonglong vista 2008..
 '! QueryUnbiasedInterruptTime(quad*t) 'vista 2008..
 
  bool CreateTimerQueueTimer(
  sys*     phNewTimer,
  sys      TimerQueue=0,
  sys      Callback,
  sys      Parameter=0,
  dword    DueTime=0,
  dword    Period=100,
  dword    flags=0
  );

  bool DeleteTimerQueueTimer(
  sys TimerQueue=0,
  sys Timer,
  sys CompletionEvent=0
  );

  end extern


  function CalcDay(SYSTEMTIME*ti=null) as int
  ===========================================
  '
  'Start of Gregorian Calendar Friday 15 October 1582
  '
  int tdd,tmo,tyy,mds,lps,tsl
  int b1,b2,b3
  if @ti=0 then
    SYSTEMTIME st
    GetLocalTime st
    @ti=@st
  end if
  tyy=ti.wYear
  tmo=ti.wMonth
  tdd=ti.wDay-1 'base zero
  '
  select tmo
    case 1  : mds=0
    case 2  : mds=31
    case 3  : mds=59
    case 4  : mds=90
    case 5  : mds=120
    case 6  : mds=151
    case 7  : mds=181
    case 8  : mds=212
    case 9  : mds=243
    case 10 : mds=273
    case 11 : mds=304
    case 12 : mds=334
  end select
  if tmo<=2 then 'jan feb
    if mod(tyy,4)=0 then 'could be a leap year
      lps=1
      if mod(tyy,100)=0 then lps=0
      if mod(tyy,400)=0 then lps=1
    end if
    if lps then tdd-- 'remove this year's leap day
  end if
  'CALCULTE LEAP DAYS
  '
  'b3=tyy\400
  'b2=edx\100 'edx remainder
  'b1=edx\4 'edx remainder
  'lps=(b3*97)+(b2*24)+b1
  'return (tyy*365)+mds+tdd+lps
  '
  b1=tyy\4   '4year leaps
  b2=tyy\100 '100year non-leaps
  b3=tyy\400 '400year leaps
  lps=b1-b2+b3 'leaps
  return tdd+mds+lps+(tyy*365) 'days
  end function


  function CalcDate(SYSTEMTIME*ti,int gday)
  =========================================
  '
  'convert back to Gregorian Date
  '
  int tdd,tmo,tyy,mds,lds,lps,lpd
  int a,b1,b2,b3,r
  'EXTRACT LEAP DAYS
  b3=gday\146097 'gday/(400*365+97)
  b2=edx\36524   'remainder/(100*365+24)
  b1=edx\1461    'reamainder/(4*365+1)
  lps=(b3*97)+(b2*24)+b1 'total leap days in previous years
  a=gday-lps 'remove all leap days 
  tyy=a\365 'calc previous whole years
  mds=a-(tyy*365)+1 'calc days base1 (1..365)
  if mod(tyy,4)=0 then 'this could be a leap year
    lds=1
    if mod(tyy,100)=0 then lds=0
    if mod(tyy,400)=0 then lds=1
  end if
  if lds then 'this is a leap year
    if mds >=59 then
      if mds=59 then
        lpd=1 'feb 29th
      end if
    else
      mds++ 'compensate for leap day deduction
    end if
  end if
  select mds
    case <=  31 : tmo=1  : a=0
    case <=  59 : tmo=2  : a=31
    case <=  90 : tmo=3  : a=59
    case <= 120 : tmo=4  : a=90
    case <= 151 : tmo=5  : a=120
    case <= 181 : tmo=6  : a=151
    case <= 212 : tmo=7  : a=181
    case <= 243 : tmo=8  : a=212
    case <= 273 : tmo=9  : a=242
    case <= 304 : tmo=10 : a=273
    case <= 334 : tmo=11 : a=304
    case <= 365 : tmo=12 : a=334
  end select
  tdd=mds-a+lpd
  ti.wYear=tyy 'years 0 to 65535 limit
  ti.wMonth=tmo
  ti.wDay=tdd
  a=mod(gday+6,7)
  'if a<0 then a+=7
  ti.wDayOfWeek=a+1
  end function


  function CalcTimeStamp(SYSTEMTIME*ti=null, sys offd=0, quad*tq) as sys
  ======================================================================
  'excluding leap seconds
  int tdd,thh,tmi
  sys tss
  if @ti=0 then
    SYSTEMTIME st
    GetSystemTime st
    @ti=@st
  end if
  thh=ti.wHour
  tmi=ti.wMinute
  tss=ti.wSecond
  tdd=CalcDay(ti)
  tdd-=offd 'subtract day offset
  tss=tss+(tmi*60)+(thh*3600) 'lapsed seconds today
  tq=tss+tdd*86400 'total seconds since 00:00:00 01/01/1970
  return tq 'beware 32bit overflows
  end function


  function CalcDateTime(SYSTEMTIME*ti, sys offd=0, quad*tq=null)
  ==============================================================
  'seconds since Thursday 01/01/1970 00:00:00 UTC
  'excluding leap seconds
  '
  sys tdd
  int tss,tmi,thh,gday
  '
  'EXTRACT HOUR:MINUTE:SECOND
  addr rcx,tq
  mov edx,[rcx+4]
  mov eax,[rcx]
  mov ecx,86400
  div ecx
  tdd=eax 'day
  thh=edx\3600
  tmi=edx\60
  tss=edx
  ti.wHour=thh
  ti.wMinute=tmi
  ti.wSecond=tss
  '
  gday=tdd+offd  'add offset in days
  CalcDate(ti,gday)
  end function


  function CalcUnixTimeStamp(SYSTEMTIME*ti=null, quad*tq) as sys
  ==============================================================
  'seconds since Thursday 01/01/1970 00:00:00 UTC
  'excluding leap seconds
  CalcTimeStamp(ti,719527,tq) 'offset 01/01/170 in days
  return tq
  end function


  function CalcUnixDateTime(SYSTEMTIME*ti=null, quad*tq)
  ======================================================
  CalcDateTime(ti,719527,tq) 'offset 01/01/1970 in days
  end function


  macro longlongstore(t) {addr ecx,t : mov [ecx],eax : mov [ecx+4],edx}


  function TimeStr(SYSTEMTIME*tt=null) as string
  ==============================================
  'see ISO 8601
  string d,h,i
  macro f(v) mid( "0"+str(v) , -2)
  macro m(v) mid("00"+str(v), -3)
  SYSTEMTIME u,*t
  if @tt=0 then GetLocalTime(u) : @t=@u else @t=@tt 
  
  d=str(t.wYear) +"-"+f(t.wMonth)+"-"+f(t.wDay)
  h="T"+f(t.wHour)+":"+f(t.wMinute)+":"+f(t.wSecond)
  i="."+m(t.wMilliseconds)
  function=d h i
  end function


  function TimeLapseMs(SYSTEMTIME *t1,*t2) as sys
  ===============================================
  '<60 second timer
  sys td = t2.wMilliSeconds-t1.wMilliseconds+
  ((t2.wSecond-t1.wSecond)*1000)+
  ((t2.wMinute-t1.wMinute)*60000)+
  ((t2.wHour-t1.wHour)*3600000)+
  ((t2.wDayOfWeek-t1.wDayOfWeek)*86400000)
  '
  'end of week crossing
  if td<0 then td+=604800000
  return td
  end function
