vanadiel-time 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ doc/
3
+ .yardoc/
4
+ pkg/
5
+ vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
@@ -0,0 +1 @@
1
+ --markup markdown --title "vanadiel-time Documentation" --protected
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2012-09-02
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'kramdown'
7
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Yuki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,47 @@
1
+ # vanadiel-time
2
+
3
+ * [Homepage](https://github.com/pasela/vanadiel-time-gem)
4
+ * [Issues](https://github.com/pasela/vanadiel-time-gem/issues)
5
+ * [Documentation](http://rubydoc.info/gems/vanadiel-time/frames)
6
+ * [Email](mailto:paselan at gmail.com )
7
+
8
+ ## Description
9
+
10
+ A library for dealing with Vana'diel time from Final Fantasy XI.
11
+ Converting between realtime and Vana'diel time, and so on.
12
+
13
+ ## Examples
14
+
15
+ require 'vanadiel/time'
16
+
17
+ p Vanadiel::Time.now #=> 1156-09-01 10:31:27
18
+ p Vanadiel::Time.new(1156, 2, 4, 10, 15, 30) #=> 1156-02-04 10:15:30
19
+
20
+ Another example:
21
+
22
+ require 'vanadiel/time'
23
+
24
+ et = Time.now
25
+ vt = Vanadiel::Time.at(et)
26
+
27
+ puts "Earth : %s" % et.strftime("%Y-%m-%d %H:%M:%S %A")
28
+ puts "Vana'diel: %s" % vt.strftime("%Y-%m-%d %H:%M:%S %A")
29
+ puts " %s %s (%d%%)" % [Vanadiel::Moon::MOONNAMES_JA[vt.moon_age],
30
+ Vanadiel::Moon::MOONNAMES[vt.moon_age],
31
+ vt.moon_percent]
32
+
33
+ output:
34
+
35
+ Earth : 2012-09-02 02:58:43 Sunday
36
+ Vana'diel: 1156-08-19 02:28:06 Iceday
37
+ 二十日余月 Waning Crescent (38%)
38
+
39
+ ## Install
40
+
41
+ $ gem install vanadiel-time
42
+
43
+ ## Copyright
44
+
45
+ Copyright (c) 2012 Yuki
46
+
47
+ See {file:LICENSE.txt} for details.
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'bundler'
7
+ rescue LoadError => e
8
+ warn e.message
9
+ warn "Run `gem install bundler` to install Bundler."
10
+ exit -1
11
+ end
12
+
13
+ begin
14
+ Bundler.setup(:development)
15
+ rescue Bundler::BundlerError => e
16
+ warn e.message
17
+ warn "Run `bundle install` to install missing gems."
18
+ exit e.status_code
19
+ end
20
+
21
+ require 'rake'
22
+
23
+ require 'rubygems/tasks'
24
+ Gem::Tasks.new
25
+
26
+ require 'rspec/core/rake_task'
27
+ RSpec::Core::RakeTask.new
28
+
29
+ task :test => :spec
30
+ task :default => :spec
31
+
32
+ require 'yard'
33
+ YARD::Rake::YardocTask.new
34
+ task :doc => :yard
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ require 'vanadiel/time'
4
+
5
+ et = Time.now
6
+ vt = Vanadiel::Time.at(et)
7
+
8
+ puts "Earth : %s" % et.strftime("%Y-%m-%d %H:%M:%S %A")
9
+ puts "Vana'diel: %s" % vt.strftime("%Y-%m-%d %H:%M:%S %A")
10
+ puts " %s %s (%d%%)" % [Vanadiel::Moon::MOONNAMES_JA[vt.moon_age],
11
+ Vanadiel::Moon::MOONNAMES[vt.moon_age],
12
+ vt.moon_percent]
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ module Vanadiel
4
+ # Vanadiel::Day has some constants about Vana'diel days.
5
+ module Day
6
+ # 火曜日
7
+ FIRESDAY = 0
8
+ # 土曜日
9
+ EARTHSDAY = 1
10
+ # 水曜日
11
+ WATERSDAY = 2
12
+ # 風曜日
13
+ WINDSDAY = 3
14
+ # 氷曜日
15
+ ICEDAY = 4
16
+ # 雷曜日
17
+ LIGHTNINGDAY = 5
18
+ # 光曜日
19
+ LIGHTSDAY = 6
20
+ # 闇曜日
21
+ DARKSDAY = 7
22
+
23
+ # Day names
24
+ DAYNAMES = ['Firesday', 'Earthsday', 'Watersday', 'Windsday', 'Iceday', 'Lightningday', 'Lightsday', 'Darksday']
25
+
26
+ # Day names for Japanese
27
+ DAYNAMES_JA = ['火曜日', '土曜日', '水曜日', '風曜日', '氷曜日', '雷曜日', '光曜日', '闇曜日']
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ module Vanadiel
4
+ # Vanadiel::Moon has some constants about Vana'diel moon phases (12 steps version).
5
+ #
6
+ # moon phase (0..11, for Japanese service)
7
+ module Moon
8
+ # 新月
9
+ NEW_MOON = 0
10
+ # 三日月
11
+ WAXING_CRESCENT1 = 1
12
+ # 七日月
13
+ WAXING_CRESCENT2 = 2
14
+ # 上弦の月
15
+ FIRST_QUARTER = 3
16
+ # 十日夜
17
+ WAXING_GIBBOUS1 = 4
18
+ # 十三夜
19
+ WAXING_GIBBOUS2 = 5
20
+ # 満月
21
+ FULL_MOON = 6
22
+ # 十六夜
23
+ WANING_GIBBOUS1 = 7
24
+ # 居待月
25
+ WANING_GIBBOUS2 = 8
26
+ # 下弦の月
27
+ LAST_QUARTER = 9
28
+ # 二十日余月
29
+ WANING_CRESCENT1 = 10
30
+ # 二十六夜
31
+ WANING_CRESCENT2 = 11
32
+
33
+ # Moon phase names
34
+ MOONNAMES = [
35
+ 'New Moon',
36
+ 'Waxing Crescent',
37
+ 'Waxing Crescent',
38
+ 'First Quarter',
39
+ 'Waxing Gibbous',
40
+ 'Waxing Gibbous',
41
+ 'Full Moon',
42
+ 'Waning Gibbous',
43
+ 'Waning Gibbous',
44
+ 'Last Quarter',
45
+ 'Waning Crescent',
46
+ 'Waning Crescent',
47
+ ]
48
+
49
+ # Moon phase names for Japanese
50
+ MOONNAMES_JA = [
51
+ '新月',
52
+ '三日月',
53
+ '七日月',
54
+ '上弦の月',
55
+ '十日夜',
56
+ '十三夜',
57
+ '満月',
58
+ '十六夜',
59
+ '居待月',
60
+ '下弦の月',
61
+ '二十日余月',
62
+ '二十六夜',
63
+ ]
64
+ end
65
+
66
+ # Vanadiel::Moon7 has some constants about Vana'diel moon phases (8 steps version).
67
+ #
68
+ # moon phase (0..7, for Non-Japanese service)
69
+ module Moon7
70
+ NEW_MOON = 0
71
+ WAXING_CRESCENT = 1
72
+ FIRST_QUARTER = 2
73
+ WAXING_GIBBOUS = 3
74
+ FULL_MOON = 4
75
+ WANING_GIBBOUS = 5
76
+ LAST_QUARTER = 6
77
+ WANING_CRESCENT = 7
78
+
79
+ # Moon phase names
80
+ MOONNAMES = [
81
+ 'New Moon',
82
+ 'Waxing Crescent',
83
+ 'First Quarter',
84
+ 'Waxing Gibbous',
85
+ 'Full Moon',
86
+ 'Waning Gibbous',
87
+ 'Last Quarter',
88
+ 'Waning Crescent',
89
+ ]
90
+ end
91
+ end
@@ -0,0 +1,533 @@
1
+ # encoding: utf-8
2
+
3
+ require 'vanadiel/day'
4
+ require 'vanadiel/moon'
5
+
6
+ module Vanadiel
7
+ # Vanadiel::Time is an abstraction of Vana'diel dates and times from Final Fantasy XI.
8
+ # Time is stored internally as the number of microseconds since C.E. 0001-01-01 00:00:00.
9
+ #
10
+ # Vana'diel time spec:
11
+ #
12
+ # One year = 12 months = 360 days
13
+ # One month = 30 days
14
+ # One day = 24 hours
15
+ # One hour = 60 minutes
16
+ # One minute = 60 seconds
17
+ # One second = 0.04 seconds of the earth's (1/25th of a second)
18
+ #
19
+ # Vana'diel second = 0.04 earth seconds (1/25th of a second)
20
+ # Vana'diel minute = 2.4 earth seconds
21
+ # Vana'diel hour = 2 minutes 24 earth seconds
22
+ # Vana'diel day = 57 minutes 36 earth seconds
23
+ # Vana'diel week = 7 hours 40 minutes 48 earth seconds
24
+ # Vana'diel calendar month = 1 day 4 hours 48 earth minutes
25
+ # Vana'diel lunar month = 3 days 14 hours 24 earth minutes
26
+ # Vana'diel year = 14 days 9 hours 36 earth minutes
27
+ #
28
+ # Each full lunar cycle lasts for 84 Vana'diel days.
29
+ # Vana'diel has 12 distinct moon phases.
30
+ # Japanese client expresses moon phases by 12 kinds of texts. (percentage is not displayed in Japanese client)
31
+ # Non-Japanese client expresses moon phases by 7 kinds of texts and percentage.
32
+ #
33
+ # C.E. = Crystal Era
34
+ #
35
+ # A.D. -91270800 => 1967/02/10 00:00:00 +0900
36
+ # C.E. 0 => 0001/01/01 00:00:00
37
+ #
38
+ # A.D. 2002/01/01(Tue) 00:00:00 JST
39
+ # C.E. 0886/01/01(Fir) 00:00:00
40
+ #
41
+ # A.D. 2047/10/22(Tue) 01:00:00 JST
42
+ # C.E. 2047/10/22(Wat) 01:00:00
43
+ #
44
+ # A.D. 2047/10/21(Mon) 15:37:30 UTC
45
+ # C.E. 2047/10/21(Win) 15:37:30
46
+ class Time
47
+ # vanadiel-time version
48
+ VERSION = "0.1.0"
49
+
50
+ ONE_SECOND = 1000000
51
+ ONE_MINUTE = 60 * ONE_SECOND
52
+ ONE_HOUR = 60 * ONE_MINUTE
53
+ ONE_DAY = 24 * ONE_HOUR
54
+ ONE_WEEK = 8 * ONE_DAY
55
+ ONE_MONTH = 30 * ONE_DAY
56
+ ONE_YEAR = 360 * ONE_DAY
57
+
58
+ VANA_TIME_SCALE = 25 # Vana'diel time goes 25 times faster than the Earth
59
+ VANA_BASE_YEAR = 886
60
+ VANA_BASE_TIME = (VANA_BASE_YEAR * ONE_YEAR) / VANA_TIME_SCALE
61
+ EARTH_BASE_TIME = 1009810800 * ONE_SECOND # 2002/01/01 00:00:00.000 JST
62
+ DIFF_TIME = VANA_BASE_TIME - EARTH_BASE_TIME
63
+ MOON_CYCLE_DAYS = 84 # Vana'diel moon cycle lasts 84 days
64
+
65
+ # @return [Bignum] the value of the time as microseconds since C.E. 0001-01-01 00:00:00
66
+ attr_reader :time
67
+
68
+ # @return [Fixnum] the year for time
69
+ attr_reader :year
70
+
71
+ # @return [Fixnum] the month of the year (1..12) for time
72
+ attr_reader :month
73
+
74
+ # @return [Fixnum] the month of the year (1..12) for time
75
+ alias_method :mon, :month
76
+
77
+ # @return [Fixnum] the day of the month (1..30) for time
78
+ attr_reader :mday
79
+ alias_method :day, :mday
80
+
81
+ # @return [Fixnum] the hour of the day (0..23) for time
82
+ attr_reader :hour
83
+
84
+ # @return [Fixnum] the minute of the hour (0..59) for time
85
+ attr_reader :min
86
+
87
+ # @return [Fixnum] the second of the minute (0..59) for time
88
+ attr_reader :sec
89
+
90
+ # @return [Fixnum] just the number of microseconds (0..999999) for time
91
+ attr_reader :usec
92
+
93
+ # @return [Fixnum] an integer representing the day of the week, 0..7, with Firesday == 0
94
+ attr_reader :wday
95
+
96
+ # @return [Fixnum] an integer representing the day of the year (1..360)
97
+ attr_reader :yday
98
+
99
+ # @return [Fixnum] an integer representing the moon age (0..11), for Japanese service
100
+ attr_reader :moon_age
101
+ alias_method :moon_age12, :moon_age
102
+
103
+ # @return [Fixnum] an integer representing the moon age (0..7), for Non-Japanese service
104
+ attr_reader :moon_age7
105
+
106
+ # @return [Fixnum] an integer representing the moon phase percentage (0..100), for Non-Japanese service
107
+ attr_reader :moon_percent
108
+
109
+ # @return [Fixnum] the number of microseconds of the moon
110
+ attr_reader :time_of_moon
111
+
112
+ # It is initialized to the current time if no argument.
113
+ # If one or more arguments specified, the time is initialized to the specified time.
114
+ #
115
+ # @overload new()
116
+ # It is initialized to the current time if no argument.
117
+ # @overload new(year, mon = 1, day = 1, hour = 0, min = 0, sec = 0, usec = 0)
118
+ # If one or more arguments specified, the time is initialized to the specified time.
119
+ # @param [Integer] year the year part (1..n)
120
+ # @param [Integer] mon the month part (1..12)
121
+ # @param [Integer] day the day of month part (1..30)
122
+ # @param [Integer] hour the hour part (0..23)
123
+ # @param [Integer] min the minute part (0..59)
124
+ # @param [Integer] sec the second part (0..59)
125
+ # @param [Integer] usec the microsecond part (0..999999)
126
+ def initialize(*args)
127
+ self.time = args.empty? ? self.class.earth_to_vana(::Time.now.to_f * ONE_SECOND) : self.class.ymdhms_to_usec(*args)
128
+ end
129
+
130
+ # Synonym for Vanadiel::Time.new. Returns a new time object initialized to the current time.
131
+ #
132
+ # @return [Vanadiel::Time] the time object initialized to the current time.
133
+ def self.now
134
+ self.new
135
+ end
136
+
137
+ # Same as Vanadiel::Time.new, but the year is required.
138
+ #
139
+ # @return [Vanadiel::Time] the time object initialized to the specified time.
140
+ def self.mktime(year, *rest_part)
141
+ args = [year, *rest_part]
142
+ self.new(*args)
143
+ end
144
+
145
+ # Creates a new time object with the value given by time.
146
+ #
147
+ # @overload at(time)
148
+ # @param [::Time, Vanadiel::Time] time the time object
149
+ # @return [Vanadiel::Time] the time object initialized to the specified time.
150
+ # @overload at(seconds, usec = 0)
151
+ # @param [Integer, Float] sec seconds from C.E. 0001-01-01 00:00:00
152
+ # @param [Integer] usec the microseconds
153
+ # @return [Vanadiel::Time] the time object initialized to the specified time.
154
+ def self.at(time, usec = 0)
155
+ obj = self.new
156
+ if time.is_a? ::Time
157
+ obj.time = self.earth_to_vana(time.to_f * ONE_SECOND)
158
+ elsif time.is_a?(Vanadiel::Time)
159
+ obj.time = time.time
160
+ elsif time.is_a?(Integer) || time.is_a?(Float)
161
+ obj.time = ((time * ONE_SECOND) + usec).to_i
162
+ else
163
+ raise ArgumentError, 'invalid argument'
164
+ end
165
+ obj
166
+ end
167
+
168
+ # Converts microseconds as Vana'diel time to microseconds as the Earth time from the Epoch.
169
+ #
170
+ # @param [Integer] vana_time microseconds as Vana'diel time
171
+ # @return [Integer] microseconds as the Earth time
172
+ def self.vana_to_earth(vana_time)
173
+ earth = (((vana_time + ONE_YEAR) / VANA_TIME_SCALE) - DIFF_TIME)
174
+ end
175
+
176
+ # Converts microseconds as the Earth time to microseconds as Vana'diel time from the Epoch.
177
+ #
178
+ # @param [Integer] earth_time microseconds as the Earth time
179
+ # @return [Integer] microseconds as Vana'diel time
180
+ def self.earth_to_vana(earth_time)
181
+ (earth_time + DIFF_TIME) * VANA_TIME_SCALE - ONE_YEAR
182
+ end
183
+
184
+ # Returns true if time represents Firesday.
185
+ #
186
+ # @return [Boolean] true if Firesday
187
+ def firesday?; @wday == Vanadiel::Day::FIRESDAY; end
188
+
189
+ # Returns true if time represents Earthsday.
190
+ #
191
+ # @return [Boolean] true if Earthsday
192
+ def earthsday?; @wday == Vanadiel::Day::EARTHSDAY; end
193
+
194
+ # Returns true if time represents Watersday.
195
+ #
196
+ # @return [Boolean] true if Watersday
197
+ def watersday?; @wday == Vanadiel::Day::WATERSDAY; end
198
+
199
+ # Returns true if time represents Windsday.
200
+ #
201
+ # @return [Boolean] true if Windsday
202
+ def windsday?; @wday == Vanadiel::Day::WINDSDAY; end
203
+
204
+ # Returns true if time represents Iceday.
205
+ #
206
+ # @return [Boolean] true if Iceday
207
+ def iceday?; @wday == Vanadiel::Day::ICEDAY; end
208
+
209
+ # Returns true if time represents Lightningday.
210
+ #
211
+ # @return [Boolean] true if Lightningday
212
+ def lightningday?; @wday == Vanadiel::Day::LIGHTNINGDAY; end
213
+
214
+ # Returns true if time represents Lightsday.
215
+ #
216
+ # @return [Boolean] true if Lightsday
217
+ def lightsday?; @wday == Vanadiel::Day::LIGHTSDAY; end
218
+
219
+ # Returns true if time represents Darksday.
220
+ #
221
+ # @return [Boolean] true if Darksday
222
+ def darksday?; @wday == Vanadiel::Day::DARKSDAY; end
223
+
224
+ # Format Vana'diel time according to the directives in the format string.
225
+ # The directives begins with a percent (%) character. Any text not listed
226
+ # as a directive will be passed through to the output string.
227
+ #
228
+ # The directive consists of a percent (%) character, zero or more flags,
229
+ # optional minimum field width and a conversion specifier as follows.
230
+ #
231
+ # %<flags><width><conversion>
232
+ #
233
+ # Flags:
234
+ #
235
+ # - don't pad a numerical output.
236
+ # _ use spaces for padding.
237
+ # 0 use zeros for padding.
238
+ # ^ upcase the result string.
239
+ # # change case.
240
+ #
241
+ # The minimum field width specifies the minimum width.
242
+ #
243
+ # Format directives:
244
+ #
245
+ # Date (Year, Month, Day):
246
+ # %Y - Year with century (can be negative)
247
+ # -0001, 0000, 1995, 2009, 14292, etc.
248
+ # %C - year / 100 (round down. 20 in 2009)
249
+ # %y - year % 100 (00..99)
250
+ #
251
+ # %m - Month of the year, zero-padded (01..12)
252
+ # %_m blank-padded ( 1..12)
253
+ # %-m no-padded (1..12)
254
+ #
255
+ # %d - Day of the month, zero-padded (01..30)
256
+ # %-d no-padded (1..30)
257
+ # %e - Day of the month, blank-padded ( 1..30)
258
+ #
259
+ # %j - Day of the year (001..360)
260
+ #
261
+ # Time (Hour, Minute, Second, Subsecond):
262
+ # %H - Hour of the day, 24-hour clock, zero-padded (00..23)
263
+ # %k - Hour of the day, 24-hour clock, blank-padded ( 0..23)
264
+ #
265
+ # %M - Minute of the hour (00..59)
266
+ #
267
+ # %S - Second of the minute (00..59)
268
+ #
269
+ # %L - Millisecond of the second (000..999)
270
+ # %N - Fractional seconds digits, default is 6 digits (microsecond)
271
+ # %3N millisecond (3 digits)
272
+ # %6N microsecond (6 digits)
273
+ #
274
+ # Weekday:
275
+ # %A - The full weekday name (``Firesday'')
276
+ # %^A uppercased (``FIRESDAY'')
277
+ # %w - Day of the week (Firesday is 0, 0..7)
278
+ #
279
+ # Seconds since the Epoch:
280
+ # %s - Number of seconds since 0001-01-01 00:00:00
281
+ #
282
+ # Literal string:
283
+ # %n - Newline character (\n)
284
+ # %t - Tab character (\t)
285
+ # %% - Literal ``%'' character
286
+ #
287
+ # Combination:
288
+ # %F - The ISO 8601 date format (%Y-%m-%d)
289
+ # %X - Same as %T
290
+ # %R - 24-hour time (%H:%M)
291
+ # %T - 24-hour time (%H:%M:%S)
292
+ #
293
+ # @param [String] format the format string
294
+ # @return [String] formatted string
295
+ def strftime(format)
296
+ source = { 'Y' => @year, 'C' => @year / 100, 'y' => @year % 100,
297
+ 'm' => @month, 'd' => @mday, 'e' => @mday, 'j' => @yday,
298
+ 'H' => @hour, 'k' => @hour, 'M' => @min, 'S' => @sec, 'L' => @usec, 'N' => @usec,
299
+ 'A' => @wday, 'w' => @wday, 's' => @time,
300
+ 'n' => "\n", 't' => "\t", '%' => '%' }
301
+ default_padding = { 'e' => ' ', 'k' => ' ', 'A' => ' ', 'n' => ' ', 't' => ' ', '%' => ' ' }
302
+ default_padding.default = '0'
303
+ default_width = { 'y' => 2, 'm' => 2, 'd' => 2, 'e' => 2, 'H' => 2, 'k' => 2, 'M' => 2, 'S' => 2,
304
+ 'j' => 3, 'L' => 3,
305
+ 'N' => 6 }
306
+ default_width.default = 0
307
+
308
+ format.gsub(/%([-_0^#]+)?(\d+)?([FXRT])/) {
309
+ case $3
310
+ when 'F' then '%Y-%m-%d'
311
+ when 'T', 'X' then '%H:%M:%S'
312
+ when 'R' then '%H:%M'
313
+ end
314
+ }.gsub(/%([-_0^#]+)?(\d+)?([YCymdejHkMSLNAawsnt%])/) {|s|
315
+ flags = $1; width = $2.to_i; conversion = $3; upcase = false
316
+ padding = default_padding[conversion]
317
+ width = default_width[conversion] if width.zero?
318
+ v = source[conversion]
319
+
320
+ flags.each_char {|c|
321
+ case c
322
+ when '-' then padding = nil
323
+ when '_' then padding = ' '
324
+ when '0' then padding = '0'
325
+ when '^', '#' then upcase = true
326
+ end
327
+ } if flags
328
+
329
+ case conversion
330
+ when 'L', 'N'
331
+ if (width <= 6)
332
+ v = v / (100000 / (10 ** (width - 1)))
333
+ else
334
+ v = v * (10 ** (width - 6))
335
+ end
336
+ when 'A'
337
+ v = Vanadiel::Day::DAYNAMES[v]
338
+ end
339
+
340
+ v = v.to_s
341
+ if width > 0 && padding && v.length < width
342
+ v = (padding * (width - v.length)) + v
343
+ end
344
+
345
+ upcase ? v.upcase : v
346
+ }
347
+ end
348
+
349
+ # Adds some number of seconds (possibly fractional) to time and returns that value as a new time.
350
+ #
351
+ # @param [Integer, Float] sec seconds
352
+ # @return [Vanadiel::Time] new time
353
+ def +(sec)
354
+ self.class.at((@time + (sec * ONE_SECOND)) / ONE_SECOND)
355
+ end
356
+
357
+ # Returns a new time that represents the difference between two times,
358
+ # or subtracts the given number of seconds in numeric from time.
359
+ #
360
+ # @overload -(time)
361
+ # Returns a new time that represents the difference between two times.
362
+ # @param [::Time, Vanadiel::Time] other_time the other time object
363
+ # @return [Float] seconds that represents the difference
364
+ # @overload -(seconds)
365
+ # Subtracts the given number of seconds in numeric from time.
366
+ # @param [Integer, Float] sec seconds
367
+ # @return [Vanadiel::Time] new time
368
+ def -(time)
369
+ if time.is_a? ::Time
370
+ (@time.to_f - self.class.earth_to_vana(time.to_f * ONE_SECOND)) / ONE_SECOND
371
+ elsif time.is_a?(Vanadiel::Time)
372
+ (@time.to_f - time.time) / ONE_SECOND
373
+ elsif time.is_a?(Integer) || time.is_a?(Float)
374
+ self.class.at((@time / ONE_SECOND) - time)
375
+ else
376
+ raise ArgumentError, 'invalid argument'
377
+ end
378
+ end
379
+
380
+ # Compares time with other time.
381
+ #
382
+ # @param [Vanadiel::Time] other_time the other time
383
+ # @return [-1] if the time is earlier than the other time.
384
+ # @return [0] if the time is same as the other time.
385
+ # @return [1] if the time is later than the other time.
386
+ # @return [nil] if it cannot compare.
387
+ def <=>(other_time)
388
+ @time <=> other_time.time
389
+ end
390
+
391
+ # Returns the value of time as a floating point number of seconds since C.E. 0001-01-01 00:00:00.
392
+ #
393
+ # @return [Float] seconds
394
+ def to_i
395
+ @time / ONE_SECOND
396
+ end
397
+
398
+ # Returns the value of time as an integer number of seconds since C.E. 0001-01-01 00:00:00.
399
+ #
400
+ # @return [Integer] seconds
401
+ def to_f
402
+ @time.to_f / ONE_SECOND
403
+ end
404
+
405
+ # Returns a string representing time. Equivalent to calling #strftime with
406
+ # a format string of "%Y-%m-%d %H:%M:%S".
407
+ #
408
+ # @return [String] the string representing time
409
+ def to_s
410
+ self.strftime('%Y-%m-%d %H:%M:%S')
411
+ end
412
+
413
+ # Returns the value of time as the Earth time object.
414
+ #
415
+ # @return [::Time] the Earth time object
416
+ def to_earth_time
417
+ ::Time.at(self.class.vana_to_earth(@time) / ONE_SECOND)
418
+ end
419
+
420
+ # Returns a hash code for this time object.
421
+ #
422
+ # @return [Fixnum] the hash code
423
+ def hash; @time.hash ^ self.class.hash; end
424
+
425
+ # Returns true if time and other time are the same time.
426
+ #
427
+ # @return [Boolean] true if same
428
+ def ==(other); @time == other.time; end
429
+
430
+ # Returns true if time and other time are both Vanadiel::Time objects with the same time.
431
+ #
432
+ # @return [Boolean] true if same
433
+ def eql?(other); self.hash == other.hash; end
434
+
435
+ # Manually sets the time and recompute all fields.
436
+ #
437
+ # @param [Integer] time an integer number of microseconds
438
+ # @note This accessor is used internally.
439
+ def time=(time)
440
+ @time = time
441
+ compute_fields
442
+ end
443
+
444
+ def marshal_dump
445
+ @time
446
+ end
447
+
448
+ def marshal_load(obj)
449
+ self.time = obj
450
+ end
451
+
452
+ # Converts to the value of time as an integer number of microseconds since C.E. 0001-01-01 00:00:00.
453
+ #
454
+ # @param [Integer] year the year part (1..n)
455
+ # @param [Integer] mon the month part (1..12)
456
+ # @param [Integer] day the day of month part (1..30)
457
+ # @param [Integer] hour the hour part (0..23)
458
+ # @param [Integer] min the minute part (0..59)
459
+ # @param [Integer] sec the second part (0..59)
460
+ # @param [Integer] usec the microsecond part (0..999999)
461
+ # @return [Integer] microseconds as Vana'diel time
462
+ def self.ymdhms_to_usec(year, mon = 1, day = 1, hour = 0, min = 0, sec = 0, usec = 0)
463
+ raise ArgumentError, 'year out of range' if year < 0
464
+ raise ArgumentError, 'mon out of range' if mon < 1 || mon > 12
465
+ raise ArgumentError, 'day out of range' if day < 1 || day > 30
466
+ raise ArgumentError, 'hour out of range' if hour < 0 || hour > 23
467
+ raise ArgumentError, 'min out of range' if min < 0 || min > 59
468
+ raise ArgumentError, 'sec out of range' if sec < 0 || sec > 59
469
+ raise ArgumentError, 'usec out of range' if usec < 0 || usec > 999999
470
+ ((year - 1) * ONE_YEAR) + ((mon - 1) * ONE_MONTH) + ((day - 1) * ONE_DAY) + (hour * ONE_HOUR) + (min * ONE_MINUTE) + (sec * ONE_SECOND) + usec
471
+ end
472
+
473
+ private
474
+
475
+ # Computes fields by its value of time.
476
+ def compute_fields
477
+ @year = (@time / ONE_YEAR).floor + 1
478
+ @month = (@time % ONE_YEAR / ONE_MONTH).floor + 1
479
+ @mday = (@time % ONE_MONTH / ONE_DAY).floor + 1
480
+ @hour = (@time % ONE_DAY / ONE_HOUR).floor
481
+ @min = (@time % ONE_HOUR / ONE_MINUTE).floor
482
+ @sec = (@time % ONE_MINUTE / ONE_SECOND).floor
483
+ @usec = (@time % ONE_SECOND).floor
484
+
485
+ @wday = (@time % ONE_WEEK / ONE_DAY).floor
486
+ @yday = ((@month - 1) * 30) + @mday
487
+
488
+ # MOON_BASE_TIME = 0 - (ONE_DAY * 12) # Start of New moon (10%)
489
+ #
490
+ # moon_time = @time - MOON_BASE_TIME
491
+ # @moon_age = (moon_time / ONE_DAY / 7 % (MAX_MOON_AGE + 1)).floor
492
+ # @time_of_moon = ((moon_time / ONE_DAY % 7) * ONE_DAY).floor
493
+ # + (@hour * ONE_HOUR)
494
+ # + (@min * ONE_MINUTE)
495
+ # + (@sec * ONE_SECOND)
496
+ # + (@usec)
497
+
498
+ # C.E. 0001/01/01 00:00:00 => WXC 19%
499
+ # C.E. 0886/01/01 00:00:00 => NM 10%
500
+ #
501
+ # 0% NM 7% WXC 40% FQM 57% WXG 90% FM 93% WNG 60% LQM 43% WNC 10% NM
502
+ # 2% NM 10% WXC 43% FQM 60% WXG 93% FM 90% WNG 57% LQM 40% WNC 7% NM
503
+ # 5% NM 12% WXC 45% FQM 62% WXG 95% FM 88% WNG 55% LQM 38% WNC 5% NM
504
+ # 14% WXC 48% FQM 64% WXG 98% FM 86% WNG 52% LQM 36% WNC 2% NM
505
+ # 17% WXC 50% FQM 67% WXG 100% FM 83% WNG 50% LQM 33% WNC
506
+ # 19% WXC 52% FQM 69% WXG 98% FM 81% WNG 48% LQM 31% WNC
507
+ # 21% WXC 55% FQM 71% WXG 95% FM 79% WNG 45% LQM 29% WNC
508
+ # 24% WXC 74% WXG 76% WNG 26% WNC
509
+ # 26% WXC 76% WXG 74% WNG 24% WNC
510
+ # 29% WXC 79% WXG 71% WNG 21% WNC
511
+ # 31% WXC 81% WXG 69% WNG 19% WNC
512
+ # 33% WXC 83% WXG 67% WNG 17% WNC
513
+ # 36% WXC 86% WXG 64% WNG 14% WNC
514
+ # 38% WXC 88% WXG 62% WNG 12% WNC
515
+ days = (@time / ONE_DAY).floor
516
+ @moon_percent = (((days + 8) % MOON_CYCLE_DAYS) * (200.0 / MOON_CYCLE_DAYS)).round
517
+ @moon_percent = 200 - @moon_percent if @moon_percent > 100
518
+
519
+ @moon_age = ((days + 12) / 7) % 12
520
+ @moon_age7 = case @moon_age
521
+ when 0 then 0
522
+ when 1, 2 then 1
523
+ when 3 then 2
524
+ when 4, 5 then 3
525
+ when 6 then 4
526
+ when 7, 8 then 5
527
+ when 9 then 6
528
+ when 10, 11 then 7
529
+ end
530
+ @time_of_moon = (((days + 12) % 7) * ONE_DAY) + (@hour * ONE_HOUR) + (@min * ONE_MINUTE) + (@sec * ONE_SECOND) + @usec
531
+ end
532
+ end
533
+ end