PageRenderTime 73ms CodeModel.GetById 40ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kernel/bootstrap/Time.rb

https://github.com/timfel/maglev
Ruby | 553 lines | 408 code | 96 blank | 49 comment | 42 complexity | 610a4f5085224c94429fa20353d344a4 MD5 | raw file
  1. # Time in Ruby is identically Smalltalk RubyTime
  2. #
  3. # The Smalltalk RubyTime class holds a single value, @microseconds, which
  4. # is the number of microseconds since the epoch.
  5. class Time
  6. ######################################################################
  7. # BEGIN RUBINIUS ( with Gemstone modifications)
  8. ######################################################################
  9. include Comparable
  10. ZoneOffset = {
  11. 'UTC' => 0,
  12. # ISO 8601
  13. 'Z' => 0,
  14. # RFC 822
  15. 'UT' => 0, 'GMT' => 0,
  16. 'EST' => -5, 'EDT' => -4,
  17. 'CST' => -6, 'CDT' => -5,
  18. 'MST' => -7, 'MDT' => -6,
  19. 'PST' => -8, 'PDT' => -7,
  20. # Following definition of military zones is original one.
  21. # See RFC 1123 and RFC 2822 for the error in RFC 822.
  22. 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6,
  23. 'G' => +7, 'H' => +8, 'I' => +9, 'K' => +10, 'L' => +11, 'M' => +12,
  24. 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6,
  25. 'T' => -7, 'U' => -8, 'V' => -9, 'W' => -10, 'X' => -11, 'Y' => -12,
  26. }
  27. MonthValue = {
  28. 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6,
  29. 'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' =>10, 'NOV' =>11, 'DEC' =>12
  30. }
  31. LeapYearMonthDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  32. CommonYearMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  33. FULL_DAY_NAME = ['Sunday, Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
  34. FULL_MONTH_NAME = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
  35. RFC2822_DAY_NAME = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  36. RFC2822_MONTH_NAME = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
  37. PRE_EPOCH_DAYS = 719468
  38. TM_FIELDS = {
  39. :sec => 0,
  40. :min => 1,
  41. :hour => 2,
  42. :mday => 3,
  43. :mon => 4,
  44. :year => 5,
  45. :wday => 6,
  46. :yday => 7,
  47. :isdst => 8,
  48. }
  49. # TIMEVAL_FIELDS = { # not used in Gemstone
  50. # :sec => 0,
  51. # :usec => 1,
  52. #}
  53. #--
  54. # TODO: doesn't load nsec or ivars
  55. #++
  56. def self._load(data) # used by Marshal
  57. raise TypeError, 'marshaled time format differ' unless data.length == 8
  58. major, minor = data.unpack 'VV'
  59. if (major & (1 << 31)) == 0 then
  60. at major, minor
  61. else
  62. major &= ~(1 << 31)
  63. is_gmt = (major >> 30) & 0x1
  64. year = ((major >> 14) & 0xffff) + 1900
  65. mon = ((major >> 10) & 0xf) + 1
  66. mday = (major >> 5) & 0x1f
  67. hour = major & 0x1f
  68. min = (minor >> 26) & 0x3f
  69. sec = (minor >> 20) & 0x3f
  70. isdst = false
  71. usec = minor & 0xfffff
  72. time = self.gm(year, mon, mday, hour, min, sec, usec)
  73. time.localtime # unless is_gmt.zero? # HACK MRI ignores the is_gmt flag
  74. time
  75. end
  76. end
  77. #--
  78. # TODO: doesn't dump nsec or ivars
  79. #++
  80. def _dump(limit = nil) # used by marshal
  81. is_gmt = @_st_is_gmt
  82. unless is_gmt
  83. # Maglev deviation, always dumping in GMT format
  84. t = self.dup.gmtime
  85. return t._dump
  86. end
  87. tm = @_st_tm
  88. year = tm[TM_FIELDS[:year]]
  89. if (year & 0xffff) != year then
  90. raise ArgumentError, "year too big to marshal: #{year}"
  91. end
  92. major = 1 << 31 | # 1 bit
  93. (is_gmt ? 1 : 0) << 30 | # 1 bit
  94. tm[TM_FIELDS[:year]] << 14 | # 16 bits
  95. tm[TM_FIELDS[:mon]] << 10 | # 4 bits
  96. tm[TM_FIELDS[:mday]] << 5 | # 5 bits
  97. tm[TM_FIELDS[:hour]] # 5 bits
  98. minor = tm[TM_FIELDS[:min]] << 26 | # 6 bits
  99. tm[TM_FIELDS[:sec]] << 20 | # 6 bits
  100. self.usec # 20 bits # GEMSTONE changes
  101. [major, minor].pack 'VV'
  102. end
  103. def dup
  104. t = self.class.allocate # Gemstone changes
  105. t.__init(@_st_microseconds, @_st_is_gmt)
  106. end
  107. def self.local(first, *args)
  108. if args.size == 9
  109. second = first
  110. minute = args[0]
  111. hour = args[1]
  112. day = args[2]
  113. month = args[3]
  114. year = args[4]
  115. # args[5] is wday , not used # gemstone comments
  116. # args[6] is yday , not used
  117. isdst = args[7] ? 1 : 0
  118. # args[8] is tz , not used yet
  119. usec = 0
  120. else
  121. year = first
  122. month = self.__monthname_to_num(args[0])
  123. day = args[1] || 1
  124. hour = args[2] || 0
  125. minute = args[3] || 0
  126. second = args[4] || 0
  127. usec = args[5] || 0
  128. isdst = -1 # ask C mktime() to determine dst
  129. end
  130. self.__mktime(second, minute, hour, day, month, year, usec, isdst, false)
  131. end
  132. def self.mktime(first, *args)
  133. self.local(first, *args)
  134. end
  135. def self.__monthname_to_num(ma)
  136. # resolve month names to numbers
  137. if ma._isInteger
  138. month = ma
  139. else
  140. ma = Type.coerce_to_or_nil(ma, String, :to_str)
  141. if ma._isString
  142. mint = ma.to_i
  143. if mint._equal?(0)
  144. month = MonthValue[ ma.upcase] || raise(ArgumentError.new('argument out of range'))
  145. else
  146. month = mint
  147. end
  148. else
  149. month = ma || 1
  150. end
  151. end
  152. month
  153. end
  154. def self.gm(first, *args)
  155. if args.size == 9
  156. second = first
  157. minute = args[0]
  158. hour = args[1]
  159. day = args[2]
  160. month = args[3]
  161. year = args[4]
  162. usec = 0
  163. else
  164. # set argument defaults
  165. year = first
  166. month = self.__monthname_to_num(args[0])
  167. day = args[1] || 1
  168. hour = args[2] || 0
  169. minute = args[3] || 0
  170. second = args[4] || 0
  171. usec = args[5] || 0
  172. end
  173. self.__mktime(second, minute, hour, day, month, year, usec, 0, true)
  174. end
  175. # Gemstone , new implementations at end of file
  176. # def self.at ; end
  177. def inspect
  178. if @_st_is_gmt
  179. strftime("%a %b %d %H:%M:%S UTC %Y")
  180. else
  181. strftime("%a %b %d %H:%M:%S %z %Y")
  182. end
  183. end
  184. def seconds
  185. @_st_microseconds.__divide(1_000_000) # Gemstone
  186. end
  187. def +(other)
  188. microsecs = @_st_microseconds
  189. if other._isInteger
  190. microsecs += other * 1_000_000
  191. elsif other._kind_of?(Time)
  192. raise TypeError , 'Time#+ , arg may not be a Time'
  193. else
  194. # addition with rounding to nearest microsecond
  195. other = Type.coerce_to(other, Float, :to_f)
  196. if other > 0.0
  197. microsecs += ((other + 0.0000005) * 1_000_000.0).to_i
  198. elsif other <= 0.0
  199. unless other == 0.0
  200. microsecs += ((other - 0.0000005) * 1_000_000.0).to_i
  201. end
  202. end
  203. end
  204. t = self.class.allocate
  205. t.__init(microsecs, @_st_is_gmt)
  206. end
  207. def -(other)
  208. microsecs = @_st_microseconds
  209. if other._isInteger
  210. microsecs -= other * 1_000_000
  211. elsif other._kind_of?(Time)
  212. delta = microsecs - other.__microsecs
  213. return (delta.to_f).__divide(1_000_000.0)
  214. else
  215. other = Type.coerce_to(other, Float, :to_f)
  216. microsecs -= (other * 1_000_000.0).to_i
  217. end
  218. t = self.class.allocate
  219. t.__init(microsecs, @_st_is_gmt)
  220. end
  221. def succ
  222. self + 1
  223. end
  224. def <=>(other)
  225. if other._kind_of?(Time)
  226. @_st_microseconds <=> other.__microsecs
  227. else
  228. nil
  229. end
  230. end
  231. # It seems that MRI return nil if other isn't a Time object
  232. def ==(other)
  233. result = self <=> other
  234. result._equal?(nil) ? result : result._equal?(0)
  235. end
  236. def hash
  237. micro_sec = @_st_microseconds
  238. (micro_sec.__divide(1_000_000)) ^ (micro_sec % 1_000_000)
  239. end
  240. def eql?(other)
  241. (self <=> other)._equal?(0)
  242. end
  243. def asctime
  244. strftime("%a %b %e %H:%M:%S %Y")
  245. end
  246. def hour
  247. @_st_tm[2]
  248. end
  249. def min
  250. @_st_tm[1]
  251. end
  252. def sec
  253. @_st_tm[0]
  254. end
  255. def day
  256. @_st_tm[3]
  257. end
  258. def year
  259. @_st_tm[5] + 1900
  260. end
  261. def yday
  262. @_st_tm[7] + 1
  263. end
  264. def wday
  265. @_st_tm[6]
  266. end
  267. def zone
  268. strftime("%Z")
  269. end
  270. def mon
  271. @_st_tm[4] + 1
  272. end
  273. def gmt?
  274. @_st_is_gmt
  275. end
  276. def usec
  277. @_st_microseconds % 1_000_000
  278. end
  279. def to_i
  280. @_st_microseconds.__divide(1_000_000)
  281. end
  282. def to_f
  283. @_st_microseconds.__divide(1_000_000.0)
  284. end
  285. ##
  286. # Returns:
  287. # [ sec, min, hour, day, month, year, wday, yday, isdst, zone ]
  288. def to_a
  289. [sec, min, hour, day, month, year, wday, yday, isdst, zone]
  290. end
  291. def gmt_offset
  292. return 0 if @_st_is_gmt
  293. other = dup.gmtime
  294. if year != other.year
  295. offset = year < other.year ? -1 : 1
  296. elsif month != other.month
  297. offset = month < other.month ? -1 : 1
  298. elsif mday != other.mday
  299. offset = mday < other.mday ? -1 : 1
  300. else
  301. offset = 0
  302. end
  303. offset *= 24
  304. offset += hour - other.hour
  305. offset *= 60
  306. offset += min - other.min
  307. offset *= 60
  308. offset += sec - other.sec
  309. end
  310. def localtime
  311. if @_st_is_gmt
  312. self.__set_tmarray(false)
  313. end
  314. self
  315. end
  316. def gmtime
  317. unless @_st_is_gmt
  318. self.__set_tmarray(true)
  319. end
  320. self
  321. end
  322. def dst?
  323. @_st_tm[8]._not_equal?(0)
  324. end
  325. def getlocal
  326. dup.localtime
  327. end
  328. def getgm
  329. dup.gmtime
  330. end
  331. def self.__to_int(arg)
  332. if arg._isString
  333. arg.to_i
  334. else
  335. Type.coerce_to(arg, Integer, :to_int )
  336. end
  337. end
  338. def self.__mktime(sec, min, hour, mday, mon, year, usec, isdst=-1, from_gmt=false)
  339. sec = __to_int(sec)
  340. min = __to_int(min)
  341. hour = __to_int(hour)
  342. mday = __to_int(mday)
  343. mon = __to_int(mon)
  344. # range checks on above args done in C in __c_mktime
  345. year = __to_int(year)
  346. usec = __to_int(usec)
  347. sec += usec.__divide(1000000)
  348. # This logic is taken from MRI, on how to deal with 2 digit dates.
  349. if year < 200
  350. if 0 <= year and year < 39
  351. warn "2 digit year used: #{year}"
  352. year += 2000
  353. elsif 69 <= year and year < 139
  354. warn "2 or 3 digit year used: #{year}"
  355. year += 1900
  356. end
  357. end
  358. year = year - 1900
  359. # build args to C mktime , args are in local time
  360. args = [ sec, min, hour, mday, mon, year,
  361. 0, # wday
  362. 0, # yday,
  363. isdst # negative means ask mktime() to determine Daylight/Standard
  364. ]
  365. atime_t = __c_mktime(args, from_gmt)
  366. t = self.allocate
  367. microsecs = (atime_t * 1000000) + (usec % 1000000)
  368. t.__init(microsecs, from_gmt)
  369. end
  370. ##
  371. # +sec+ and +usec+ are always given in gmt here.
  372. #
  373. # +want_gmt+ says whether the caller wants a gmtime or local time object.
  374. def at_gmt(sec, usec, want_gmt) # GEMSTONE
  375. if sec._isInteger
  376. usec = usec ? usec.to_i : 0
  377. elsif usec
  378. sec = Type.coerce_to(sec, Integer, :to_i)
  379. usec = usec.to_i
  380. else
  381. sec = Type.coerce_to(sec, Float, :to_f)
  382. usec = ((sec % 1) * 1_000_000).to_i
  383. sec = sec.to_i
  384. end
  385. usec = usec + (sec * 1000000)
  386. @_st_microseconds = usec
  387. if want_gmt
  388. force_gmtime
  389. else
  390. force_localtime
  391. end
  392. end
  393. def self.utc(first, *args) # GEMSTONE
  394. gm(first, *args)
  395. end
  396. alias_method :utc?, :gmt?
  397. alias_method :month, :mon
  398. alias_method :ctime, :asctime
  399. alias_method :mday, :day
  400. alias_method :to_i, :seconds
  401. alias_method :to_s, :inspect
  402. alias_method :tv_sec, :seconds
  403. alias_method :tv_usec, :usec
  404. alias_method :utc, :gmtime
  405. alias_method :isdst, :dst?
  406. alias_method :utc_offset, :gmt_offset
  407. alias_method :gmtoff, :gmt_offset
  408. alias_method :getutc, :getgm
  409. ######################################################################
  410. # END Modified RUBINIUS
  411. ######################################################################
  412. # begin Gemstone specific code
  413. class_primitive 'new'
  414. class_primitive 'now'
  415. class_primitive_nobridge 'allocate' , '_basicNew'
  416. class_primitive_nobridge '__c_mktime' , 'mktime:fromGmt:'
  417. # __strftime takes a format String as the arg
  418. primitive_nobridge '__strftime' , 'strftime:'
  419. # Smalltalk initialize takes care of all instvars,
  420. # for use cases Time.new Time.now
  421. primitive_nobridge 'initialize'
  422. # __set_tmarray fills in @is_gmt , @tm based on @microseconds
  423. primitive_nobridge '__set_tmarray', '_setTmArray:'
  424. def __microsecs
  425. @_st_microseconds
  426. end
  427. def __init(aMicrosecs, isGmt)
  428. @_st_microseconds = aMicrosecs
  429. __set_tmarray(isGmt)
  430. self
  431. end
  432. def self.at(a_time)
  433. res = self.allocate
  434. if (a_time._kind_of?(self))
  435. usecs = a_time.__microsecs
  436. elsif a_time._isInteger
  437. usecs = a_time * 1_000_000
  438. else
  439. a_time = Type.coerce_to(a_time, Float, :to_f)
  440. usecs = (a_time * 1000000.0).to_int
  441. end
  442. res.__init( usecs , false)
  443. end
  444. def self.at(secs, microsecs)
  445. res = self.allocate
  446. usecs = (secs.to_i * 1_000_000) + microsecs.to_i
  447. res.__init( usecs , false)
  448. end
  449. def strftime(fmt='%a %b %d %H:%M:%S %Z %Y' )
  450. fmt = fmt.to_str unless fmt._isString
  451. __strftime(fmt)
  452. end
  453. def self.times
  454. Process.times
  455. end
  456. end
  457. Time.__freeze_constants