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?

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  ENDIFENDIFEND 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

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 SELECTEND 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

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 SELECTEND 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

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

Code: Select all

`Screen 19Type Calendar_Month    Number As Integer      Month_name As String End TypeDim Shared MonthData(1 To 12)   As Calendar_MonthData "January", 31, "February", 28,  "March", 31Data "April", 30,   "May", 31, "June", 30, "July", 31, "August", 31Data "September",   30, "October", 31, "November", 30, "December", 31For I As Integer = 1 To 12    Read MonthData(I).Month_name, MonthData(I).NumberNext Function IsLeapYear (N As Integer) As Integer    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)End FunctionSub 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 7End Subfunction 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)            NextEnd function'==============  RUN =============================draw string(50,50), "SUNDAY ON DAY "& calendar(2014,3,8,14)  'MarchSleepclsdraw string(50,50), "SUNDAY ON DAY "& calendar(2014,11,1,7)   'Novembersleep `

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

Sorry, lang qb:

Code: Select all

` #lang "qb"Screen 19Type Calendar_Month    Number As Integer      Month_name As String End TypeDim Shared MonthData(1 To 12)   As Calendar_MonthData "January", 31, "February", 28,  "March", 31Data "April", 30,   "May", 31, "June", 30, "July", 31, "August", 31Data "September",   30, "October", 31, "November", 30, "December", 31For I As Integer = 1 To 12    Read MonthData(I).Month_name, MonthData(I).NumberNext Function IsLeapYear (N As Integer) As Integer    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)End FunctionSub 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 7End Subfunction 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)            NextEnd function'==============  RUN =============================draw string(50,50), "SUNDAY ON DAY "& calendar(2014,3,8,14)  'MarchSleepclsdraw string(50,50), "SUNDAY ON DAY "& calendar(2014,11,1,7)   'Novembersleep `
TJF
Posts: 3604
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

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

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 SELECTEND 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

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 FunctionSub 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 7End SubFunction 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        '===============================    NextEnd FunctionPrint "SUNDAY ON DAY "& calendar(2014,3,8,14)  'MarchPrint "SUNDAY ON DAY "& calendar(2014,11,1,7)   'NovemberSleep `
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

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

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 SELECTEND 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 LONGPrint 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

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 FunctionSub 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 7End SubFunction 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        '===============================    NextEnd FunctionPrint "SUNDAY ON DAY "& calendar(2014,3,8,14)  'MarchPrint "SUNDAY ON DAY "& calendar(2014,11,1,7)   'NovemberSleep `

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 LONGDECLARE Function IsLeapYear (N As Integer) As IntegerDECLARE Function Calendar (_year As Integer, _month As Integer,lowerdate As Integer,upperdate As Integer) As IntegerPrint 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 FUNCTIONFunction IsLeapYear (N As Integer) As Integer    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)End FunctionSub 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 7End SubFunction 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    NextEnd 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

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 SELECTEND FUNCTION`
TRS80
Posts: 11
Joined: Sep 13, 2014 17:50

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

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 SELECTEND 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

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

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 SELECTEND FUNCTION'' TESTSUB OP(BYREF I AS STRING)  ?MID\$(I,1,8); " "; MID\$(I,9,2); ":"; MID\$(I,11,2); " --> "; IsUSDST(I)END SUBOP("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

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 19Type Calendar_Month    Number As Integer      Month_name As String End TypeDim Shared MonthData(1 To 12)   As Calendar_MonthData "January", 31, "February", 28,  "March", 31Data "April", 30,   "May", 31, "June", 30, "July", 31, "August", 31Data "September",   30, "October", 31, "November", 30, "December", 31For I As Integer = 1 To 12    Read MonthData(I).Month_name, MonthData(I).NumberNext Function IsLeapYear (N As Integer) As Integer    IsLeapYear = (N Mod 4 = 0 And N Mod 100 <> 0) Or (N Mod 400 = 0)End FunctionSub 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 7End Subsub 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)  'MarchSleepclscalendar(1990,11)   'Novembersleep  `

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 FunctionSub 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 7End SubFunction 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=1end 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 ifEnd Functiondim as string ddim as string tprintd="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)printd="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)printprint "-------------------------"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)printd="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)printprintprint "--------------------------"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)printd="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)printprint "-------------------------"print "test mid seasons"printd="06-09-2014"t="01:59:00"print d,t,calendar(d,t)printd="12-09-2014"t="02:00:00"print d,t,calendar(d,t)printprint"--------------------------"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

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 1Dim As Integer _Day, _Month, _Year, _start, _stopFor _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; _stopNextSleepEnd`

Output

Code: Select all

`For 2014 US Daylight Saving start on March  9 and ends on November 2For 2015 US Daylight Saving start on March  8 and ends on November 1For 2016 US Daylight Saving start on March 13 and ends on November 6For 2017 US Daylight Saving start on March 12 and ends on November 5For 2018 US Daylight Saving start on March 11 and ends on November 4For 2019 US Daylight Saving start on March 10 and ends on November 3For 2020 US Daylight Saving start on March  8 and ends on November 1For 2021 US Daylight Saving start on March 14 and ends on November 7For 2022 US Daylight Saving start on March 13 and ends on November 6For 2023 US Daylight Saving start on March 12 and ends on November 5For 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).