zmanim 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/Gemfile +6 -0
- data/LICENSE +504 -0
- data/README.md +124 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/zmanim.rb +6 -0
- data/lib/zmanim/astronomical_calendar.rb +105 -0
- data/lib/zmanim/hebrew_calendar/hebrew_date_formatter.rb +243 -0
- data/lib/zmanim/hebrew_calendar/jewish_calendar.rb +390 -0
- data/lib/zmanim/hebrew_calendar/jewish_date.rb +397 -0
- data/lib/zmanim/limudim/anchor/day_of_month_anchor.rb +49 -0
- data/lib/zmanim/limudim/anchor/day_of_week_anchor.rb +26 -0
- data/lib/zmanim/limudim/anchor/day_of_year_anchor.rb +27 -0
- data/lib/zmanim/limudim/calculators/daf_yomi_bavli.rb +53 -0
- data/lib/zmanim/limudim/calculators/daf_yomi_yerushalmi.rb +58 -0
- data/lib/zmanim/limudim/calculators/mishna_yomis.rb +48 -0
- data/lib/zmanim/limudim/calculators/parsha.rb +91 -0
- data/lib/zmanim/limudim/calculators/tehillim_monthly.rb +43 -0
- data/lib/zmanim/limudim/cycle.rb +40 -0
- data/lib/zmanim/limudim/interval.rb +37 -0
- data/lib/zmanim/limudim/limud.rb +42 -0
- data/lib/zmanim/limudim/limud_calculator.rb +137 -0
- data/lib/zmanim/limudim/limudim_formatter.rb +168 -0
- data/lib/zmanim/limudim/unit.rb +53 -0
- data/lib/zmanim/util/astronomical_calculations.rb +34 -0
- data/lib/zmanim/util/geo_location.rb +116 -0
- data/lib/zmanim/util/hebrew_numeric_formatter.rb +67 -0
- data/lib/zmanim/util/math_helper.rb +14 -0
- data/lib/zmanim/util/noaa_calculator.rb +180 -0
- data/lib/zmanim/util/sun_times_calculator.rb +123 -0
- data/lib/zmanim/util/text_helper.rb +7 -0
- data/lib/zmanim/util/time_zone_converter.rb +20 -0
- data/lib/zmanim/version.rb +3 -0
- data/lib/zmanim/zmanim_calendar.rb +106 -0
- data/zmanim.gemspec +37 -0
- metadata +153 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Zmanim::Limudim
|
2
|
+
class Limud
|
3
|
+
attr_reader :interval, :unit
|
4
|
+
|
5
|
+
def initialize(interval, unit)
|
6
|
+
@interval = interval
|
7
|
+
@unit = unit
|
8
|
+
end
|
9
|
+
|
10
|
+
def cycle
|
11
|
+
interval.cycle
|
12
|
+
end
|
13
|
+
|
14
|
+
def description
|
15
|
+
unit.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_date
|
19
|
+
interval.start_date
|
20
|
+
end
|
21
|
+
|
22
|
+
def end_date
|
23
|
+
interval.end_date
|
24
|
+
end
|
25
|
+
|
26
|
+
def iteration
|
27
|
+
interval.iteration
|
28
|
+
end
|
29
|
+
|
30
|
+
def cycle_start_date
|
31
|
+
cycle.start_date
|
32
|
+
end
|
33
|
+
|
34
|
+
def cycle_end_date
|
35
|
+
cycle.end_date
|
36
|
+
end
|
37
|
+
|
38
|
+
def cycle_iteration
|
39
|
+
cycle.iteration
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'zmanim/hebrew_calendar/jewish_date'
|
2
|
+
|
3
|
+
module Zmanim::Limudim
|
4
|
+
module LimudCalculator
|
5
|
+
def limud(date)
|
6
|
+
jewish_date = jewish_date(date)
|
7
|
+
cycle = find_cycle(jewish_date)
|
8
|
+
return nil unless cycle
|
9
|
+
units = cycle_units_calculation.(cycle)
|
10
|
+
interval = cycle.first_interval(interval_end_calculation)
|
11
|
+
while !jewish_date.between?(interval.start_date, interval.end_date) do
|
12
|
+
interval = interval.next(interval_end_calculation)
|
13
|
+
while !jewish_date.between?(interval.start_date, interval.end_date) && skip_interval?(interval)
|
14
|
+
interval = interval.skip(interval_end_calculation)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
unit = unit_for_interval(units, interval)
|
18
|
+
Zmanim::Limudim::Limud.new(interval, unit)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Jewish Date on which the first cycle starts (if not perpetual)
|
22
|
+
def initial_cycle_date
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Anchor on which a cycle resets (where relevant)
|
27
|
+
# e.g. for Parsha this would be a Day-of-Year anchor
|
28
|
+
def perpetual_cycle_anchor
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Number of units to apply over an iteration
|
33
|
+
def unit_step
|
34
|
+
1
|
35
|
+
end
|
36
|
+
|
37
|
+
# Are units components of some larger grouping? (e.g. pages or mishnayos)
|
38
|
+
def tiered_units?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
# For tiered units, this would be a Hash in the format:
|
43
|
+
# `{some_name: last_page, ...}`
|
44
|
+
# or:
|
45
|
+
# `{maseches: {perek_number => mishnayos, ...}, ...}`.
|
46
|
+
#
|
47
|
+
# For simple units, use an Array in the format:
|
48
|
+
# `[:some_name, ...]`
|
49
|
+
def default_units
|
50
|
+
{}
|
51
|
+
end
|
52
|
+
|
53
|
+
# Set if units are applied fractionally (indicated by a fractional unit_step).
|
54
|
+
# For example, an amud yomi calculator would set `%w(a b)`
|
55
|
+
def fractional_units
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Change this when using page numbers that do not generally start from one.
|
60
|
+
# (e.g. Talmud Bavli pages start from 2)
|
61
|
+
def default_starting_page
|
62
|
+
1
|
63
|
+
end
|
64
|
+
|
65
|
+
def starting_page(units, unit_name)
|
66
|
+
default_starting_page
|
67
|
+
end
|
68
|
+
|
69
|
+
def cycle_end_calculation
|
70
|
+
->(start_date, iteration){ start_date }
|
71
|
+
end
|
72
|
+
|
73
|
+
def interval_end_calculation
|
74
|
+
->(cycle, start_date){ start_date }
|
75
|
+
end
|
76
|
+
|
77
|
+
def cycle_units_calculation
|
78
|
+
->(cycle){ default_units }
|
79
|
+
end
|
80
|
+
|
81
|
+
def unit_for_interval(units, interval)
|
82
|
+
return tiered_units_for_interval(units, interval) if tiered_units?
|
83
|
+
Unit.new(*units[interval.iteration-1])
|
84
|
+
end
|
85
|
+
|
86
|
+
def skip_interval?(interval)
|
87
|
+
false
|
88
|
+
end
|
89
|
+
|
90
|
+
def tiered_units_for_interval(units, interval)
|
91
|
+
iteration = interval.iteration
|
92
|
+
offset = ((iteration - 1) * unit_step) + 1
|
93
|
+
offset2 = (offset - 1) + unit_step if unit_step > 1
|
94
|
+
offsets = [offset, offset2].compact
|
95
|
+
targets = offsets.map{|o| [o, []]}
|
96
|
+
results = find_offset_units(units, targets)
|
97
|
+
return nil unless results.map(&:first).uniq == [0]
|
98
|
+
paths = results.map(&:last)
|
99
|
+
Unit.new(*paths)
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_offset_units(units, targets)
|
103
|
+
units.reduce(targets) do |t, (name, attributes)|
|
104
|
+
if attributes.is_a?(Numeric)
|
105
|
+
start = starting_page(units, name)
|
106
|
+
length = (attributes - start) + 1
|
107
|
+
t.select{|o, p| o == 0} + t.reject{|o,p| o == 0}.map do |o, p|
|
108
|
+
o <= length ?
|
109
|
+
[0, p + [name, (start + o) - 1]] :
|
110
|
+
[o - length, p]
|
111
|
+
end
|
112
|
+
else
|
113
|
+
t.select{|o, p| o == 0} +
|
114
|
+
find_offset_units(attributes, t.reject{|o,p| o == 0}.map{|o, p| [o, p + [name]]}).map do |o, p|
|
115
|
+
o == 0 ? [o, p] : [o, p[0..-2]]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_cycle(date)
|
122
|
+
if initial_cycle_date
|
123
|
+
Cycle.from_cycle_initiation(initial_cycle_date, cycle_end_calculation, date)
|
124
|
+
elsif perpetual_cycle_anchor
|
125
|
+
Cycle.from_perpetual_anchor(perpetual_cycle_anchor, cycle_end_calculation, date)
|
126
|
+
else
|
127
|
+
raise 'Cycle cannot be determined without an initial date or an anchor'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
protected
|
132
|
+
|
133
|
+
def jewish_date(date)
|
134
|
+
date.respond_to?(:jewish_year) ? date : Zmanim::HebrewCalendar::JewishDate.new(date)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Zmanim::Limudim
|
3
|
+
class LimudimFormatter
|
4
|
+
include Zmanim::Util::TextHelper
|
5
|
+
include Zmanim::Util::HebrewNumericFormatter
|
6
|
+
|
7
|
+
attr_accessor :hebrew_format
|
8
|
+
PARSHIYOS = {
|
9
|
+
bereishis: 'בראשית',
|
10
|
+
noach: 'נח',
|
11
|
+
lech_lecha: 'לך לך',
|
12
|
+
vayeira: 'וירא',
|
13
|
+
chayei_sarah: 'חיי שרה',
|
14
|
+
toldos: 'תולדות',
|
15
|
+
vayeitzei: 'ויצא',
|
16
|
+
vayishlach: 'וישלח',
|
17
|
+
vayeishev: 'וישב',
|
18
|
+
mikeitz: 'מקץ',
|
19
|
+
vayigash: 'ויגש',
|
20
|
+
vayechi: 'ויחי',
|
21
|
+
shemos: 'שמות',
|
22
|
+
vaeirah: 'וארא',
|
23
|
+
bo: 'בא',
|
24
|
+
beshalach: 'בשלח',
|
25
|
+
yisro: 'יתרו',
|
26
|
+
mishpatim: 'משפטים',
|
27
|
+
terumah: 'תרומה',
|
28
|
+
tetzaveh: 'תצוה',
|
29
|
+
ki_sisa: 'כי תשא',
|
30
|
+
vayakheil: 'ויקהל',
|
31
|
+
pekudei: 'פקודי',
|
32
|
+
vayikra: 'ויקרא',
|
33
|
+
tzav: 'צו',
|
34
|
+
shemini: 'שמיני',
|
35
|
+
tazria: 'תזריע',
|
36
|
+
metzora: 'מצורע',
|
37
|
+
acharei: 'אחרי מות',
|
38
|
+
kedoshim: 'קדושים',
|
39
|
+
emor: 'אמר',
|
40
|
+
behar: 'בהר',
|
41
|
+
bechukosai: 'בחוקתי',
|
42
|
+
bamidbar: 'במדבר',
|
43
|
+
naso: 'נשא',
|
44
|
+
behaalosecha: 'בהעלותך',
|
45
|
+
shelach: 'שלח',
|
46
|
+
korach: 'קרח',
|
47
|
+
chukas: 'חקת',
|
48
|
+
balak: 'בלק',
|
49
|
+
pinchas: 'פינחס',
|
50
|
+
matos: 'מטות',
|
51
|
+
masei: 'מסעי',
|
52
|
+
devarim: 'דברים',
|
53
|
+
vaeschanan: 'ואתחנן',
|
54
|
+
eikev: 'עקב',
|
55
|
+
reei: 'ראה',
|
56
|
+
shoftim: 'שופטים',
|
57
|
+
ki_seitzei: 'כי תצא',
|
58
|
+
ki_savo: 'כי תבא',
|
59
|
+
nitzavim: 'נצבים',
|
60
|
+
vayeilech: 'וילך',
|
61
|
+
haazinu: 'האזינו',
|
62
|
+
vezos_haberacha: 'וזאת הברכה',
|
63
|
+
}
|
64
|
+
|
65
|
+
MASECHTOS = {
|
66
|
+
berachos: 'ברכות',
|
67
|
+
peah: 'פאה',
|
68
|
+
demai: 'דמאי',
|
69
|
+
kilayim: 'כלאים',
|
70
|
+
sheviis: 'שביעית',
|
71
|
+
terumos: 'תרומות',
|
72
|
+
maasros: 'מעשרות',
|
73
|
+
maaser_sheni: 'מעזר שני',
|
74
|
+
chalah: 'חלה',
|
75
|
+
orlah: 'ערלה',
|
76
|
+
bikurim: 'בכורים',
|
77
|
+
shabbos: 'שבת',
|
78
|
+
eruvin: 'ערובין',
|
79
|
+
pesachim: 'פסחים',
|
80
|
+
shekalim: 'שקלים',
|
81
|
+
yoma: 'יומא',
|
82
|
+
sukkah: 'סוכה',
|
83
|
+
beitzah: 'ביצה',
|
84
|
+
rosh_hashanah: 'ראש השנה',
|
85
|
+
taanis: 'תענית',
|
86
|
+
megillah: 'מגילה',
|
87
|
+
moed_katan: 'מועד קטן',
|
88
|
+
chagigah: 'חגיגה',
|
89
|
+
yevamos: 'יבמות',
|
90
|
+
kesubos: 'כתובות',
|
91
|
+
nedarim: 'נדרים',
|
92
|
+
nazir: 'נזיר',
|
93
|
+
sotah: 'סוטה',
|
94
|
+
gitin: 'גיטין',
|
95
|
+
kiddushin: 'קידושין',
|
96
|
+
bava_kamma: 'בבא קמא',
|
97
|
+
bava_metzia: 'בבא מציעא',
|
98
|
+
bava_basra: 'בבא בתרא',
|
99
|
+
sanhedrin: 'סנהדרין',
|
100
|
+
makkos: 'מכות',
|
101
|
+
shevuos: 'שבועות',
|
102
|
+
eduyos: 'עדיות',
|
103
|
+
avodah_zarah: 'עבודה זרה',
|
104
|
+
avos: 'אבות',
|
105
|
+
horiyos: 'הוריות',
|
106
|
+
zevachim: 'זבחים',
|
107
|
+
menachos: 'מנחות',
|
108
|
+
chullin: 'חולין',
|
109
|
+
bechoros: 'בכורות',
|
110
|
+
arachin: 'ערכין',
|
111
|
+
temurah: 'תמורה',
|
112
|
+
kerisos: 'כריתות',
|
113
|
+
meilah: 'מעילה',
|
114
|
+
tamid: 'תמיד',
|
115
|
+
midos: 'מידות',
|
116
|
+
kinnim: 'קינים',
|
117
|
+
keilim: 'כלים',
|
118
|
+
ohalos: 'אוהלות',
|
119
|
+
negaim: 'נגעים',
|
120
|
+
parah: 'פרה',
|
121
|
+
taharos: 'טהרות',
|
122
|
+
mikvaos: 'מקואות',
|
123
|
+
niddah: 'נדה',
|
124
|
+
machshirim: 'מכשירין',
|
125
|
+
zavim: 'זבים',
|
126
|
+
tevul_yom: 'טבול יום',
|
127
|
+
yadayim: 'ידים',
|
128
|
+
uktzin: 'עוקצין',
|
129
|
+
no_daf_today: 'אין דף היום',
|
130
|
+
}
|
131
|
+
|
132
|
+
def initialize
|
133
|
+
super
|
134
|
+
@use_geresh_gershayim = false
|
135
|
+
end
|
136
|
+
|
137
|
+
def format_parsha(limud)
|
138
|
+
prefix = hebrew_format ? 'פרשת ' : 'Parshas '
|
139
|
+
prefix + limud.unit.render do |parsha|
|
140
|
+
hebrew_format ? PARSHIYOS[parsha] : titleize(parsha)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def format_talmudic(limud)
|
145
|
+
return '' unless unit = (limud && limud.unit)
|
146
|
+
unit.render do |e|
|
147
|
+
if e.is_a?(Numeric)
|
148
|
+
format_number(e)
|
149
|
+
elsif hebrew_format
|
150
|
+
MASECHTOS[e]
|
151
|
+
else
|
152
|
+
titleize(e)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def format_tehillim(limud)
|
158
|
+
prefix = hebrew_format ? 'תהלים ' : 'Tehillim '
|
159
|
+
prefix + limud.unit.render {|e| format_number(e) }
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def format_number(number)
|
165
|
+
hebrew_format ? format_hebrew_number(number) : number
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Zmanim::Limudim
|
2
|
+
class Unit
|
3
|
+
attr_reader :components
|
4
|
+
def initialize(*components)
|
5
|
+
@components = components
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
render{|value| value }
|
10
|
+
end
|
11
|
+
|
12
|
+
def render
|
13
|
+
primary, secondary = components.map do |component|
|
14
|
+
Array(component).map{|v| yield v}
|
15
|
+
end
|
16
|
+
render_with_root(primary) + render_secondary(secondary, primary)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def render_with_root(component)
|
22
|
+
root, extension = component.first, component[1..-1]
|
23
|
+
return root.to_s if extension.length == 0
|
24
|
+
"#{root} #{render_extension(extension)}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_extension(extension)
|
28
|
+
extension.join(':')
|
29
|
+
end
|
30
|
+
|
31
|
+
def render_secondary(second_component, first_component)
|
32
|
+
if second_component.nil?
|
33
|
+
return ''
|
34
|
+
elsif second_component.first != first_component.first
|
35
|
+
' - ' + render_with_root(second_component)
|
36
|
+
elsif diff = render_difference(second_component, first_component)
|
37
|
+
'-' + diff
|
38
|
+
else
|
39
|
+
''
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def render_difference(rendering, comparing)
|
44
|
+
if rendering.length == 0
|
45
|
+
return nil
|
46
|
+
elsif rendering.first != comparing.first
|
47
|
+
render_extension(rendering)
|
48
|
+
else
|
49
|
+
render_difference(rendering[1..-1], comparing[1..-1])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'math_helper'
|
2
|
+
|
3
|
+
module Zmanim::Util
|
4
|
+
module AstronomicalCalculations
|
5
|
+
using Zmanim::Util::MathHelper
|
6
|
+
|
7
|
+
GEOMETRIC_ZENITH = 90.0
|
8
|
+
|
9
|
+
attr_writer :earth_radius,
|
10
|
+
:refraction,
|
11
|
+
:solar_radius
|
12
|
+
|
13
|
+
def refraction
|
14
|
+
@refraction ||= 34 / 60.0
|
15
|
+
end
|
16
|
+
|
17
|
+
def solar_radius
|
18
|
+
@solar_radius ||= 16 / 60.0
|
19
|
+
end
|
20
|
+
|
21
|
+
def earth_radius
|
22
|
+
@earth_radius ||= 6356.9 # km
|
23
|
+
end
|
24
|
+
|
25
|
+
def elevation_adjustment(elevation)
|
26
|
+
Math.acos(earth_radius / (earth_radius + (elevation / 1000.0))).to_degrees
|
27
|
+
end
|
28
|
+
|
29
|
+
def adjusted_zenith(zenith, elevation)
|
30
|
+
return zenith unless zenith == GEOMETRIC_ZENITH #only adjust for exact sunrise or sunset
|
31
|
+
zenith + solar_radius + refraction + elevation_adjustment(elevation)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Zmanim::Util
|
2
|
+
class GeoLocation
|
3
|
+
attr_reader :elevation,
|
4
|
+
:latitude,
|
5
|
+
:location_name,
|
6
|
+
:longitude,
|
7
|
+
:time_zone
|
8
|
+
|
9
|
+
def initialize(name, latitude, longitude, time_zone, elevation:nil)
|
10
|
+
self.location_name = name
|
11
|
+
self.latitude = latitude
|
12
|
+
self.longitude = longitude
|
13
|
+
self.time_zone = time_zone
|
14
|
+
self.elevation = elevation
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.GMT
|
18
|
+
new('Greenwich, England', 51.4772, 0, 'GMT')
|
19
|
+
end
|
20
|
+
|
21
|
+
def latitude=(args)
|
22
|
+
if args.respond_to?(:size) && args.size == 4
|
23
|
+
degrees, minutes, seconds, direction = *args
|
24
|
+
temp = degrees + ((minutes + (seconds / 60.0)) / 60.0)
|
25
|
+
raise ArgumentError unless %w{S N}.include? direction
|
26
|
+
raise ArgumentError unless temp >= 0
|
27
|
+
temp *= -1 if direction == 'S'
|
28
|
+
self.latitude = temp
|
29
|
+
else
|
30
|
+
raise ArgumentError unless (-90..90).include?(args)
|
31
|
+
@latitude = args
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def longitude=(args)
|
36
|
+
if args.respond_to?(:size) && args.size == 4
|
37
|
+
degrees, minutes, seconds, direction = *args
|
38
|
+
temp = degrees + ((minutes + (seconds / 60.0)) / 60.0)
|
39
|
+
raise ArgumentError unless %w{W E}.include? direction
|
40
|
+
raise ArgumentError unless temp >= 0
|
41
|
+
temp *= -1 if direction == 'W'
|
42
|
+
self.longitude = temp
|
43
|
+
else
|
44
|
+
raise ArgumentError unless (-180..180).include?(args)
|
45
|
+
@longitude = args
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def elevation=(e)
|
50
|
+
e ||= 0
|
51
|
+
raise ArgumentError unless e >= 0
|
52
|
+
@elevation = e
|
53
|
+
end
|
54
|
+
|
55
|
+
def location_name=(name)
|
56
|
+
@location_name = name
|
57
|
+
end
|
58
|
+
|
59
|
+
def time_zone=(tz)
|
60
|
+
tz = TZInfo::Timezone.get(tz) if tz.is_a?(String)
|
61
|
+
raise ArgumentError unless tz.is_a?(TZInfo::Timezone)
|
62
|
+
@time_zone = tz
|
63
|
+
end
|
64
|
+
|
65
|
+
# Number of Days to adjust due to antimeridian crossover
|
66
|
+
#
|
67
|
+
# The actual Time Zone offset may deviate from the expected offset based on the longitude
|
68
|
+
# But since the 'absolute time' calculations are always based on longitudinal offset from UTC
|
69
|
+
# for a given date, the date is presumed to only increase East of the Prime Meridian, and to
|
70
|
+
# only decrease West of it.
|
71
|
+
# For Time Zones that cross the antimeridian, the date will be artificially adjusted before calculation
|
72
|
+
# to conform with this presumption.
|
73
|
+
#
|
74
|
+
# For example, Samoa (located around 172W) uses a local offset of +14:00. When asking to calculate for
|
75
|
+
# 2017-03-15, the calculator should operate using 2017-03-14 since the expected zone is -11. After
|
76
|
+
# determining the UTC time, the local offset of +14:00 should be applied to bring the date back to 2017-03-15.
|
77
|
+
def antimeridian_adjustment
|
78
|
+
local_hours_offset = local_mean_time_offset / Zmanim::AstronomicalCalendar::HOUR_MILLIS.to_f
|
79
|
+
if local_hours_offset >= 20
|
80
|
+
1
|
81
|
+
elsif local_hours_offset <= -20
|
82
|
+
-1
|
83
|
+
else
|
84
|
+
0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Local Mean Time offset for the expected time zone (in ms).
|
89
|
+
#
|
90
|
+
# The offset is the difference between Local Mean Time at the given
|
91
|
+
# longitude and Standard Time in effect for the given time zone.
|
92
|
+
def local_mean_time_offset
|
93
|
+
(longitude * 4 * Zmanim::AstronomicalCalendar::MINUTE_MILLIS) - standard_time_offset
|
94
|
+
end
|
95
|
+
|
96
|
+
# Standard Time offset from UTC based on the provided Time Zone (in ms).
|
97
|
+
#
|
98
|
+
# This will ignore DST transformations.
|
99
|
+
def standard_time_offset
|
100
|
+
time_zone.current_period.utc_offset * 1000
|
101
|
+
end
|
102
|
+
|
103
|
+
# Time Zone offset from UTC at a given point in time (in ms).
|
104
|
+
#
|
105
|
+
# This will take into account any DST transformation in effect
|
106
|
+
# for the given Time Zone at the given time
|
107
|
+
def time_zone_offset_at(utc_time)
|
108
|
+
time_zone.period_for_utc(utc_time).utc_total_offset * 1000
|
109
|
+
end
|
110
|
+
|
111
|
+
def clone
|
112
|
+
self.class.new(location_name.dup, latitude, longitude, time_zone.dup, elevation: elevation)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|