Module: GreenButtonData::Dst

Includes:
Utilities
Included in:
LocalTimeParameters, Parser::LocalTimeParameters
Defined in:
lib/green-button-data/dst.rb

Constant Summary collapse

BITMASK_SECOND =

From ESPI XML schema:

extension

Bit map encoded rule from which is calculated the start or

end time, within the current year, to which daylight savings time offset must be applied.

The rule encoding: Bits 0 - 11: seconds 0 - 3599 Bits 12 - 16: hours 0 - 23 Bits 17 - 19: day of the week 0 = not applicable, 1 - 7 (Monday = 1) Bits:20 - 24: day of the month 0 = not applicable, 1 - 31 Bits: 25 - 27: operator (detailed below) Bits: 28 - 31: month 1 - 12

Rule value of 0xFFFFFFFF means rule processing/DST correction is disabled.

The operators:

0: DST starts/ends on the Day of the Month 1: DST starts/ends on the Day of the Week that is on or after the Day of the Month 2: DST starts/ends on the first occurrence of the Day of the Week in a month 3: DST starts/ends on the second occurrence of the Day of the Week in a month 4: DST starts/ends on the third occurrence of the Day of the Week in a month 5: DST starts/ends on the forth occurrence of the Day of the Week in a month 6: DST starts/ends on the fifth occurrence of the Day of the Week in a month 7: DST starts/ends on the last occurrence of the Day of the Week in a month

An example: DST starts on third Friday in March at 1:45 AM. The rule… Seconds: 2700 Hours: 1 Day of Week: 5 Day of Month: 0 Operator: 4 Month: 3

0x00000fff
BITMASK_HOUR =
0x0001f000
BITMASK_DAY_OF_WEEK =
0x000e0000
BITMASK_DAY_OF_MONTH =
0x01f00000
BITMASK_DST_RULE =
0x0e000000
BITMASK_MONTH =
0xf0000000
BITSHIFT_HOUR =
12
BITSHIFT_DAY_OF_WEEK =
17
BITSHIFT_DAY_OF_MONTH =
20
BITSHIFT_DST_RULE =
25
BITSHIFT_MONTH =
28

Instance Method Summary collapse

Methods included from Utilities

#attributes_to_hash, #epoch_to_time, #first_sunday_of, #last_weekday_of, #normalize_epoch, #nth_weekday_of, #parse_datetime

Instance Method Details

#byte_to_dst_datetime(byte, year = Time.now.year) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/green-button-data/dst.rb', line 51

def byte_to_dst_datetime(byte, year = Time.now.year)
  # Bits 0 - 11: seconds 0 - 3599
  seconds = byte & BITMASK_SECOND

  # Bits 12 - 16: hours 0 - 23
  hour = (byte & BITMASK_HOUR) >> BITSHIFT_HOUR

  # Bits 17 - 19: day of the week; 0 = NA, 1 - 7 (Monday = 1)
  weekday = (byte & BITMASK_DAY_OF_WEEK) >> BITSHIFT_DAY_OF_WEEK

  # Bits 20 - 24: day of the month; 0 = NA, 1 - 31
  day = (byte & BITMASK_DAY_OF_MONTH) >> BITSHIFT_DAY_OF_MONTH

  # Bits 25 - 27: DST rule 0 - 7
  dst_rule = (byte & BITMASK_DST_RULE) >> BITSHIFT_DST_RULE

  # Bits 28 - 31: month 1 - 12
  month = (byte & BITMASK_MONTH) >> BITSHIFT_MONTH

  # Raise an error unless all the values are in valid range
  seconds.between?(0, 3599) and hour.between?(0, 23) and
  weekday.between?(1, 7) and day.between?(0, 31) and
  dst_rule.between?(0, 7) and month.between?(1, 12) or
  raise RangeError, 'Invalid value range'

  # In Ruby, Sunday = 0 not 7
  weekday = weekday == 7 ? 0 : weekday

  # Check the DST rule
  dst_day = if dst_rule == 1
    # Rule 1: DST starts/ends on Day of Week on or after the Day of Month
    day_of_month = DateTime.new year, month, day
    day_offset = if weekday >= day_of_month.wday
      weekday - day_of_month.wday
    else
      7 + weekday - day_of_month.wday
    end

    day_of_month + day_offset
  elsif dst_rule.between?(2, 6)
    # Rule 2 - 6: DST starts/ends on Nth Day of Week in given month
    # Nth Day of Week (e.g. third Friday of July)
    nth_weekday_of year, month, weekday, dst_rule - 1
  elsif dst_rule == 7
    # Rule 7: DST starts/ends on last Day of Week in given month
    last_weekday_of year, month, weekday
  else
    # Rule 0: DST starts/ends on the Day of Month
    DateTime.new year, month, day
  end

  # Add the hour and seconds component to the day
  dst_day + Rational(hour * 3600 + seconds, 86400)
end