package org.jruby.ext.date;
import org.jruby.*;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.ext.date.RubyDate.*;
import static org.jruby.util.Numeric.*;
abstract class DateUtils {
static long civil_to_jd(int y, int m, int d, double sg) {
double a, b, jd;
if (m <= 2) {
y -= 1;
m += 12;
}
a = Math.floor(y / 100.0);
b = 2 - a + Math.floor(a / 4.0);
jd = Math.floor(365.25 * (y + 4716)) + Math.floor(30.6001 * (m + 1)) + d + b - 1524;
if (jd < sg) {
jd -= b;
}
return (long) jd;
}
static int[] jd_to_civil(long jd, double sg) {
double x, a, b, c, d, e, y, m, dom;
if (jd < sg)
a = jd;
else {
x = Math.floor((jd - 1867216.25) / 36524.25);
a = jd + 1 + x - Math.floor(x / 4.0);
}
b = a + 1524;
c = Math.floor((b - 122.1) / 365.25);
d = Math.floor(365.25 * c);
e = Math.floor((b - d) / 30.6001);
dom = b - d - Math.floor(30.6001 * e);
if (e <= 13) {
m = e - 1;
y = c - 4716;
}
else {
m = e - 13;
y = c - 4715;
}
return new int[] { (int) y, (int) m, (int) dom };
}
static long ordinal_to_jd(int y, int d, final long sg) {
return find_fdoy(y, sg) + d - 1;
}
static int[] jd_to_ordinal(long jd, final double sg) {
int y = jd_to_civil(jd, sg)[0];
long j = find_fdoy(y, (int) sg);
return new int[] { y, (int) (jd - j + 1) };
}
static long commercial_to_jd(int y, int w, int d, final long sg) {
long j = find_fdoy(y, sg) + 3;
return (j - (((j - 1) + 1) % 7)) + 7 * (w - 1) + (d - 1);
}
static int[] jd_to_commercial(long jd, final long sg) {
int a = jd_to_civil(jd - 3, sg)[0];
final int y;
if (jd >= commercial_to_jd(a + 1, 1, 1, sg)) {
y = a + 1;
}
else {
y = a;
}
int w = 1 + (int) ((jd - commercial_to_jd(y, 1, 1, sg)) / 7);
int d = (int) ((jd + 1) % 7);
if (d == 0) d = 7;
return new int[] { y, w, d };
}
private static long weeknum_to_jd(int y, int w, int d, int f, final long sg) {
long a = find_fdoy(y, sg) + 6;
return (a - ((a - f) + 1) % 7 - 7) + 7 * w + d;
}
private static int[] jd_to_weeknum(long jd, int f, final long sg) {
final int y = jd_to_civil(jd, sg)[0];
long a = find_fdoy(y, sg) + 6;
long val = (jd - (a - ((a - f) + 1) % 7) + 7);
int w = (int) (val / 7), d = (int) (val % 7);
return new int[] { y, w, d };
}
private static long nth_kday_to_jd(int y, int m, int n, int k, final long sg) {
final long j;
if (n > 0) {
j = find_fdom(y, m, sg) - 1;
}
else {
j = find_ldom(y, m, sg) + 7;
}
return (j - (((j - k) + 1) % 7)) + 7 * n;
}
private static int[] jd_to_nth_kday(long jd, final long sg) {
final int[] y_m_d = jd_to_civil(jd, sg);
final int y = y_m_d[0];
final int m = y_m_d[1];
long j = find_fdom(y, m, sg);
int jd_to_wday = (int) ((jd + 1) % 7);
return new int[] { y, m, (int) (((jd - j) / 7) + 1), jd_to_wday };
}
static boolean valid_time_p(long h, long min, long s) {
if (h < 0) h += 24;
if (min < 0) min += 60;
if (s < 0) s += 60;
return !(h < 0 || h > 24 ||
min < 0 || min > 59 ||
s < 0 || s > 59 ||
(h == 24 && (min > 0 || s > 0)));
}
static IRubyObject day_to_sec(ThreadContext context, IRubyObject d) {
return RubyFixnum.newFixnum(context.runtime, DAY_IN_SECONDS).op_mul(context, d);
}
static final int INVALID_OFFSET = Integer.MIN_VALUE;
static int offset_to_sec(ThreadContext context, IRubyObject of) {
long n; IRubyObject vs;
switch (of.getMetaClass().getClassIndex()) {
case INTEGER:
long i = ((RubyInteger) of).getLongValue();
if (i != -1 && i != 0 && i != 1) return INVALID_OFFSET;
return (int) i * DAY_IN_SECONDS;
case FLOAT:
double d = ((RubyFloat) of).getDoubleValue();
d = d * DAY_IN_SECONDS;
if (d < -DAY_IN_SECONDS || d > DAY_IN_SECONDS) return INVALID_OFFSET;
n = Math.round(d);
return (int) n;
case STRING:
RubyClass date = getDate(context.runtime);
vs = sites(context).zone_to_diff.call(context, date, date, of);
if (!(vs instanceof RubyFixnum)) return INVALID_OFFSET;
n = ((RubyFixnum) vs).getLongValue();
if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return INVALID_OFFSET;
return (int) n;
case RATIONAL:
vs = day_to_sec(context, of);
if (!(vs instanceof RubyRational)) {
if (!(vs instanceof RubyFixnum)) return INVALID_OFFSET;
n = ((RubyFixnum) vs).getLongValue();
if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return INVALID_OFFSET;
return (int) n;
}
RubyInteger vn = (RubyInteger) ((RubyRational) vs).getNumerator();
RubyInteger vd = (RubyInteger) ((RubyRational) vs).getDenominator();
if (vn instanceof RubyFixnum && vd instanceof RubyFixnum && vd.getLongValue() == 1)
n = ((RubyFixnum) vn).getLongValue();
else {
vn = (RubyInteger) ((RubyRational) vs).round(context);
if (!(vn instanceof RubyFixnum)) return INVALID_OFFSET;
n = ((RubyFixnum) vn).getLongValue();
if (n < -DAY_IN_SECONDS || n > DAY_IN_SECONDS) return INVALID_OFFSET;
}
return (int) n;
}
return INVALID_OFFSET;
}
static Long find_ldom(int y, int m, final long sg) {
Long j = null;
for (int d = 31; d >= 1; d--) {
j = _valid_civil_p(y, m, d, sg);
if (j != null) break;
}
return j;
}
static Long find_fdom(int y, int m, final long sg) {
Long j = null;
for (int d = 1; d <= 31; d++) {
j = _valid_civil_p(y, m, d, sg);
if (j != null) break;
}
return j;
}
static Long find_fdoy(int y, final long sg) {
Long j = null;
for (int d = 1; d <= 31; d++) {
j = _valid_civil_p(y, 1, d, sg);
if (j != null) break;
}
return j;
}
static Long find_ldoy(int y, final long sg) {
Long j = null;
for (int d = 31; d >= 1; d--) {
j = _valid_civil_p(y, 12, d, sg);
if (j != null) break;
}
return j;
}
static Long _valid_civil_p(int y, int m, int d, final long sg) {
if (d < 0) {
Long j = find_ldom(y, m, sg);
if (j == null) return null;
int[] ny_nm_nd = jd_to_civil(j + d + 1, sg);
if (y != ny_nm_nd[0] || m != ny_nm_nd[1]) return null;
d = ny_nm_nd[2];
}
long jd = civil_to_jd(y, m, d, sg);
int[] y_m_d = jd_to_civil(jd, sg);
if (y != y_m_d[0] || m != y_m_d[1] || d != y_m_d[2]) return null;
return jd;
}
static Long _valid_ordinal_p(int y, int d, final long sg) {
if (d < 0) {
Long j = find_ldoy(y, sg);
if (j == null) return null;
int[] ny_nd = jd_to_ordinal(j + d + 1, sg);
if (y != ny_nd[0]) return null;
d = ny_nd[1];
}
long jd = ordinal_to_jd(y, d, sg);
int[] y_d = jd_to_ordinal(jd, sg);
if (y != y_d[0] || d != y_d[1]) return null;
return jd;
}
static Long _valid_commercial_p(int y, int w, int d, final long sg) {
if (d < 0) d += 8;
if (w < 0) {
int[] ny_nw_nd = jd_to_commercial(commercial_to_jd(y + 1, 1, 1, sg) + w * 7, sg);
if (y != ny_nw_nd[0]) return null;
w = ny_nw_nd[1];
}
long jd = commercial_to_jd(y, w, d, sg);
int[] ny_nw_nd = jd_to_commercial(jd, sg);
if (y != ny_nw_nd[0] || w != ny_nw_nd[1] || d != ny_nw_nd[2]) return null;
return jd;
}
static Long _valid_weeknum_p(int y, int w, int d, int f, final long sg) {
if (d < 0) d += 7;
if (w < 0) {
int[] ny_nw_nd = jd_to_weeknum(weeknum_to_jd(y + 1, 1, f, f, sg) + w * 7, f, sg);
if (y != ny_nw_nd[0]) return null;
w = ny_nw_nd[1];
}
long jd = weeknum_to_jd(y, w, d, f, sg);
int[] ny_nw_nd = jd_to_weeknum(jd, f, sg);
if (y != ny_nw_nd[0] || w != ny_nw_nd[1] || d != ny_nw_nd[2]) return null;
return jd;
}
static Long _valid_nth_kday_p(int y, int m, int n, int k, final long sg) {
if (k < 0) k += 7;
if (n < 0) {
int val = (y * 12 + m);
int ny = val / 12, nm = val % 12;
nm = (nm + 1) / 1;
int [] ny_nm_nn_nk = jd_to_nth_kday(nth_kday_to_jd(ny, nm, 1, k, sg) + n * 7, sg);
if (y != ny_nm_nn_nk[0] || m != ny_nm_nn_nk[1]) return null;
n = ny_nm_nn_nk[2];
}
long jd = nth_kday_to_jd(y, m, n, k, sg);
int[] ny_nm_nn_nk = jd_to_nth_kday(jd, sg);
if (y != ny_nm_nn_nk[0] || m != ny_nm_nn_nk[1] || n != ny_nm_nn_nk[2] || k != ny_nm_nn_nk[3]) return null;
return jd;
}
static int decode_year(ThreadContext context, IRubyObject y, final int style, RubyInteger[] nth) {
final int period = style < 0 ? CM_PERIOD_GCY : CM_PERIOD_JCY;
if (y instanceof RubyFixnum) {
long iy, it, inth;
iy = ((RubyFixnum) y).getLongValue();
if (iy < RubyFixnum.MAX - 4712) {
it = iy + 4712;
inth = (it / ((long) period));
if (inth != 0) {
it = (it % ((long) period));
}
nth[0] = RubyFixnum.newFixnum(context.runtime, inth);
return (int) it - 4712;
}
}
IRubyObject t;
t = f_add(context, y, RubyFixnum.newFixnum(context.runtime, 4712));
nth[0] = (RubyInteger) f_idiv(context, t, RubyFixnum.newFixnum(context.runtime, period));
if (!f_zero_p(context, nth[0])) {
t = f_mod(context, t, RubyFixnum.newFixnum(context.runtime, period));
}
return t.convertToInteger().getIntValue() - 4712;
}
static long guess_style(ThreadContext context, IRubyObject y, double sg) {
long style = 0;
if (sg == Double.POSITIVE_INFINITY) {
style = JULIAN;
} else if (sg == Double.NEGATIVE_INFINITY) {
style = GREGORIAN;
} else if (!(y instanceof RubyFixnum)) {
style = ((RubyNumeric) y).isPositive(context).isTrue() ? GREGORIAN : JULIAN;
} else {
long iy = y.convertToInteger().getLongValue();
if (iy < REFORM_BEGIN_YEAR)
style = JULIAN;
else if (iy > REFORM_END_YEAR)
style = GREGORIAN;
}
return style;
}
private static JavaSites.DateSites sites(ThreadContext context) {
return context.sites.Date;
}
private static final int JC_PERIOD0 = 1461;
private static final int GC_PERIOD0 = 146097;
private static final int CM_PERIOD0 = 71149239;
static final int CM_PERIOD = (0xfffffff / CM_PERIOD0 * CM_PERIOD0);
private static final int CM_PERIOD_JCY = (CM_PERIOD / JC_PERIOD0 * 4);
private static final int CM_PERIOD_GCY = (CM_PERIOD / GC_PERIOD0 * 400);
}