vanadiel-time 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.
@@ -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