You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnosygnu_xowa/100_core/src/gplx/Time_span_.java

162 lines
6.9 KiB

/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx;
import gplx.core.strings.*; import gplx.core.envs.*;
public class Time_span_ {
public static final Time_span Zero = new Time_span(0);
public static final Time_span Null = new Time_span(-1);
public static Time_span fracs_(long val) {return new Time_span(val);}
public static Time_span seconds_(double seconds) {
long fracs = (long)(seconds * Divisors[Idx_Sec]);
return new Time_span(fracs);
}
public static Time_span decimal_(Decimal_adp seconds) {
return new Time_span(seconds.To_long_mult_1000());
}
public static Time_span units_(int frc, int sec, int min, int hour) {
int[] units = new int[] {frc, sec, min, hour};
long fracs = Merge_long(units, Time_span_.Divisors);
return Time_span_.fracs_(fracs);
}
public static Time_span from_(long bgn) {return Time_span_.fracs_(System_.Ticks() - bgn);}
public static final long parse_null = Long_.Min_value;
public static Time_span parse(String raw) {
byte[] bry = Bry_.new_u8(raw);
long fracs = parse_to_fracs(bry, 0, bry.length, false);
return fracs == parse_null ? null : Time_span_.fracs_(fracs);
}
public static long parse_to_fracs(byte[] raw, int bgn, int end, boolean fail_if_ws) {
int sign = 1, val_f = 0, val_s = 0, val_m = 0, val_h = 0, colon_pos = 0, unit_val = 0, unit_multiple = 1;
for (int i = end - 1; i >= bgn; i--) { // start from end; fracs should be lowest unit
byte b = raw[i];
switch (b) {
case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4:
case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9:
int unit_digit = Byte_ascii.To_a7_int(b);
unit_val = (unit_multiple == 1) ? unit_digit : unit_val + (unit_digit * unit_multiple);
switch (colon_pos) {
case 0: val_s = unit_val; break;
case 1: val_m = unit_val; break;
case 2: val_h = unit_val; break;
default: return parse_null; // only hour:minute:second supported for ':' separator; ':' count should be <= 2
}
unit_multiple *= 10;
break;
case Byte_ascii.Dot:
double factor = (double)1000 / (double)unit_multiple; // factor is necessary to handle non-standard decimals; ex: .1 -> 100; .00199 -> .001
val_f = (int)((double)val_s * factor); // move val_s unit_val to val_f; logic is indirect, b/c of differing inputs: "123" means 123 seconds; ".123" means 123 fractionals
val_s = 0;
unit_multiple = 1;
break;
case Byte_ascii.Colon:
colon_pos++;
unit_multiple = 1;
break;
case Byte_ascii.Dash:
if (i == 0 && unit_val > 0) // only if first char && unit_val > 0
sign = -1;
break;
case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr:
if (fail_if_ws) return parse_null;
break;
default:
return parse_null; // invalid char; return null;
}
}
return sign * (val_f + (val_s * Divisors[1]) + (val_m * Divisors[2]) + (val_h * Divisors[3]));
}
public static String To_str(long frc, String fmt) {
String_bldr sb = String_bldr_.new_();
int[] units = Split_long(frc, Divisors);
if (String_.Eq(fmt, Time_span_.Fmt_Short)) {
for (int i = Idx_Hour; i > -1; i--) {
int val = units[i];
if (val == 0 && i == Idx_Hour) continue; // skip hour if 0; ex: 01:02, instead of 00:01:02
if (i == Idx_Frac) continue; // skip frac b/c fmt is short
if (sb.Count() > 0) // sb already has unit; add delimiter
sb.Add(Sprs[i]);
if (val < 10) // zeroPad
sb.Add("0");
sb.Add(Int_.To_str(val));
}
return sb.To_str_and_clear();
}
boolean fmt_fracs = !String_.Eq(fmt, Time_span_.Fmt_NoFractionals);
boolean fmt_padZeros = String_.Eq(fmt, Time_span_.Fmt_PadZeros);
if (frc == 0) return fmt_padZeros ? "00:00:00.000" : "0";
int[] padZerosAry = ZeroPadding;
boolean first = true;
String dlm = "";
int zeros = 0;
if (frc < 0) sb.Add("-"); // negative sign
for (int i = Idx_Hour; i > -1; i--) { // NOTE: "> Idx_Frac" b/c frc will be handled below
int val = units[i];
if (i == Idx_Frac // only write fracs...
&& !(val == 0 && fmt_padZeros) // ... if val == 0 && fmt is PadZeros
&& !(val != 0 && fmt_fracs) // ... or val != 0 && fmt is PadZeros or Default
) continue;
if (first && val == 0 && !fmt_padZeros) continue; // if first and val == 0, don't full pad (avoid "00:")
zeros = first && !fmt_padZeros ? 1 : padZerosAry[i]; // if first, don't zero pad (avoid "01")
dlm = first ? "" : Sprs[i]; // if first, don't use dlm (avoid ":01")
sb.Add(dlm);
sb.Add(Int_.To_str_pad_bgn_zero(val, zeros));
first = false;
}
return sb.To_str();
}
@gplx.Internal protected static int[] Split_long(long fracs, int[] divisors) {
int divLength = Array_.Len(divisors);
int[] rv = new int[divLength];
long cur = Math_.Abs(fracs);
for (int i = divLength - 1; i > -1; i--) {
int divisor = divisors[i];
long factor = cur / divisor;
rv[i] = (int)factor;
cur -= (factor * divisor);
}
return rv;
}
@gplx.Internal protected static long Merge_long(int[] vals, int[] divisors) {
long rv = 0; int valLength = Array_.Len(vals);
for (int i = 0; i < valLength; i++) {
rv += vals[i] * divisors[i];
}
return rv;
}
public static final String Fmt_PadZeros = "00:00:00.000"; // u,h00:m00:s00.f000
public static final String Fmt_Short = "short"; // u,h##:m#0:s00;
public static final String Fmt_Default = "0.000"; // v,#.000
public static final String Fmt_NoFractionals = "0"; // v,#
@gplx.Internal protected static final int[] Divisors = {
1, //1 fracs
1000, //1,000 fracs in a second
60000, //60,000 fracs in a minute (60 seconds * 1,000)
3600000, //3,600,000 fracs in an hour (60 minutes * 60,000)
};
public static final String MajorDelimiter = ":";
public static final int
Idx_Frac = 0
, Idx_Sec = 1
, Idx_Min = 2
, Idx_Hour = 3;
static int[] ZeroPadding = {3, 2, 2, 2,};
static String[] Sprs = {".", MajorDelimiter, MajorDelimiter, "",};
public static Time_span cast(Object arg) {try {return (Time_span)arg;} catch(Exception exc) {throw Err_.new_type_mismatch_w_exc(exc, Time_span.class, arg);}}
public static final double Ratio_f_to_s = 1000;
}