Upgrade to 1.46.0
[platform/upstream/nghttp2.git] / third-party / mruby / mrbgems / mruby-time / src / time.c
1 /*
2 ** time.c - Time class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6
7 #ifndef MRB_WITHOUT_FLOAT
8 #include <math.h>
9 #endif
10
11 #include <mruby.h>
12 #include <mruby/class.h>
13 #include <mruby/data.h>
14 #include <mruby/numeric.h>
15 #include <mruby/time.h>
16 #include <mruby/string.h>
17
18 #ifdef MRB_DISABLE_STDIO
19 #include <string.h>
20 #endif
21
22 #include <stdlib.h>
23
24 #define NDIV(x,y) (-(-((x)+1)/(y))-1)
25 #define TO_S_FMT "%Y-%m-%d %H:%M:%S "
26
27 #if defined(_MSC_VER) && _MSC_VER < 1800
28 double round(double x) {
29   return floor(x + 0.5);
30 }
31 #endif
32
33 #ifndef MRB_WITHOUT_FLOAT
34 # if !defined(__MINGW64__) && defined(_WIN32)
35 #  define llround(x) round(x)
36 # endif
37 #endif
38
39 #if defined(__MINGW64__) || defined(__MINGW32__)
40 # include <sys/time.h>
41 #endif
42
43 /** Time class configuration */
44
45 /* gettimeofday(2) */
46 /* C99 does not have gettimeofday that is required to retrieve microseconds */
47 /* uncomment following macro on platforms without gettimeofday(2) */
48 /* #define NO_GETTIMEOFDAY */
49
50 /* gmtime(3) */
51 /* C99 does not have reentrant gmtime_r() so it might cause troubles under */
52 /* multi-threading environment.  undef following macro on platforms that */
53 /* does not have gmtime_r() and localtime_r(). */
54 /* #define NO_GMTIME_R */
55
56 #ifdef _WIN32
57 #ifdef _MSC_VER
58 /* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */
59 #define gmtime_r(tp, tm)    ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL)
60 #define localtime_r(tp, tm)    ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL)
61 #else
62 #define NO_GMTIME_R
63 #endif
64 #endif
65
66 /* asctime(3) */
67 /* mruby usually use its own implementation of struct tm to string conversion */
68 /* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */
69 /* By default mruby tries to use asctime_r() which is reentrant. */
70 /* Undef following macro on platforms that does not have asctime_r(). */
71 /* #define NO_ASCTIME_R */
72
73 /* timegm(3) */
74 /* mktime() creates tm structure for localtime; timegm() is for UTC time */
75 /* define following macro to use probably faster timegm() on the platform */
76 /* #define USE_SYSTEM_TIMEGM */
77
78 /** end of Time class configuration */
79
80 #ifndef NO_GETTIMEOFDAY
81 # ifdef _WIN32
82 #  define WIN32_LEAN_AND_MEAN  /* don't include winsock.h */
83 #  include <windows.h>
84 #  define gettimeofday my_gettimeofday
85
86 #  ifdef _MSC_VER
87 #    define UI64(x) x##ui64
88 #  else
89 #    define UI64(x) x##ull
90 #  endif
91
92 typedef long suseconds_t;
93
94 # if (!defined __MINGW64__) && (!defined __MINGW32__)
95 struct timeval {
96   time_t tv_sec;
97   suseconds_t tv_usec;
98 };
99 # endif
100
101 static int
102 gettimeofday(struct timeval *tv, void *tz)
103 {
104   if (tz) {
105     mrb_assert(0);  /* timezone is not supported */
106   }
107   if (tv) {
108     union {
109       FILETIME ft;
110       unsigned __int64 u64;
111     } t;
112     GetSystemTimeAsFileTime(&t.ft);   /* 100 ns intervals since Windows epoch */
113     t.u64 -= UI64(116444736000000000);  /* Unix epoch bias */
114     t.u64 /= 10;                      /* to microseconds */
115     tv->tv_sec = (time_t)(t.u64 / (1000 * 1000));
116     tv->tv_usec = t.u64 % (1000 * 1000);
117   }
118   return 0;
119 }
120 # else
121 #  include <sys/time.h>
122 # endif
123 #endif
124 #ifdef NO_GMTIME_R
125 #define gmtime_r(t,r) gmtime(t)
126 #define localtime_r(t,r) localtime(t)
127 #endif
128
129 #ifndef USE_SYSTEM_TIMEGM
130 #define timegm my_timgm
131
132 static unsigned int
133 is_leapyear(unsigned int y)
134 {
135   return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
136 }
137
138 static time_t
139 timegm(struct tm *tm)
140 {
141   static const unsigned int ndays[2][12] = {
142     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
143     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
144   };
145   time_t r = 0;
146   int i;
147   unsigned int *nday = (unsigned int*) ndays[is_leapyear(tm->tm_year+1900)];
148
149   static const int epoch_year = 70;
150   if(tm->tm_year >= epoch_year) {
151     for (i = epoch_year; i < tm->tm_year; ++i)
152       r += is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
153   } else {
154     for (i = tm->tm_year; i < epoch_year; ++i)
155       r -= is_leapyear(i+1900) ? 366*24*60*60 : 365*24*60*60;
156   }
157   for (i = 0; i < tm->tm_mon; ++i)
158     r += nday[i] * 24 * 60 * 60;
159   r += (tm->tm_mday - 1) * 24 * 60 * 60;
160   r += tm->tm_hour * 60 * 60;
161   r += tm->tm_min * 60;
162   r += tm->tm_sec;
163   return r;
164 }
165 #endif
166
167 /* Since we are limited to using ISO C99, this implementation is based
168 * on time_t. That means the resolution of time is only precise to the
169 * second level. Also, there are only 2 timezones, namely UTC and LOCAL.
170 */
171
172 typedef struct mrb_timezone_name {
173   const char name[8];
174   size_t len;
175 } mrb_timezone_name;
176
177 static const mrb_timezone_name timezone_names[] = {
178   { "none", sizeof("none") - 1 },
179   { "UTC", sizeof("UTC") - 1 },
180   { "LOCAL", sizeof("LOCAL") - 1 },
181 };
182
183 #ifndef MRB_DISABLE_STDIO
184 static const char mon_names[12][4] = {
185   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
186 };
187
188 static const char wday_names[7][4] = {
189   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
190 };
191 #endif
192
193 struct mrb_time {
194   time_t              sec;
195   time_t              usec;
196   enum mrb_timezone   timezone;
197   struct tm           datetime;
198 };
199
200 static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
201
202 #ifndef MRB_WITHOUT_FLOAT
203 void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
204 typedef mrb_float mrb_sec;
205 #define mrb_sec_value(mrb, sec) mrb_float_value(mrb, sec)
206 #else
207 typedef mrb_int mrb_sec;
208 #define mrb_sec_value(mrb, sec) mrb_fixnum_value(sec)
209 #endif
210
211 #define MRB_TIME_T_UINT (~(time_t)0 > 0)
212 #define MRB_TIME_MIN (                                                      \
213   MRB_TIME_T_UINT ? 0 :                                                     \
214                     (sizeof(time_t) <= 4 ? INT32_MIN : INT64_MIN)           \
215 )
216 #define MRB_TIME_MAX (                                                      \
217   MRB_TIME_T_UINT ? (sizeof(time_t) <= 4 ? UINT32_MAX : UINT64_MAX) :       \
218                     (sizeof(time_t) <= 4 ? INT32_MAX : INT64_MAX)           \
219 )
220
221 static mrb_bool
222 fixable_time_t_p(time_t v)
223 {
224   if (MRB_INT_MIN <= MRB_TIME_MIN && MRB_TIME_MAX <= MRB_INT_MAX) return TRUE;
225   return FIXABLE(v);
226 }
227
228 static time_t
229 mrb_to_time_t(mrb_state *mrb, mrb_value obj, time_t *usec)
230 {
231   time_t t;
232
233   switch (mrb_type(obj)) {
234 #ifndef MRB_WITHOUT_FLOAT
235     case MRB_TT_FLOAT:
236       {
237         mrb_float f = mrb_float(obj);
238
239         mrb_check_num_exact(mrb, f);
240         if (f >= ((mrb_float)MRB_TIME_MAX-1.0) || f < ((mrb_float)MRB_TIME_MIN+1.0)) {
241           goto out_of_range;
242         }
243
244         if (usec) {
245           t = (time_t)f;
246           *usec = (time_t)llround((f - t) * 1.0e+6);
247         }
248         else {
249           t = (time_t)llround(f);
250         }
251       }
252       break;
253 #endif /* MRB_WITHOUT_FLOAT */
254     default:
255     case MRB_TT_FIXNUM:
256       {
257         mrb_int i = mrb_int(mrb, obj);
258
259         if ((MRB_INT_MAX > MRB_TIME_MAX && i > 0 && i > (mrb_int)MRB_TIME_MAX) ||
260             (MRB_TIME_MIN > MRB_INT_MIN && MRB_TIME_MIN > i)) {
261           goto out_of_range;
262         }
263
264         t = (time_t)i;
265         if (usec) { *usec = 0; }
266       }
267       break;
268   }
269
270   return t;
271
272 out_of_range:
273   mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", obj);
274
275   /* not reached */
276   if (usec) { *usec = 0; }
277   return 0;
278 }
279
280 /** Updates the datetime of a mrb_time based on it's timezone and
281     seconds setting. Returns self on success, NULL of failure.
282     if `dealloc` is set `true`, it frees `self` on error. */
283 static struct mrb_time*
284 time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc)
285 {
286   struct tm *aid;
287   time_t t = self->sec;
288
289   if (self->timezone == MRB_TIMEZONE_UTC) {
290     aid = gmtime_r(&t, &self->datetime);
291   }
292   else {
293     aid = localtime_r(&t, &self->datetime);
294   }
295   if (!aid) {
296     mrb_sec sec = (mrb_sec)t;
297
298     if (dealloc) mrb_free(mrb, self);
299     mrb_raisef(mrb, E_ARGUMENT_ERROR, "%v out of Time range", mrb_sec_value(mrb, sec));
300     /* not reached */
301     return NULL;
302   }
303 #ifdef NO_GMTIME_R
304   self->datetime = *aid; /* copy data */
305 #endif
306
307   return self;
308 }
309
310 static mrb_value
311 mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm)
312 {
313   return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm));
314 }
315
316 /* Allocates a mrb_time object and initializes it. */
317 static struct mrb_time*
318 time_alloc_time(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone timezone)
319 {
320   struct mrb_time *tm;
321
322   tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
323   tm->sec  = sec;
324   tm->usec = usec;
325   if (tm->usec < 0) {
326     long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
327     tm->usec -= sec2 * 1000000;
328     tm->sec += sec2;
329   }
330   else if (tm->usec >= 1000000) {
331     long sec2 = (long)(tm->usec / 1000000);
332     tm->usec -= sec2 * 1000000;
333     tm->sec += sec2;
334   }
335   tm->timezone = timezone;
336   time_update_datetime(mrb, tm, TRUE);
337
338   return tm;
339 }
340
341 static struct mrb_time*
342 time_alloc(mrb_state *mrb, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
343 {
344   time_t tsec, tusec;
345
346   tsec = mrb_to_time_t(mrb, sec, &tusec);
347   tusec += mrb_to_time_t(mrb, usec, NULL);
348
349   return time_alloc_time(mrb, tsec, tusec, timezone);
350 }
351
352 static mrb_value
353 mrb_time_make_time(mrb_state *mrb, struct RClass *c, time_t sec, time_t usec, enum mrb_timezone timezone)
354 {
355   return mrb_time_wrap(mrb, c, time_alloc_time(mrb, sec, usec, timezone));
356 }
357
358 static mrb_value
359 mrb_time_make(mrb_state *mrb, struct RClass *c, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
360 {
361   return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone));
362 }
363
364 static struct mrb_time*
365 current_mrb_time(mrb_state *mrb)
366 {
367   struct mrb_time tmzero = {0};
368   struct mrb_time *tm;
369   time_t sec, usec;
370
371 #if defined(TIME_UTC) && !defined(__ANDROID__)
372   {
373     struct timespec ts;
374     if (timespec_get(&ts, TIME_UTC) == 0) {
375       mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons");
376     }
377     sec = ts.tv_sec;
378     usec = ts.tv_nsec / 1000;
379   }
380 #elif defined(NO_GETTIMEOFDAY)
381   {
382     static time_t last_sec = 0, last_usec = 0;
383
384     sec = time(NULL);
385     if (sec != last_sec) {
386       last_sec = sec;
387       last_usec = 0;
388     }
389     else {
390       /* add 1 usec to differentiate two times */
391       last_usec += 1;
392     }
393     usec = last_usec;
394   }
395 #else
396   {
397     struct timeval tv;
398
399     gettimeofday(&tv, NULL);
400     sec = tv.tv_sec;
401     usec = tv.tv_usec;
402   }
403 #endif
404   tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
405   *tm = tmzero;
406   tm->sec = sec; tm->usec = usec;
407   tm->timezone = MRB_TIMEZONE_LOCAL;
408   time_update_datetime(mrb, tm, TRUE);
409
410   return tm;
411 }
412
413 /* Allocates a new Time object with given millis value. */
414 static mrb_value
415 mrb_time_now(mrb_state *mrb, mrb_value self)
416 {
417   return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
418 }
419
420 MRB_API mrb_value
421 mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone zone)
422 {
423   return mrb_time_make_time(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
424 }
425
426 /* 15.2.19.6.1 */
427 /* Creates an instance of time at the given time in seconds, etc. */
428 static mrb_value
429 mrb_time_at_m(mrb_state *mrb, mrb_value self)
430 {
431   mrb_value sec;
432   mrb_value usec = mrb_fixnum_value(0);
433
434   mrb_get_args(mrb, "o|o", &sec, &usec);
435
436   return mrb_time_make(mrb, mrb_class_ptr(self), sec, usec, MRB_TIMEZONE_LOCAL);
437 }
438
439 static struct mrb_time*
440 time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
441   mrb_int ahour, mrb_int amin, mrb_int asec, mrb_int ausec,
442   enum mrb_timezone timezone)
443 {
444   time_t nowsecs;
445   struct tm nowtime = { 0 };
446
447   nowtime.tm_year  = (int)ayear  - 1900;
448   nowtime.tm_mon   = (int)amonth - 1;
449   nowtime.tm_mday  = (int)aday;
450   nowtime.tm_hour  = (int)ahour;
451   nowtime.tm_min   = (int)amin;
452   nowtime.tm_sec   = (int)asec;
453   nowtime.tm_isdst = -1;
454
455   if (nowtime.tm_mon  < 0 || nowtime.tm_mon  > 11
456       || nowtime.tm_mday < 1 || nowtime.tm_mday > 31
457       || nowtime.tm_hour < 0 || nowtime.tm_hour > 24
458       || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0))
459       || nowtime.tm_min  < 0 || nowtime.tm_min  > 59
460       || nowtime.tm_sec  < 0 || nowtime.tm_sec  > 60)
461     mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range");
462
463   if (timezone == MRB_TIMEZONE_UTC) {
464     nowsecs = timegm(&nowtime);
465   }
466   else {
467     nowsecs = mktime(&nowtime);
468   }
469   if (nowsecs == (time_t)-1) {
470     mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
471   }
472
473   return time_alloc_time(mrb, nowsecs, ausec, timezone);
474 }
475
476 /* 15.2.19.6.2 */
477 /* Creates an instance of time at the given time in UTC. */
478 static mrb_value
479 mrb_time_gm(mrb_state *mrb, mrb_value self)
480 {
481   mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0;
482
483   mrb_get_args(mrb, "i|iiiiii",
484                 &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
485   return mrb_time_wrap(mrb, mrb_class_ptr(self),
486           time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_UTC));
487 }
488
489
490 /* 15.2.19.6.3 */
491 /* Creates an instance of time at the given time in local time zone. */
492 static mrb_value
493 mrb_time_local(mrb_state *mrb, mrb_value self)
494 {
495   mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0, amin = 0, asec = 0, ausec = 0;
496
497   mrb_get_args(mrb, "i|iiiiii",
498                 &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
499   return mrb_time_wrap(mrb, mrb_class_ptr(self),
500           time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL));
501 }
502
503 static struct mrb_time*
504 time_get_ptr(mrb_state *mrb, mrb_value time)
505 {
506   struct mrb_time *tm;
507
508   tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time);
509   if (!tm) {
510     mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
511   }
512   return tm;
513 }
514
515 static mrb_value
516 mrb_time_eq(mrb_state *mrb, mrb_value self)
517 {
518   mrb_value other = mrb_get_arg1(mrb);
519   struct mrb_time *tm1, *tm2;
520   mrb_bool eq_p;
521
522   tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
523   tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
524   eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec;
525
526   return mrb_bool_value(eq_p);
527 }
528
529 static mrb_value
530 mrb_time_cmp(mrb_state *mrb, mrb_value self)
531 {
532   mrb_value other = mrb_get_arg1(mrb);
533   struct mrb_time *tm1, *tm2;
534
535   tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
536   tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
537   if (!tm1 || !tm2) return mrb_nil_value();
538   if (tm1->sec > tm2->sec) {
539     return mrb_fixnum_value(1);
540   }
541   else if (tm1->sec < tm2->sec) {
542     return mrb_fixnum_value(-1);
543   }
544   /* tm1->sec == tm2->sec */
545   if (tm1->usec > tm2->usec) {
546     return mrb_fixnum_value(1);
547   }
548   else if (tm1->usec < tm2->usec) {
549     return mrb_fixnum_value(-1);
550   }
551   return mrb_fixnum_value(0);
552 }
553
554 static mrb_value
555 mrb_time_plus(mrb_state *mrb, mrb_value self)
556 {
557   mrb_value o = mrb_get_arg1(mrb);
558   struct mrb_time *tm;
559   time_t sec, usec;
560
561   tm = time_get_ptr(mrb, self);
562   sec = mrb_to_time_t(mrb, o, &usec);
563   return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec+sec, tm->usec+usec, tm->timezone);
564 }
565
566 static mrb_value
567 mrb_time_minus(mrb_state *mrb, mrb_value self)
568 {
569   mrb_value other = mrb_get_arg1(mrb);
570   struct mrb_time *tm, *tm2;
571
572   tm = time_get_ptr(mrb, self);
573   tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
574   if (tm2) {
575 #ifndef MRB_WITHOUT_FLOAT
576     mrb_float f;
577     f = (mrb_sec)(tm->sec - tm2->sec)
578       + (mrb_sec)(tm->usec - tm2->usec) / 1.0e6;
579     return mrb_float_value(mrb, f);
580 #else
581     mrb_int f;
582     f = tm->sec - tm2->sec;
583     if (tm->usec < tm2->usec) f--;
584     return mrb_fixnum_value(f);
585 #endif
586   }
587   else {
588     time_t sec, usec;
589     sec = mrb_to_time_t(mrb, other, &usec);
590     return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec-sec, tm->usec-usec, tm->timezone);
591   }
592 }
593
594 /* 15.2.19.7.30 */
595 /* Returns week day number of time. */
596 static mrb_value
597 mrb_time_wday(mrb_state *mrb, mrb_value self)
598 {
599   struct mrb_time *tm;
600
601   tm = time_get_ptr(mrb, self);
602   return mrb_fixnum_value(tm->datetime.tm_wday);
603 }
604
605 /* 15.2.19.7.31 */
606 /* Returns year day number of time. */
607 static mrb_value
608 mrb_time_yday(mrb_state *mrb, mrb_value self)
609 {
610   struct mrb_time *tm;
611
612   tm = time_get_ptr(mrb, self);
613   return mrb_fixnum_value(tm->datetime.tm_yday + 1);
614 }
615
616 /* 15.2.19.7.32 */
617 /* Returns year of time. */
618 static mrb_value
619 mrb_time_year(mrb_state *mrb, mrb_value self)
620 {
621   struct mrb_time *tm;
622
623   tm = time_get_ptr(mrb, self);
624   return mrb_fixnum_value(tm->datetime.tm_year + 1900);
625 }
626
627 /* 15.2.19.7.33 */
628 /* Returns name of time's timezone. */
629 static mrb_value
630 mrb_time_zone(mrb_state *mrb, mrb_value self)
631 {
632   struct mrb_time *tm;
633
634   tm = time_get_ptr(mrb, self);
635   if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value();
636   if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value();
637   return mrb_str_new_static(mrb,
638                             timezone_names[tm->timezone].name,
639                             timezone_names[tm->timezone].len);
640 }
641
642 /* 15.2.19.7.4 */
643 /* Returns a string that describes the time. */
644 static mrb_value
645 mrb_time_asctime(mrb_state *mrb, mrb_value self)
646 {
647   struct mrb_time *tm = time_get_ptr(mrb, self);
648   struct tm *d = &tm->datetime;
649   int len;
650
651 #if defined(MRB_DISABLE_STDIO)
652   char *s;
653 # ifdef NO_ASCTIME_R
654   s = asctime(d);
655 # else
656   char buf[32];
657   s = asctime_r(d, buf);
658 # endif
659   len = strlen(s)-1;            /* truncate the last newline */
660 #else
661   char buf[256];
662
663   len = snprintf(buf, sizeof(buf), "%s %s %2d %02d:%02d:%02d %.4d",
664     wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
665     d->tm_hour, d->tm_min, d->tm_sec,
666     d->tm_year + 1900);
667 #endif
668   return mrb_str_new(mrb, buf, len);
669 }
670
671 /* 15.2.19.7.6 */
672 /* Returns the day in the month of the time. */
673 static mrb_value
674 mrb_time_day(mrb_state *mrb, mrb_value self)
675 {
676   struct mrb_time *tm;
677
678   tm = time_get_ptr(mrb, self);
679   return mrb_fixnum_value(tm->datetime.tm_mday);
680 }
681
682
683 /* 15.2.19.7.7 */
684 /* Returns true if daylight saving was applied for this time. */
685 static mrb_value
686 mrb_time_dst_p(mrb_state *mrb, mrb_value self)
687 {
688   struct mrb_time *tm;
689
690   tm = time_get_ptr(mrb, self);
691   return mrb_bool_value(tm->datetime.tm_isdst);
692 }
693
694 /* 15.2.19.7.8 */
695 /* 15.2.19.7.10 */
696 /* Returns the Time object of the UTC(GMT) timezone. */
697 static mrb_value
698 mrb_time_getutc(mrb_state *mrb, mrb_value self)
699 {
700   struct mrb_time *tm, *tm2;
701
702   tm = time_get_ptr(mrb, self);
703   tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
704   *tm2 = *tm;
705   tm2->timezone = MRB_TIMEZONE_UTC;
706   time_update_datetime(mrb, tm2, TRUE);
707   return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
708 }
709
710 /* 15.2.19.7.9 */
711 /* Returns the Time object of the LOCAL timezone. */
712 static mrb_value
713 mrb_time_getlocal(mrb_state *mrb, mrb_value self)
714 {
715   struct mrb_time *tm, *tm2;
716
717   tm = time_get_ptr(mrb, self);
718   tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
719   *tm2 = *tm;
720   tm2->timezone = MRB_TIMEZONE_LOCAL;
721   time_update_datetime(mrb, tm2, TRUE);
722   return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
723 }
724
725 /* 15.2.19.7.15 */
726 /* Returns hour of time. */
727 static mrb_value
728 mrb_time_hour(mrb_state *mrb, mrb_value self)
729 {
730   struct mrb_time *tm;
731
732   tm = time_get_ptr(mrb, self);
733   return mrb_fixnum_value(tm->datetime.tm_hour);
734 }
735
736 /* 15.2.19.7.16 */
737 /* Initializes a time by setting the amount of milliseconds since the epoch.*/
738 static mrb_value
739 mrb_time_initialize(mrb_state *mrb, mrb_value self)
740 {
741   mrb_int ayear = 0, amonth = 1, aday = 1, ahour = 0,
742   amin = 0, asec = 0, ausec = 0;
743   mrb_int n;
744   struct mrb_time *tm;
745
746   n = mrb_get_args(mrb, "|iiiiiii",
747        &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
748   tm = (struct mrb_time*)DATA_PTR(self);
749   if (tm) {
750     mrb_free(mrb, tm);
751   }
752   mrb_data_init(self, NULL, &mrb_time_type);
753
754   if (n == 0) {
755     tm = current_mrb_time(mrb);
756   }
757   else {
758     tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL);
759   }
760   mrb_data_init(self, tm, &mrb_time_type);
761   return self;
762 }
763
764 /* 15.2.19.7.17(x) */
765 /* Initializes a copy of this time object. */
766 static mrb_value
767 mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
768 {
769   mrb_value src = mrb_get_arg1(mrb);
770   struct mrb_time *t1, *t2;
771
772   if (mrb_obj_equal(mrb, copy, src)) return copy;
773   if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
774     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
775   }
776   t1 = (struct mrb_time *)DATA_PTR(copy);
777   t2 = (struct mrb_time *)DATA_PTR(src);
778   if (!t2) {
779     mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time");
780   }
781   if (!t1) {
782     t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
783     mrb_data_init(copy, t1, &mrb_time_type);
784   }
785   *t1 = *t2;
786   return copy;
787 }
788
789 /* 15.2.19.7.18 */
790 /* Sets the timezone attribute of the Time object to LOCAL. */
791 static mrb_value
792 mrb_time_localtime(mrb_state *mrb, mrb_value self)
793 {
794   struct mrb_time *tm;
795
796   tm = time_get_ptr(mrb, self);
797   tm->timezone = MRB_TIMEZONE_LOCAL;
798   time_update_datetime(mrb, tm, FALSE);
799   return self;
800 }
801
802 /* 15.2.19.7.19 */
803 /* Returns day of month of time. */
804 static mrb_value
805 mrb_time_mday(mrb_state *mrb, mrb_value self)
806 {
807   struct mrb_time *tm;
808
809   tm = time_get_ptr(mrb, self);
810   return mrb_fixnum_value(tm->datetime.tm_mday);
811 }
812
813 /* 15.2.19.7.20 */
814 /* Returns minutes of time. */
815 static mrb_value
816 mrb_time_min(mrb_state *mrb, mrb_value self)
817 {
818   struct mrb_time *tm;
819
820   tm = time_get_ptr(mrb, self);
821   return mrb_fixnum_value(tm->datetime.tm_min);
822 }
823
824 /* 15.2.19.7.21 and 15.2.19.7.22 */
825 /* Returns month of time. */
826 static mrb_value
827 mrb_time_mon(mrb_state *mrb, mrb_value self)
828 {
829   struct mrb_time *tm;
830
831   tm = time_get_ptr(mrb, self);
832   return mrb_fixnum_value(tm->datetime.tm_mon + 1);
833 }
834
835 /* 15.2.19.7.23 */
836 /* Returns seconds in minute of time. */
837 static mrb_value
838 mrb_time_sec(mrb_state *mrb, mrb_value self)
839 {
840   struct mrb_time *tm;
841
842   tm = time_get_ptr(mrb, self);
843   return mrb_fixnum_value(tm->datetime.tm_sec);
844 }
845
846 #ifndef MRB_WITHOUT_FLOAT
847 /* 15.2.19.7.24 */
848 /* Returns a Float with the time since the epoch in seconds. */
849 static mrb_value
850 mrb_time_to_f(mrb_state *mrb, mrb_value self)
851 {
852   struct mrb_time *tm;
853
854   tm = time_get_ptr(mrb, self);
855   return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
856 }
857 #endif
858
859 /* 15.2.19.7.25 */
860 /* Returns an Integer with the time since the epoch in seconds. */
861 static mrb_value
862 mrb_time_to_i(mrb_state *mrb, mrb_value self)
863 {
864   struct mrb_time *tm;
865
866   tm = time_get_ptr(mrb, self);
867 #ifndef MRB_WITHOUT_FLOAT
868   if (!fixable_time_t_p(tm->sec)) {
869     return mrb_float_value(mrb, (mrb_float)tm->sec);
870   }
871 #endif
872   return mrb_fixnum_value((mrb_int)tm->sec);
873 }
874
875 /* 15.2.19.7.26 */
876 /* Returns an Integer with the time since the epoch in microseconds. */
877 static mrb_value
878 mrb_time_usec(mrb_state *mrb, mrb_value self)
879 {
880   struct mrb_time *tm;
881
882   tm = time_get_ptr(mrb, self);
883 #ifndef MRB_WITHOUT_FLOAT
884   if (!fixable_time_t_p(tm->usec)) {
885     return mrb_float_value(mrb, (mrb_float)tm->usec);
886   }
887 #endif
888   return mrb_fixnum_value((mrb_int)tm->usec);
889 }
890
891 /* 15.2.19.7.27 */
892 /* Sets the timezone attribute of the Time object to UTC. */
893 static mrb_value
894 mrb_time_utc(mrb_state *mrb, mrb_value self)
895 {
896   struct mrb_time *tm;
897
898   tm = time_get_ptr(mrb, self);
899   tm->timezone = MRB_TIMEZONE_UTC;
900   time_update_datetime(mrb, tm, FALSE);
901   return self;
902 }
903
904 /* 15.2.19.7.28 */
905 /* Returns true if this time is in the UTC timezone false if not. */
906 static mrb_value
907 mrb_time_utc_p(mrb_state *mrb, mrb_value self)
908 {
909   struct mrb_time *tm;
910
911   tm = time_get_ptr(mrb, self);
912   return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
913 }
914
915 static size_t
916 time_to_s_utc(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len)
917 {
918   return strftime(buf, buf_len, TO_S_FMT "UTC", &tm->datetime);
919 }
920
921 static size_t
922 time_to_s_local(mrb_state *mrb, struct mrb_time *tm, char *buf, size_t buf_len)
923 {
924 #if defined(_MSC_VER) && _MSC_VER < 1900 || defined(__MINGW64__) || defined(__MINGW32__)
925   struct tm datetime = {0};
926   time_t utc_sec = timegm(&tm->datetime);
927   size_t len;
928   int offset;
929
930   if (utc_sec == (time_t)-1) {
931     mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
932   }
933   offset = abs((int)(utc_sec - tm->sec) / 60);
934   datetime.tm_year = 100;
935   datetime.tm_hour = offset / 60;
936   datetime.tm_min = offset % 60;
937   len = strftime(buf, buf_len, TO_S_FMT, &tm->datetime);
938   buf[len++] = utc_sec < tm->sec ? '-' : '+';
939
940   return len + strftime(buf + len, buf_len - len, "%H%M", &datetime);
941 #else
942   return strftime(buf, buf_len, TO_S_FMT "%z", &tm->datetime);
943 #endif
944 }
945
946 static mrb_value
947 mrb_time_to_s(mrb_state *mrb, mrb_value self)
948 {
949   char buf[64];
950   struct mrb_time *tm = time_get_ptr(mrb, self);
951   mrb_bool utc = tm->timezone == MRB_TIMEZONE_UTC;
952   size_t len = (utc ? time_to_s_utc : time_to_s_local)(mrb, tm, buf, sizeof(buf));
953   mrb_value str = mrb_str_new(mrb, buf, len);
954   RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
955   return str;
956 }
957
958 void
959 mrb_mruby_time_gem_init(mrb_state* mrb)
960 {
961   struct RClass *tc;
962   /* ISO 15.2.19.2 */
963   tc = mrb_define_class(mrb, "Time", mrb->object_class);
964   MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA);
965   mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable"));
966   mrb_define_class_method(mrb, tc, "at", mrb_time_at_m, MRB_ARGS_ARG(1, 1));      /* 15.2.19.6.1 */
967   mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6));       /* 15.2.19.6.2 */
968   mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */
969   mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */
970   mrb_define_class_method(mrb, tc, "now", mrb_time_now, MRB_ARGS_NONE());       /* 15.2.19.6.5 */
971   mrb_define_class_method(mrb, tc, "utc", mrb_time_gm, MRB_ARGS_ARG(1,6));      /* 15.2.19.6.6 */
972
973   mrb_define_method(mrb, tc, "=="     , mrb_time_eq     , MRB_ARGS_REQ(1));
974   mrb_define_method(mrb, tc, "<=>"    , mrb_time_cmp    , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */
975   mrb_define_method(mrb, tc, "+"      , mrb_time_plus   , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */
976   mrb_define_method(mrb, tc, "-"      , mrb_time_minus  , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */
977   mrb_define_method(mrb, tc, "to_s"   , mrb_time_to_s   , MRB_ARGS_NONE());
978   mrb_define_method(mrb, tc, "inspect", mrb_time_to_s   , MRB_ARGS_NONE());
979   mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */
980   mrb_define_method(mrb, tc, "ctime"  , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */
981   mrb_define_method(mrb, tc, "day"    , mrb_time_day    , MRB_ARGS_NONE()); /* 15.2.19.7.6 */
982   mrb_define_method(mrb, tc, "dst?"   , mrb_time_dst_p  , MRB_ARGS_NONE()); /* 15.2.19.7.7 */
983   mrb_define_method(mrb, tc, "getgm"  , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.8 */
984   mrb_define_method(mrb, tc, "getlocal",mrb_time_getlocal,MRB_ARGS_NONE()); /* 15.2.19.7.9 */
985   mrb_define_method(mrb, tc, "getutc" , mrb_time_getutc , MRB_ARGS_NONE()); /* 15.2.19.7.10 */
986   mrb_define_method(mrb, tc, "gmt?"   , mrb_time_utc_p  , MRB_ARGS_NONE()); /* 15.2.19.7.11 */
987   mrb_define_method(mrb, tc, "gmtime" , mrb_time_utc    , MRB_ARGS_NONE()); /* 15.2.19.7.13 */
988   mrb_define_method(mrb, tc, "hour"   , mrb_time_hour, MRB_ARGS_NONE());    /* 15.2.19.7.15 */
989   mrb_define_method(mrb, tc, "localtime", mrb_time_localtime, MRB_ARGS_NONE()); /* 15.2.19.7.18 */
990   mrb_define_method(mrb, tc, "mday"   , mrb_time_mday, MRB_ARGS_NONE());    /* 15.2.19.7.19 */
991   mrb_define_method(mrb, tc, "min"    , mrb_time_min, MRB_ARGS_NONE());     /* 15.2.19.7.20 */
992
993   mrb_define_method(mrb, tc, "mon"  , mrb_time_mon, MRB_ARGS_NONE());       /* 15.2.19.7.21 */
994   mrb_define_method(mrb, tc, "month", mrb_time_mon, MRB_ARGS_NONE());       /* 15.2.19.7.22 */
995
996   mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE());        /* 15.2.19.7.23 */
997   mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE());       /* 15.2.19.7.25 */
998 #ifndef MRB_WITHOUT_FLOAT
999   mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE());       /* 15.2.19.7.24 */
1000 #endif
1001   mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE());       /* 15.2.19.7.26 */
1002   mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE());        /* 15.2.19.7.27 */
1003   mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE());       /* 15.2.19.7.28 */
1004   mrb_define_method(mrb, tc, "wday", mrb_time_wday, MRB_ARGS_NONE());       /* 15.2.19.7.30 */
1005   mrb_define_method(mrb, tc, "yday", mrb_time_yday, MRB_ARGS_NONE());       /* 15.2.19.7.31 */
1006   mrb_define_method(mrb, tc, "year", mrb_time_year, MRB_ARGS_NONE());       /* 15.2.19.7.32 */
1007   mrb_define_method(mrb, tc, "zone", mrb_time_zone, MRB_ARGS_NONE());       /* 15.2.19.7.33 */
1008
1009   mrb_define_method(mrb, tc, "initialize", mrb_time_initialize, MRB_ARGS_REQ(1)); /* 15.2.19.7.16 */
1010   mrb_define_method(mrb, tc, "initialize_copy", mrb_time_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.19.7.17 */
1011
1012   /*
1013     methods not available:
1014       gmt_offset(15.2.19.7.12)
1015       gmtoff(15.2.19.7.14)
1016       utc_offset(15.2.19.7.29)
1017   */
1018 }
1019
1020 void
1021 mrb_mruby_time_gem_final(mrb_state* mrb)
1022 {
1023 }