zmanim 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|