Determine if Date/time falls within US Daylight Saving Time?

General FreeBASIC programming questions.
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

Determine if Date/time falls within US Daylight Saving Time?

Postby TRS80 » Oct 31, 2014 21:15

I posted an earlier thread that touched on this, but I'm wondering if there is a better way to solve this problem. I am dealing with date/time strings in this format:

20141031003000

Which if written in a more human-readable form would appear as 2014-10-31 00:30:00 (or October 31, 2014 at 12:30 AM)

The rules for U.S. Daylight Saving time are "Daylight saving time starts on the second Sunday in March and ends on the first Sunday in November, with the time changes taking place at 2:00 a.m. local time." (Source: Wikipedia). This means that:

If the month is January, February, or December, it is standard time all month.
If the month is April through October it is daylight time all month.
If it is November 8-30 or March 1-7 it is standard time.
If it is March 15-31 it is daylight time.

That part is easy to program. The problem is that the switch occurs on Sunday during the week of March 8-14 in the spring, and on Sunday during the week of November 1-7. But, how do I determine which day is Sunday and adjust accordingly?

Strangely, in the program I am trying to modify the day of the week is available in the variable DW, but it seems the value returned is 0-6 with 0=Tuesday, 1=Wednesday, 2=Thursday, 3=Friday, 4=Saturday, 5=Sunday, and 6=Monday. It appears they are taking a Julian date and doing a MOD 7 on it so as it happens the days of the week don't begin with Sunday.

I thought about creating a function but got stuck here (note that since this is not finished it probably has a few unsquashed bugs, and it has to be compiled with the -lang qb switch):

Code: Select all

DECLARE FUNCTION IsUSDST (DateTime AS STRING, DayWeek AS INTEGER)

FUNCTION IsUSDST (DateTime AS STRING, DayWeek AS INTEGER)
REM Variables used in function: M=Month (1-12), D-Day (1-31), H=Hour (00-23)
M=VAL(MID$(DateTime,5,2))
IF (M>3) AND (M<11) THEN
  IsUSDST$="1"
ELSEIF (M<3) or (M=12) THEN
  IsUSDST$="0"
ELSE
  D=VAL(MID$(DateTime,7,2))
  IF (M=3) AND (D>14) THEN
    IsUSDST$="1"
  ELSEIF ((M=3) AND (D<8)) OR ((M=11) AND (D>7)) THEN
    IsUSDST$="0"
  ELSE
    H=VAL(MID$(DateTime,9,2))
    IF M=3 THEN
      ...
    ELSEIF M=11 THEN
      ...
    ENDIF
  ENDIF
ENDIF
END FUNCTION


Basically I am stuck on what to put where the ... appears. In the first instance the date will be March 8-14 but if it is BEFORE Sunday I need to return a value of "0" for IsUSDST$, and "1" if it is after. If it is Sunday, then I need to return the value depending on the hour, which would be defined as H=VAL(MID$(DateTime,9,2)) and if H is 0 or 1 then it should return "0", if >2 then "1" and if "2" then "-1" since technically that hour is lost.

In the second instance the date will be November 1-7 but if it is BEFORE Sunday I need to return a value of "1" for IsUSDST$, and "0" if it is after. If it is Sunday, then I need to return the value depending on the hour, which would be defined as in the previous paragraph and if H is 0 or 1 then it should return "1", and if >1 then "0" (in this situation the extra hour doesn't matter since nothing of importance happens during that hour).

It's that whole "determining whether the date is before or after Sunday" that I can't seem to wrap my brain around. Am I making this more complicated than it needs to be? It would be easier if I could just give it a Julian date (which is available in the program as a long integer) and hour, and have it determine if that is or isn't under Daylight Saving Time, but I would have no clue at all how to do that.
TJF
Posts: 3604
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Determine if Date/time falls within US Daylight Saving T

Postby TJF » Oct 31, 2014 23:42

IMHO it should look like (in #LANG "FB")

Code: Select all

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS BYTE
  VAR m = CUBYTE(MID$(DateTime, 5, 2))
  SELECT CASE AS CONST m
  CASE 1, 2, 12 : RETURN 0
  CASE 4 TO 10  : RETURN 1
  END SELECT

  VAR j = CUSHORT(MID$(DateTime, 1, 4)) _
    , h = CUBYTE(MID$(DateTime, 9, 2)) _
    , f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4 _
    , w = f MOD 7 '                 first week day of the month (0 = Sa)

  IF m = 3 THEN
    VAR t = IIF(w > 1, 16, 9) - w '           switch day, we're in March
    SELECT CASE AS CONST t - CUBYTE(MID$(DateTime, 7, 2))
    CASE 0 : RETURN IIF(h > 2, 1, IIF(h < 2, 0, -1))
    CASE 1 TO 13 : RETURN 0
    CASE ELSE    : RETURN 1
    END SELECT
  END IF

  VAR t = IIF(w > 1, 9, 2) - w '           switch day, we're in November
  SELECT CASE AS CONST t - CUBYTE(MID$(DateTime, 7, 2))
  CASE 0 : RETURN IIF(h > 1, 0, 1)
  CASE 1 TO 6 : RETURN 1
  CASE ELSE   : RETURN 0
  END SELECT
END FUNCTION


(It doesn't need the DayWeek parameter since a week day computation is included, based on the Gregorian Calender, reliable for dates after the year 1582.)
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

Re: Determine if Date/time falls within US Daylight Saving T

Postby TRS80 » Nov 01, 2014 0:36

TJF wrote:IMHO it should look like (in #LANG "FB")

Code: Select all

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS BYTE
  VAR m = CUBYTE(MID$(DateTime, 5, 2))
  SELECT CASE AS CONST m
  CASE 1, 2, 12 : RETURN 0
  CASE 4 TO 10  : RETURN 1
  END SELECT

  VAR j = CUSHORT(MID$(DateTime, 1, 4)) _
    , h = CUBYTE(MID$(DateTime, 9, 2)) _
    , f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4 _
    , w = f MOD 7 '                 first week day of the month (0 = Sa)

  IF m = 3 THEN
    VAR t = IIF(w > 1, 16, 9) - w '           switch day, we're in March
    SELECT CASE AS CONST t - CUBYTE(MID$(DateTime, 7, 2))
    CASE 0 : RETURN IIF(h > 2, 1, IIF(h < 2, 0, -1))
    CASE 1 TO 13 : RETURN 0
    CASE ELSE    : RETURN 1
    END SELECT
  END IF

  VAR t = IIF(w > 1, 9, 2) - w '           switch day, we're in November
  SELECT CASE AS CONST t - CUBYTE(MID$(DateTime, 7, 2))
  CASE 0 : RETURN IIF(h > 1, 0, 1)
  CASE 1 TO 6 : RETURN 1
  CASE ELSE   : RETURN 0
  END SELECT
END FUNCTION


(It doesn't need the DayWeek parameter since a week day computation is included, based on the Gregorian Calender, reliable for dates after the year 1582.)


Thank you! Unfortunately it does not work with the -lang qb switch, and if I add a DECLARE statement and try to compile with -lang qb I get a bunch of errors:

[code=text file=Untitled.bas]$ fbc isdst.bas -lang qb
isdst.bas(1) error 14: Expected identifier, found 'BYTE'
DECLARE FUNCTION IsUSDST (BYREF DateTime AS STRING) AS BYTE
^
isdst.bas(1) error 3: Expected End-of-Line, found 'BYTE'
DECLARE FUNCTION IsUSDST (BYREF DateTime AS STRING) AS BYTE
^
isdst.bas(3) error 14: Expected identifier, found 'BYTE'
FUNCTION IsUSDST (BYREF DateTime AS STRING) AS BYTE
^
isdst.bas(3) error 3: Expected End-of-Line, found 'BYTE'
FUNCTION IsUSDST (BYREF DateTime AS STRING) AS BYTE
^
isdst.bas(4) error 10: Expected '=', found 'm'
VAR m = CUBYTE(MID$(DateTime, 5, 2))
^
isdst.bas(5) error 24: Invalid data types
SELECT CASE AS CONST m
^
isdst.bas(10) error 10: Expected '=', found 'j'
VAR j = CUSHORT(MID$(DateTime, 1, 4)) _
^
isdst.bas(16) error 10: Expected '=', found 't'
VAR t = IIF(w > 1, 16, 9) - w ' switch day, we're in March
^
isdst.bas(17) error 70: Array not dimensioned, before '('
SELECT CASE AS CONST t - CUBYTE(MID$(DateTime, 7, 2))
^
isdst.bas(18) error 3: Expected End-of-Line, found '('
CASE 0 : RETURN IIF(h > 2, 1, IIF(h < 2, 0, -1))
^
isdst.bas(18) error 125: Too many errors, exiting
[/code]
As I noted the rest of the program is written in the old QBASIC/QuickBASIC format so unless I want to try and rewrite the whole thing (which I really don't) I need something that works with the -lang qb switch. Maybe I can modify it to make it work, but to do that I will need to understand why the above errors are occurring, and right now I don't.
dodicat
Posts: 6723
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Determine if Date/time falls within US Daylight Saving T

Postby dodicat » Nov 01, 2014 0:46

Here is a self check method.
Sunday being the key day??

Code: Select all

Screen 19
Type Calendar_Month
    Number As Integer 
    Month_name As String
End Type

Dim Shared MonthData(1 To 12)   As Calendar_Month

Data "January", 31, "February", 28,  "March", 31
Data "April", 30,   "May", 31, "June", 30, "July", 31, "August", 31
Data "September",   30, "October", 31, "November", 30, "December", 31

For I As Integer = 1 To 12
    Read MonthData(I).Month_name, MonthData(I).Number
Next


Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then           
            NumDays = NumDays + LEAP   
        Else                               
            NumDays = NumDays + NORMAL
        End If
    Next
   
    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I).Number
    Next
    TotalDays = MonthData(_month).Number
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

function Calendar (_year As Integer, _month As Integer,lowerdate as integer,upperdate as integer) as integer
    Dim As String header
    Dim As Integer totaldays,_pos,_csrlin
    Dim As Integer startday,leftmargin
    ComputeMonth _year, _month, StartDay, TotalDays
    Header = Rtrim(MonthData(_month).Month_name) + ", " + Str(_year)
    LeftMargin = (35 - Len(Header)) \ 2
    Locate 5
    Print Tab(LeftMargin+50); Header
    Print
    Print Tab(50);"Su    M   Tu    W   Th    F   Sa"
    Print
    LeftMargin = 5 * StartDay + 1
    Print Tab(LeftMargin+49);
    For I As Integer = 1 To TotalDays
        Print Using "##_   "; I;
        '================================
        If I>=lowerdate And I<=upperdate Then   'dates required
            'SUNDAY
            if (startday+I-1) mod 7=0 then function= I

        End If
        '===============================
        If Pos(0) > 82 Then Print Tab(50)
       
    Next
End function

'==============  RUN =============================
draw string(50,50), "SUNDAY ON DAY "& calendar(2014,3,8,14)  'March
Sleep
cls
draw string(50,50), "SUNDAY ON DAY "& calendar(2014,11,1,7)   'November
sleep



 


OK from 1899 onwards.
dodicat
Posts: 6723
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Determine if Date/time falls within US Daylight Saving T

Postby dodicat » Nov 01, 2014 0:50

Sorry, lang qb:

Code: Select all

 
#lang "qb"

Screen 19
Type Calendar_Month
    Number As Integer 
    Month_name As String
End Type

Dim Shared MonthData(1 To 12)   As Calendar_Month

Data "January", 31, "February", 28,  "March", 31
Data "April", 30,   "May", 31, "June", 30, "July", 31, "August", 31
Data "September",   30, "October", 31, "November", 30, "December", 31

For I As Integer = 1 To 12
    Read MonthData(I).Month_name, MonthData(I).Number
Next


Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then           
            NumDays = NumDays + LEAP   
        Else                               
            NumDays = NumDays + NORMAL
        End If
    Next
   
    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I).Number
    Next
    TotalDays = MonthData(_month).Number
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

function Calendar (_year As Integer, _month As Integer,lowerdate as integer,upperdate as integer) as integer
    Dim As String header
    Dim As Integer totaldays,_pos,_csrlin
    Dim As Integer startday,leftmargin
    ComputeMonth _year, _month, StartDay, TotalDays
    Header = Rtrim$(MonthData(_month).Month_name) + ", " + Str$(_year)
    LeftMargin = (35 - Len(Header)) \ 2
    Locate 5
    Print Tab(LeftMargin+50); Header
    Print
    Print Tab(50);"Su    M   Tu    W   Th    F   Sa"
    Print
    LeftMargin = 5 * StartDay + 1
    Print Tab(LeftMargin+49);
    For I As Integer = 1 To TotalDays
        Print Using "##_   "; I;
        '================================
        If I>=lowerdate And I<=upperdate Then   'dates required
            'SUNDAY
            if (startday+I-1) mod 7=0 then calendar= I

        End If
        '===============================
        If Pos(0) > 82 Then Print Tab(50)
       
    Next
End function

'==============  RUN =============================
draw string(50,50), "SUNDAY ON DAY "& calendar(2014,3,8,14)  'March
Sleep
cls
draw string(50,50), "SUNDAY ON DAY "& calendar(2014,11,1,7)   'November
sleep



 
TJF
Posts: 3604
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Determine if Date/time falls within US Daylight Saving T

Postby TJF » Nov 01, 2014 1:21

Code: Select all

#LANG "qb"

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
  m = VAL(MID$(DateTime, 5, 2))
  SELECT CASE m
  CASE 1, 2, 12    : IsUSDST =  0
  CASE 4 TO 10     : IsUSDST =  1
  CASE ELSE
    j = VAL(MID$(DateTime, 1, 4))
    h = VAL(MID$(DateTime, 9, 2))
    f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4
    w = f MOD 7 '                   first week day of the month (0 = Sa)

    IF m = 3 THEN
      t = __IIF(w > 1, 16, 9) - w '           switch day, we're in March
      SELECT CASE t - VAL(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h > 2, 1, __IIF(h < 2, 0, -1))
      CASE 1 TO 13 : IsUSDST = 0
      CASE ELSE    : IsUSDST = 1
      END SELECT
    ELSE
      t = __IIF(w > 1, 9, 2) - w '         switch day, we're in November
      SELECT CASE t - VAL(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h > 1, 0, 1)
      CASE 1 TO 6  : IsUSDST = 1
      CASE ELSE    : IsUSDST = 0
      END SELECT
    END IF
  END SELECT
END FUNCTION


Note:

The LANG "qb" version is significantly slower than the LANG "FB" version.
Last edited by TJF on Nov 01, 2014 1:29, edited 1 time in total.
dodicat
Posts: 6723
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Determine if Date/time falls within US Daylight Saving T

Postby dodicat » Nov 01, 2014 1:29

Here is my lang qb method without the graphics.

Code: Select all


#lang "qb"
Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Dim As Integer MonthData(1 To 12)'={31,28,31,30,31,30,31,31,30,31,30,31}
    monthdata(1)=31
    monthdata(2)=28
    monthdata(3)=31
    monthdata(4)=30
    monthdata(5)=31
    monthdata(6)=30
    monthdata(7)=31
    monthdata(8)=31
    monthdata(9)=30
    monthdata(10)=31
    monthdata(11)=30
    monthdata(12)=31
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then           
            NumDays = NumDays + LEAP   
        Else                               
            NumDays = NumDays + NORMAL
        End If
    Next
   
    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I)
    Next
    TotalDays = MonthData(_month)
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

Function Calendar (_year As Integer, _month As Integer,lowerdate As Integer,upperdate As Integer) As Integer
    Dim As Integer totaldays
    Dim As Integer startday
    ComputeMonth _year, _month, StartDay, TotalDays
    For I As Integer = 1 To TotalDays
        If I>=lowerdate And I<=upperdate Then   'dates range required
            'SUNDAY
            If (startday+I-1) Mod 7=0 Then calendar= I
        End If
        '===============================
    Next
End Function


Print "SUNDAY ON DAY "& calendar(2014,3,8,14)  'March

Print "SUNDAY ON DAY "& calendar(2014,11,1,7)   'November
Sleep
 
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

Re: Determine if Date/time falls within US Daylight Saving T

Postby TRS80 » Nov 01, 2014 1:56

TJF wrote:

Code: Select all

#LANG "qb"

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
  m = VAL(MID$(DateTime, 5, 2))
  SELECT CASE m
  CASE 1, 2, 12    : IsUSDST =  0
  CASE 4 TO 10     : IsUSDST =  1
  CASE ELSE
    j = VAL(MID$(DateTime, 1, 4))
    h = VAL(MID$(DateTime, 9, 2))
    f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4
    w = f MOD 7 '                   first week day of the month (0 = Sa)

    IF m = 3 THEN
      t = __IIF(w > 1, 16, 9) - w '           switch day, we're in March
      SELECT CASE t - VAL(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h > 2, 1, __IIF(h < 2, 0, -1))
      CASE 1 TO 13 : IsUSDST = 0
      CASE ELSE    : IsUSDST = 1
      END SELECT
    ELSE
      t = __IIF(w > 1, 9, 2) - w '         switch day, we're in November
      SELECT CASE t - VAL(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h > 1, 0, 1)
      CASE 1 TO 6  : IsUSDST = 1
      CASE ELSE    : IsUSDST = 0
      END SELECT
    END IF
  END SELECT
END FUNCTION


Note:

The LANG "qb" version is significantly slower than the LANG "FB" version.


Thank you, that compiles without errors for me. Just for test purposes I added these lines at the top:

Code: Select all

DECLARE FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
Print IsUSDST(COMMAND$)
END


The only thing is, it doesn't handle the calculation correctly for November (and maybe March, didn't test that yet). Consider the following results:

[code=text file=Untitled.bas]$ ./is 20141107015900
1[/code]
(Still thinks it is Daylight time up to 1:59 AM next Friday!)

[code=text file=Untitled.bas]$ ./is 20141107020000
0[/code]
Finally realizes it's no longer DST at 2:00 AM.

I have a feeling there is something wrong in this line:

Code: Select all

f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4


Just not sure yet what it is. Will work on it more later tonight. But this is a big step forward so thank you!
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

Re: Determine if Date/time falls within US Daylight Saving T

Postby TRS80 » Nov 01, 2014 6:43

dodicat wrote:Here is my lang qb method without the graphics.

Code: Select all


#lang "qb"
Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Dim As Integer MonthData(1 To 12)'={31,28,31,30,31,30,31,31,30,31,30,31}
    monthdata(1)=31
    monthdata(2)=28
    monthdata(3)=31
    monthdata(4)=30
    monthdata(5)=31
    monthdata(6)=30
    monthdata(7)=31
    monthdata(8)=31
    monthdata(9)=30
    monthdata(10)=31
    monthdata(11)=30
    monthdata(12)=31
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then           
            NumDays = NumDays + LEAP   
        Else                               
            NumDays = NumDays + NORMAL
        End If
    Next
   
    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I)
    Next
    TotalDays = MonthData(_month)
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

Function Calendar (_year As Integer, _month As Integer,lowerdate As Integer,upperdate As Integer) As Integer
    Dim As Integer totaldays
    Dim As Integer startday
    ComputeMonth _year, _month, StartDay, TotalDays
    For I As Integer = 1 To TotalDays
        If I>=lowerdate And I<=upperdate Then   'dates range required
            'SUNDAY
            If (startday+I-1) Mod 7=0 Then calendar= I
        End If
        '===============================
    Next
End Function


Print "SUNDAY ON DAY "& calendar(2014,3,8,14)  'March

Print "SUNDAY ON DAY "& calendar(2014,11,1,7)   'November
Sleep
 


In the end I wound up using this to determine which day Sunday falls on, since there was something not right about the calculation in TJF's code (it gave incorrect results), but I did use quite a bit of his code too. It wound up looking like this:

Code: Select all

#LANG "qb"

DECLARE FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
DECLARE Function IsLeapYear (N As Integer) As Integer
DECLARE Function Calendar (_year As Integer, _month As Integer,lowerdate As Integer,upperdate As Integer) As Integer
Print IsUSDST(COMMAND$)
END

    FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
      m = VAL(MID$(DateTime, 5, 2))
      SELECT CASE m
        CASE 1, 2, 12    : IsUSDST =  0
        CASE 4 TO 10     : IsUSDST =  1
        CASE 11
          d = VAL(MID$(DateTime, 7, 2))
          SELECT CASE d
            CASE IS > 7    : IsUSDST =  0
            CASE ELSE
              w = calendar(VAL(LEFT$(DateTime, 4)),11,1,7)
              IF d > w THEN
                IsUSDST =  0
              ELSEIF d < w THEN
                IsUSDST =  1
              ELSE
                IF VAL(MID$(DateTime, 9, 2)) < 2 THEN IsUSDST =  1 ELSE IsUSDST =  0
              END IF
          END SELECT
        CASE ELSE
          d = VAL(MID$(DateTime, 7, 2))
          SELECT CASE d
            CASE IS < 8    : IsUSDST =  0
            CASE IS > 14   : IsUSDST =  1
            CASE ELSE
              w = calendar(VAL(LEFT$(DateTime, 4)),3,8,14)
              IF d > w THEN
                IsUSDST =  1
              ELSEIF d < w THEN
                IsUSDST =  0
              ELSE
                IF VAL(MID$(DateTime, 9, 2)) < 2 THEN IsUSDST =  0 ELSE IsUSDST =  1
              END IF
          END SELECT
      END SELECT
    END FUNCTION

Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Dim As Integer MonthData(1 To 12)'={31,28,31,30,31,30,31,31,30,31,30,31}
    monthdata(1)=31
    monthdata(2)=28
    monthdata(3)=31
    monthdata(4)=30
    monthdata(5)=31
    monthdata(6)=30
    monthdata(7)=31
    monthdata(8)=31
    monthdata(9)=30
    monthdata(10)=31
    monthdata(11)=30
    monthdata(12)=31
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then
            NumDays = NumDays + LEAP
        Else
            NumDays = NumDays + NORMAL
        End If
    Next

    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I)
    Next
    TotalDays = MonthData(_month)
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

Function Calendar (_year As Integer, _month As Integer,lowerdate As Integer,upperdate As Integer) As Integer
    Dim As Integer totaldays
    Dim As Integer startday
    ComputeMonth _year, _month, StartDay, TotalDays
    For I As Integer = 1 To TotalDays
        If I>=lowerdate And I<=upperdate Then   'dates range required
            'SUNDAY
            If (startday+I-1) Mod 7=0 Then calendar= I
        End If
    Next
End Function


A couple of notes: First, I decided not to worry about it giving an incorrect response for the non-existent hour on the second Sunday in March because it really makes no difference in the final program (nothing eventful happens during the early AM hours of Sunday). Second, this program only gets called once a day by a cron job during the early AM hours, and even with these changes it only takes less than one second to run, and it honestly wouldn't matter if it took much longer than that. That's the nice thing about fast computers, you can have relatively inefficient code and it will run quickly after is is compiled, but if I were actually running it on a vintage system of the type available when QBASIC/QuickBASIC first appeared I would definitely want to optimize it some, and that would also be the case it it was called much more than once a day.

It does NOT handle the extra hour in November because it's simply not necessary in this situation. If you give it a time between 1 AM and 2 AM on the first Sunday in November it will return that it is on DST. It would be very easy to output a different value for that hour, I just didn't bother because nothing happens during that time in this application.

And, the program would have been a lot shorter if that one-line calculation had returned a correct result, but it apparently didn't. I tried to see if there was an easy fix (like adding or subtracting a certain number) but if I got it to work in November then it was still wrong in March and vise versa. I'm no mathematician so the solution to that will need to be found by someone else.

So thank you again, TJF and dodicat, by picking and merging the code I was able to get it working. You guys probably saved me from a sleepless night!
TJF
Posts: 3604
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Determine if Date/time falls within US Daylight Saving T

Postby TJF » Nov 01, 2014 9:01

TRS80 wrote:I have a feeling there is something wrong in this line:

Code: Select all

f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4


Just not sure yet what it is. Will work on it more later tonight. But this is a big step forward so thank you!

You're welcome!

Indeed, you're right. I broke the code when I tried to optimize. The line should be

Code: Select all

f = 365 * j + 1 + 31 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4

( ' 32 ' replaced by ' 1 + 31 ' )


BTW:

In order to shorten the code it's better to check the 6th day of a switching month:

Code: Select all

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
  m% = __VALUINT(MID$(DateTime, 5, 2))
  SELECT CASE m%
  CASE 1, 2, 12    : IsUSDST =  0
  CASE 4 TO 10     : IsUSDST =  1
  CASE ELSE
    j% = __VALUINT(MID$(DateTime, 1, 4))
    h% = __VALUINT(MID$(DateTime, 9, 2))
    f& = 365uL * j% + 6 + 31 * (m% - 1) + j% \ 4 - INT(.4 * m% + 2.3) - 3 * (1 + j% \ 100) \ 4
    w% = f& MOD 7 '                     6th week day of the month (0 = Sa)

    IF m% = 3 THEN
      SELECT CASE 14 - w% - __VALUINT(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h% > 2, 1, __IIF(h% < 2, 0, -1))
      CASE 1 TO 13 : IsUSDST = 0
      CASE ELSE    : IsUSDST = 1
      END SELECT
    ELSE
      SELECT CASE 7 - w% - __VALUINT(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h% > 1, 0, 1)
      CASE 1 TO 6  : IsUSDST = 1
      CASE ELSE    : IsUSDST = 0
      END SELECT
    END IF
  END SELECT
END FUNCTION
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

Re: Determine if Date/time falls within US Daylight Saving T

Postby TRS80 » Nov 01, 2014 15:45

TJF wrote:
TRS80 wrote:I have a feeling there is something wrong in this line:

Code: Select all

f = 365 * j + 32 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4


Just not sure yet what it is. Will work on it more later tonight. But this is a big step forward so thank you!

You're welcome!

Indeed, you're right. I broke the code when I tried to optimize. The line should be

Code: Select all

f = 365 * j + 1 + 31 * (m - 1) + j \ 4 - INT(.4 * m + 2.3) - 3 * (1 + j \ 100) \ 4

( ' 32 ' replaced by ' 1 + 31 ' )


BTW:

In order to shorten the code it's better to check the 6th day of a switching month:

Code: Select all

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
  m% = __VALUINT(MID$(DateTime, 5, 2))
  SELECT CASE m%
  CASE 1, 2, 12    : IsUSDST =  0
  CASE 4 TO 10     : IsUSDST =  1
  CASE ELSE
    j% = __VALUINT(MID$(DateTime, 1, 4))
    h% = __VALUINT(MID$(DateTime, 9, 2))
    f& = 365uL * j% + 6 + 31 * (m% - 1) + j% \ 4 - INT(.4 * m% + 2.3) - 3 * (1 + j% \ 100) \ 4
    w% = f& MOD 7 '                     6th week day of the month (0 = Sa)

    IF m% = 3 THEN
      SELECT CASE 14 - w% - __VALUINT(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h% > 2, 1, __IIF(h% < 2, 0, -1))
      CASE 1 TO 13 : IsUSDST = 0
      CASE ELSE    : IsUSDST = 1
      END SELECT
    ELSE
      SELECT CASE 7 - w% - __VALUINT(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h% > 1, 0, 1)
      CASE 1 TO 6  : IsUSDST = 1
      CASE ELSE    : IsUSDST = 0
      END SELECT
    END IF
  END SELECT
END FUNCTION


Sorry, but neither of these are correct either:

$ ./is 20141103015900
1
$ ./is 20141103020000
0

It is showing the changeover one day late (Monday AM, not Sunday AM). I didn't try March.
fxm
Posts: 9987
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Determine if Date/time falls within US Daylight Saving T

Postby fxm » Nov 01, 2014 15:56

Warning, TJF may scold you!
TJF wrote:Could something be done in the forum in order to prevent users from bloating the threads by quoting the complete previous post?
TJF
Posts: 3604
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Determine if Date/time falls within US Daylight Saving T

Postby TJF » Nov 01, 2014 18:03

TRS80 wrote:Sorry, but neither of these are correct either:

$ ./is 20141103015900
1
$ ./is 20141103020000
0

It is showing the changeover one day late (Monday AM, not Sunday AM). I didn't try March.

Here it's working

Code: Select all

#LANG "qb"

FUNCTION IsUSDST (BYREF DateTime AS STRING) AS LONG
  m% = __VALUINT(MID$(DateTime, 5, 2))
  SELECT CASE m%
  CASE 1, 2, 12    : IsUSDST =  0
  CASE 4 TO 10     : IsUSDST =  1
  CASE ELSE
    j% = __VALUINT(MID$(DateTime, 1, 4))
    h% = __VALUINT(MID$(DateTime, 9, 2))
    f& = 365uL * j% + 6 + 31 * (m% - 1) + j% \ 4 - INT(.4 * m% + 2.3) - 3 * (1 + j% \ 100) \ 4
    w% = f& MOD 7 '                     6th week day of the month (0 = Sa)

    IF m% = 3 THEN
      SELECT CASE 14 - w% - __VALUINT(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h% > 2, 1, __IIF(h% < 2, 0, -1))
      CASE 1 TO 13 : IsUSDST = 0
      CASE ELSE    : IsUSDST = 1
      END SELECT
    ELSE
      SELECT CASE 7 - w% - __VALUINT(MID$(DateTime, 7, 2))
      CASE 0       : IsUSDST = __IIF(h% > 1, 0, 1)
      CASE 1 TO 6  : IsUSDST = 1
      CASE ELSE    : IsUSDST = 0
      END SELECT
    END IF
  END SELECT
END FUNCTION


'' TEST

SUB OP(BYREF I AS STRING)
  ?MID$(I,1,8); " "; MID$(I,9,2); ":"; MID$(I,11,2); " --> "; IsUSDST(I)
END SUB

OP("20140309010000")
OP("20140309015900")
OP("20140309020000")
OP("20140309030000")
?
OP("20141102010000")
OP("20141102015900")
OP("20141102020000")
?
OP("20141103010000")
OP("20141103015900")
OP("20141103020000")

outputs
XUBUNTU-14.04 (64 bit), fbc-1.00 wrote:20140309 01:00 --> 0
20140309 01:59 --> 0
20140309 02:00 --> -1
20140309 03:00 --> 1

20141102 01:00 --> 1
20141102 01:59 --> 1
20141102 02:00 --> 0

20141103 01:00 --> 0
20141103 01:59 --> 0
20141103 02:00 --> 0


If you're sure that you didn't mix the versions, then ask fxm to file a bug report for you. (It seems that he has too much free time.)
dodicat
Posts: 6723
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Determine if Date/time falls within US Daylight Saving T

Postby dodicat » Nov 01, 2014 19:00

I've set mine to send as parameters the date then the time to return 1 for Summer and 0 for Winter (Northern hemisphere)

The date and time are given to me here in the U.K. in the format of my test strings.
E.G. Now is:
11-01-2014 18:49:50

Across the pond or on the continent may have different formats, I don't know, It's been a while since I have been in either place.

Firstly, here is a simple calendar to check out different years:
EDIT ~~ small mistake found and corrected.

Code: Select all

'Simple calendar
 
#lang "qb"

Screen 19
Type Calendar_Month
    Number As Integer 
    Month_name As String
End Type

Dim Shared MonthData(1 To 12)   As Calendar_Month

Data "January", 31, "February", 28,  "March", 31
Data "April", 30,   "May", 31, "June", 30, "July", 31, "August", 31
Data "September",   30, "October", 31, "November", 30, "December", 31

For I As Integer = 1 To 12
    Read MonthData(I).Month_name, MonthData(I).Number
Next


Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then           
            NumDays = NumDays + LEAP   
        Else                               
            NumDays = NumDays + NORMAL
        End If
    Next
   
    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I).Number
    Next
    TotalDays = MonthData(_month).Number
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

sub Calendar (_year As Integer, _month As Integer)
    Dim As String header
    Dim As Integer totaldays,_pos,_csrlin
    Dim As Integer startday,leftmargin
    ComputeMonth _year, _month, StartDay, TotalDays
    Header = Rtrim$(MonthData(_month).Month_name) + ", " + Str$(_year)
    LeftMargin = (35 - Len(Header)) \ 2
    Locate 5
    Print Tab(LeftMargin+50); Header
    Print
    Print Tab(50);"Su    M   Tu    W   Th    F   Sa"
    Print
    LeftMargin = 5 * StartDay + 1
    Print Tab(LeftMargin+49);
    For I As Integer = 1 To TotalDays
        Print Using "##_   "; I;
        If Pos(0) > 82 Then Print Tab(50)
    Next
    if left$(header,1)="M" then draw string (20,300),"Critical dates for Sunday, 8 th  to  14 th"
    if left$(header,1)="N" then draw string (20,300),"Critical dates for Sunday, 1 st  to  7 th"
End sub

'==============  RUN =============================
calendar(1990,3)  'March
Sleep
cls
calendar(1990,11)   'November
sleep
 

And here is the calendar returning either summer time =1, or Winter time=0

Code: Select all


#lang "qb"
Function IsLeapYear (N As Integer) As Integer
    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)
End Function

Sub ComputeMonth (_year As Integer, _month As Integer, Byref StartDay As Integer,Byref TotalDays As Integer)
    Dim As Integer MonthData(1 To 12)'={31,28,31,30,31,30,31,31,30,31,30,31}
    monthdata(1)=31
    monthdata(2)=28
    monthdata(3)=31
    monthdata(4)=30
    monthdata(5)=31
    monthdata(6)=30
    monthdata(7)=31
    monthdata(8)=31
    monthdata(9)=30
    monthdata(10)=31
    monthdata(11)=30
    monthdata(12)=31
    Const LEAP = 366 Mod 7
    Const NORMAL = 365 Mod 7
    Dim As Integer NumDays,I
    For I  = 1899 To _year- 1
        If IsLeapYear(I) Then           
            NumDays = NumDays + LEAP   
        Else                               
            NumDays = NumDays + NORMAL
        End If
    Next
   
    For I = 1 To _month - 1
        NumDays = NumDays + MonthData(I)
    Next
    TotalDays = MonthData(_month)
    If IsLeapYear(_year) Then
        If _month > 2 Then
            NumDays = NumDays + 1
        Elseif _month = 2 Then
            TotalDays = TotalDays + 1
        End If
    End If
    StartDay = NumDays Mod 7
End Sub

Function Calendar (_date as string,_time as string) As Integer
    Dim As Integer totaldays
    Dim As Integer startday
    dim as integer sunday
    dim as integer _year
    dim as integer I
    _year=__valint(mid$(_date,7,4))
    dim as integer _month
    _month=__valint(mid$(_date,1,2))
    dim as integer _day
    _day=__valint(mid$(_date,4,2))
    if _month<3 and _month>11 then calendar= 0:exit function  'winter
    if _month>3 and _month<11 then calendar= 1:exit function  'summer
    ComputeMonth _year, _month, StartDay, TotalDays
    if _month=11 then
        calendar=1
    For I  = 1 To TotalDays
        If I>=1 And I<=7 Then   'dates range required-november
            'SUNDAY
            If (startday+I-1) Mod 7=0 Then sunday=I
        End If
        '===============================
    Next
    if _day=sunday then if __valint(mid$(_time,1,2))>=2 then calendar=0 else calendar=1
end if

'===============================================
if _month=3 then
    calendar=0
    For I  = 1 To TotalDays
        If I>=8 And I<=14 Then   'dates range required-march
            'SUNDAY
            If (startday+I-1) Mod 7=0 Then sunday=I
        End If
        '===============================
    Next
  if _day=sunday then if __valint(mid$(_time,1,2))>=2 then calendar=1 else calendar=0
end if
End Function

dim as string d
dim as string t

print
d="11-02-2014"
t="01:59:00"
print d,t,calendar(d,t)
d="11-02-2014"
t="02:00:00"
print d,t,calendar(d,t)
print
d="03-09-2014"
t="01:59:00"
print d,t,calendar(d,t)

d="03-09-2014"
t="02:00:00"
print d,t,calendar(d,t)

print

print "-------------------------"

d="11-01-2015"
t="01:59:00"
print d,t,calendar(d,t)
d="11-01-2015"
t="02:00:00"
print d,t,calendar(d,t)
print
d="03-08-2015"
t="01:59:00"
print d,t,calendar(d,t)

d="03-08-2015"
t="02:00:00"
print d,t,calendar(d,t)

print

print
print "--------------------------"

d="11-04-1990"
t="01:59:00"
print d,t,calendar(d,t)
d="11-04-1990"
t="02:00:00"
print d,t,calendar(d,t)
print
d="03-11-1990"
t="01:59:00"
print d,t,calendar(d,t)

d="03-11-1990"
t="02:00:00"
print d,t,calendar(d,t)
print
print "-------------------------"
print "test mid seasons"
print
d="06-09-2014"
t="01:59:00"
print d,t,calendar(d,t)
print
d="12-09-2014"
t="02:00:00"
print d,t,calendar(d,t)
print
print"--------------------------"

print "Now, this minute:"
d=__date__
t=__time__
print d,t,calendar(d,t)
Sleep
 
 
frisian
Posts: 249
Joined: Oct 08, 2009 17:25

Re: Determine if Date/time falls within US Daylight Saving T

Postby frisian » Nov 01, 2014 21:55

Ok my program does not check of a date/time falls in winter or summer time.
It only give a dates for the start and ending of daylight saving .

Code: Select all

#Lang"qb"

' The base for the function weekday is take from
' 57 practical programs & games in basic (Radio Shack 1978)

Function Weekday(D As Integer, M As Integer, Y As Integer) As Integer

  ' 1 = Sunday     2 = Monday   3 = Tuesday    4 = Wednesday
  ' 5 = Thursday   6 = Friday   7 = Saturday

  Dim As Integer k, l, z1, z2, z3, z4 ,z
  Dim As Double p

  k  = Int(0.6 + (1 / M))
  l  = Y - k
  o  = M + 12 * k
  p  = l / 100
  ' z1 =
  ' z2 = Int(p)
  ' z3 = Int((5 * l) / 4)
  ' z4 = Int(13 * (o + 1) / 5)
  ' z  = z4 + z3 - z2 + z1 + D - 1
  ' Weekday = z Mod 7 + 1
 
  ' Weekday = (Int(13 * (o + 1) / 5) + Int((5 * l) / 4) - Int(p) + Int(p / 4) + D - 1) Mod 7 + 1
 
  Weekday = ((13 * (o + 1) \ 5) + ((5 * l) \ 4) - Int(p) + Int(p / 4) + D ) Mod 7
 
End Function

'=== MAIN ===
' US daylight saving start on second Sunday of March
' first possible number is 8
' US daylight saving ends on first Sunday of November
' first possible number is 1

Dim As Integer _Day, _Month, _Year, _start, _stop

For _year = 2014 To 2024

  _start = Weekday(8, 3, _year)
  _start = 8 + ((8 - _start) Mod 7)

  _stop = Weekday(1, 11, _year)
  _stop = 1 + ((8 - _stop) Mod 7)
  ' _stop = _start - 7

  Print Using "For #### US Daylight Saving start on March ## and ends on November #" _
  ; _year; _start; _stop

Next

Sleep
End


Output

Code: Select all

For 2014 US Daylight Saving start on March  9 and ends on November 2
For 2015 US Daylight Saving start on March  8 and ends on November 1
For 2016 US Daylight Saving start on March 13 and ends on November 6
For 2017 US Daylight Saving start on March 12 and ends on November 5
For 2018 US Daylight Saving start on March 11 and ends on November 4
For 2019 US Daylight Saving start on March 10 and ends on November 3
For 2020 US Daylight Saving start on March  8 and ends on November 1
For 2021 US Daylight Saving start on March 14 and ends on November 7
For 2022 US Daylight Saving start on March 13 and ends on November 6
For 2023 US Daylight Saving start on March 12 and ends on November 5
For 2024 US Daylight Saving start on March 10 and ends on November 3


Some things "weird" if daylight saving starts on March x then it ends on November (x-7).

Return to “General”

Who is online

Users browsing this forum: No registered users and 10 guests