From 763c7b52b7c024731f41999a7f8709c1cfdcb23d Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Sat, 18 Jan 2020 16:39:16 -0500 Subject: [PATCH] ParserFunctions: Support timezone / meridian formats [#648] --- 100_core/src/gplx/DateAdp.java | 4 + 100_core/src/gplx/DateAdp__tst.java | 6 + .../xowa/xtns/pfuncs/times/Pft_fmt_itm_.java | 12 ++ .../pfuncs/times/Pft_fmt_itm_seg_int.java | 12 +- .../pfuncs/times/Pft_fmt_itm_timezone.java | 51 ++++++++ .../times/Pft_func_time__basic__tst.java | 61 ++++++++++ .../gplx/xowa/xtns/pfuncs/times/Pxd_itm_.java | 2 + .../xowa/xtns/pfuncs/times/Pxd_itm_int.java | 2 +- .../xtns/pfuncs/times/Pxd_itm_meridian.java | 113 ++++++++++++++++++ .../xowa/xtns/pfuncs/times/Pxd_parser.java | 7 ++ 10 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_timezone.java create mode 100644 400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_meridian.java diff --git a/100_core/src/gplx/DateAdp.java b/100_core/src/gplx/DateAdp.java index fa12a01d3..be7f7c0fb 100644 --- a/100_core/src/gplx/DateAdp.java +++ b/100_core/src/gplx/DateAdp.java @@ -85,6 +85,9 @@ public class DateAdp implements CompareAble, Gfo_invk { : Timezone_offset_test ; } + public String Timezone_id() { + return "UTC"; // under.getTimeZone().getID(); // NOTE: timezone is always UTC, unless over-ridden by tests + } public DateAdp XtoUtc() { java.util.Date date = under.getTime(); java.util.TimeZone tz = under.getTimeZone(); @@ -109,6 +112,7 @@ public class DateAdp implements CompareAble, Gfo_invk { long dst_adj = dst ? 3600000 : 0; return (under.getTimeInMillis() + offsetFromUTC + dst_adj) / 1000; } + public int WeekOfYear() {return under.get(Calendar.WEEK_OF_YEAR);} public int Frac() {return under.get(Calendar.MILLISECOND);} public DateAdp Add_frac(int val) {return CloneAndAdd(Calendar.MILLISECOND, val);} diff --git a/100_core/src/gplx/DateAdp__tst.java b/100_core/src/gplx/DateAdp__tst.java index 02d99a22f..0bc081a95 100644 --- a/100_core/src/gplx/DateAdp__tst.java +++ b/100_core/src/gplx/DateAdp__tst.java @@ -60,6 +60,9 @@ public class DateAdp__tst { @Test public void XtoUtc() { fxt.Test__to_utc("2012-01-01 00:00", "2012-01-01 05:00"); //4=Wed } + @Test public void Timezone_id() { + fxt.Test__timezone_id("2015-12-26T10:03:53Z", "UTC"); + } } class DateAdp__fxt { public void Test__parse_gplx(String raw, String expd) { @@ -83,4 +86,7 @@ class DateAdp__fxt { public void Test__to_utc(String raw, String expd) { Tfds.Eq(expd, DateAdp_.parse_gplx(raw).XtoUtc().XtoStr_fmt_yyyy_MM_dd_HH_mm()); } + public void Test__timezone_id(String raw, String expd) { + Gftest.Eq__str(expd, DateAdp_.parse_gplx(raw).XtoUtc().Timezone_id()); + } } diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_.java index dd2abd9f3..6a4556f52 100644 --- a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_.java +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_.java @@ -52,6 +52,10 @@ public class Pft_fmt_itm_ { , Tid_hijiri_month_idx = 31 , Tid_hijiri_day_idx = 32 , Tid_hijiri_month_name = 33 + , Tid_timezone_id_full = 35 + , Tid_timezone_id_abrv = 36 + , Tid_timezone_offset_4 = 37 + , Tid_timezone_offset_4_colon = 38 ; public static final Pft_fmt_itm @@ -107,6 +111,10 @@ public class Pft_fmt_itm_ { , Hijiri_month_idx = new Pft_fmt_itm_hijiri_month_idx() , Hijiri_day_idx = new Pft_fmt_itm_hijiri_day_idx() , Hijiri_month_name = new Pft_fmt_itm_hijiri_month_name() + , Timezone_id_full = new Pft_fmt_itm_timezone_id(Bool_.N) + , Timezone_id_abrv = new Pft_fmt_itm_timezone_id(Bool_.Y) + , Timezone_offset_4 = new Pft_fmt_itm_timezone_offset_4(Bool_.N) + , Timezone_offset_4_colon = new Pft_fmt_itm_timezone_offset_4(Bool_.Y) ; public static final Btrie_fast_mgr Regy = Btrie_fast_mgr.cs() .Add(Byte_ascii.Ltr_Y , Pft_fmt_itm_.Year_len4) // 2012 @@ -139,6 +147,10 @@ public class Pft_fmt_itm_ { .Add(Byte_ascii.Ltr_Z , Pft_fmt_itm_.Timezone_offset) // timezone offset in seconds .Add(Byte_ascii.Ltr_c , Pft_fmt_itm_.Iso_fmt) // 2012-01-02T03:04:05+00:00 .Add(Byte_ascii.Ltr_r , Pft_fmt_itm_.Rfc_5322) // Mon 02 Jan 2012 08:04:05 +0000 + .Add(Byte_ascii.Ltr_e , Pft_fmt_itm_.Timezone_id_full) // UTC + .Add(Byte_ascii.Ltr_T , Pft_fmt_itm_.Timezone_id_abrv) // UTC + .Add(Byte_ascii.Ltr_O , Pft_fmt_itm_.Timezone_offset_4) // timezone offset in seconds +0000 + .Add(Byte_ascii.Ltr_P , Pft_fmt_itm_.Timezone_offset_4_colon) // timezone offset in seconds with colon +00:00 .Add("xr" , Pft_fmt_itm_.Roman) // MCXI .Add("xkY" , Pft_fmt_itm_.Thai) // Year += 543 .Add("xoY" , Pft_fmt_itm_.Minguo) // Year -= 1911 diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_seg_int.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_seg_int.java index 0b51540af..e470205d1 100644 --- a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_seg_int.java +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_seg_int.java @@ -81,12 +81,14 @@ class Pft_fmt_itm_dayOfYear implements Pft_fmt_itm { class Pft_fmt_itm_am_pm implements Pft_fmt_itm { public int TypeId() {return Pft_fmt_itm_.Tid_AmPm;} public void Fmt(Bry_bfr bfr, Xowe_wiki wiki, Xol_lang_itm lang, DateAdp date, Pft_func_formatdate_bldr bldr) { - boolean am = date.Hour() < 13; + boolean am = date.Hour() < 12; byte[] val = null; - if ( am && lower) val = Ary_am_lower; - else if ( am && !lower) val = Ary_am_upper; - else if (!am && lower) val = Ary_pm_lower; - else if (!am && !lower) val = Ary_pm_upper; + if (am) { + val = lower ? Ary_am_lower : Ary_am_upper; + } + else { + val = lower ? Ary_pm_lower : Ary_pm_upper; + } bfr.Add(val); } private static final byte[] Ary_am_upper = Bry_.new_a7("AM"), Ary_pm_upper = Bry_.new_a7("PM"), Ary_am_lower = Bry_.new_a7("am"), Ary_pm_lower = Bry_.new_a7("pm"); public Pft_fmt_itm_am_pm(boolean lower) {this.lower = lower;} private boolean lower; diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_timezone.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_timezone.java new file mode 100644 index 000000000..cf21d9d95 --- /dev/null +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_fmt_itm_timezone.java @@ -0,0 +1,51 @@ +/* +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.xowa.xtns.pfuncs.times; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.pfuncs.*; +import gplx.xowa.langs.*; +class Pft_fmt_itm_timezone_offset_4 implements Pft_fmt_itm { + private final boolean colon; + public Pft_fmt_itm_timezone_offset_4(boolean colon) {this.colon = colon;} + public int TypeId() {return Pft_fmt_itm_.Tid_timezone_offset_4;} + public void Fmt(Bry_bfr bfr, Xowe_wiki wiki, Xol_lang_itm lang, DateAdp date, Pft_func_formatdate_bldr bldr) { + // get tz_secs + int tz_secs = date.Timezone_offset(); + + // add "+" or "-" + if (tz_secs < 0) { + bfr.Add_byte(Byte_ascii.Dash); + tz_secs *= -1; + } + else { + bfr.Add_byte(Byte_ascii.Plus); + } + + // calc mins / hours + int tz_mins = tz_secs / 60; + + // add bfr + bfr.Add_int_fixed((tz_mins / 60), 2); + if (colon) + bfr.Add_byte(Byte_ascii.Colon); + bfr.Add_int_fixed((tz_mins % 60), 2); + } +} +class Pft_fmt_itm_timezone_id implements Pft_fmt_itm { + public Pft_fmt_itm_timezone_id(boolean abrv) {} + public int TypeId() {return Pft_fmt_itm_.Tid_timezone_id_full;} + public void Fmt(Bry_bfr bfr, Xowe_wiki wiki, Xol_lang_itm lang, DateAdp date, Pft_func_formatdate_bldr bldr) { + bfr.Add_str_a7(date.Timezone_id()); + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_func_time__basic__tst.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_func_time__basic__tst.java index 62742a4d6..d93849934 100644 --- a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_func_time__basic__tst.java +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pft_func_time__basic__tst.java @@ -107,4 +107,65 @@ public class Pft_func_time__basic__tst { @Test public void Iso8601_T() {fxt.Test_parse_tmpl_str("{{#time:Y-m-d h:i:s A|T1:23}}" , "2012-01-02 01:23:00 AM");} // handle "T" flag; PAGE:pl.w:StarCraft_II:_Wings_of_Liberty @Test public void Iso8601_T_ws() {fxt.Test_parse_tmpl_str("{{#time:Y-m-d h:i:s A|T 1:23}}" , "2012-01-02 01:23:00 AM");} // handle "T" flag and ws @Test public void Iso8601_T_fail() {fxt.Test_parse_tmpl_str("{{#time:Y-m-d h:i:s A|T2012-01-02}}" , "Invalid hour: T");} // handle "T" flag and ws + + @Test public void Timezone_id() {// hard-coded to return "UTC"; DATE:2020-01-18 + fxt.Test_parse_tmpl_str("{{#time:e|2012-01-02 03:04:05}}", "UTC"); + } + @Test public void Timezone_abrv() { // hard-coded to return "UTC"; DATE:2020-01-18 + fxt.Test_parse_tmpl_str("{{#time:T|2012-01-02 03:04:05}}", "UTC"); + } + @Test public void Timezone_offset_4() { // hard-coded to return "+0000"; DATE:2020-01-18 + fxt.Test_parse_tmpl_str("{{#time:O|2012-01-02 03:04:05}}", "+0000"); + } + @Test public void Timezone_offset_4_colon() { // hard-coded to return "+00:00"; DATE:2020-01-18 + fxt.Test_parse_tmpl_str("{{#time:O|2012-01-02 03:04:05}}", "+0000"); + } + @Test public void Timezone_offset_4__vals() { + Pft_func_time__fxt func_fxt = new Pft_func_time__fxt(fxt); + func_fxt.Test__parse_w_offset_mins( 60, "{{#time:O|2012-01-02 03:04:05}}", "+0100"); + func_fxt.Test__parse_w_offset_mins(-300, "{{#time:O|2012-01-02 03:04:05}}", "-0500"); + func_fxt.Test__parse_w_offset_mins( 330, "{{#time:O|2012-01-02 03:04:05}}", "+0530"); + } + @Test public void Meridian__AM__bugfix() { + fxt.Test_parse_tmpl_str("{{#time:A|2012-01-02 12:00:00}}", "PM"); + } + @Test public void Meridian__AM() { + fxt.Test_parse_tmpl_str("{{#time:A|12:00 AM}}", "AM"); + fxt.Test_parse_tmpl_str("{{#time:A|12:00 a.m}}", "AM"); + fxt.Test_parse_tmpl_str("{{#time:A|12:00 am.}}", "AM"); + fxt.Test_parse_tmpl_str("{{#time:A|12:00 a.m.}}", "AM"); + } + @Test public void Meridian__PM() { + fxt.Test_parse_tmpl_str("{{#time:A|2:00 PM}}", "PM"); + fxt.Test_parse_tmpl_str("{{#time:A|2:00 p.m}}", "PM"); + fxt.Test_parse_tmpl_str("{{#time:A|2:00 pm.}}", "PM"); + fxt.Test_parse_tmpl_str("{{#time:A|2:00 p.m.}}", "PM"); + } + @Test public void Meridian__basic() { + fxt.Test_parse_tmpl_str("{{#time:h:i:s A|1PM}}" , "01:00:00 PM"); + fxt.Test_parse_tmpl_str("{{#time:h:i:s A|1 PM}}" , "01:00:00 PM"); + fxt.Test_parse_tmpl_str("{{#time:h:i:s A|1:02 PM}}" , "01:02:00 PM"); + fxt.Test_parse_tmpl_str("{{#time:h:i:s A|1:02:03 PM}}" , "01:02:03 PM"); + } + @Test public void Meridian__errors() { + fxt.Test_parse_tmpl_str("{{#time:h|PM}}" , "Invalid time. Nothing found before meridian"); + fxt.Test_parse_tmpl_str("{{#time:h|monday PM}}" , "Invalid time. Text found before meridian"); + fxt.Test_parse_tmpl_str("{{#time:h|0 AM}}" , "Invalid time. Invalid digit for meridian: 0"); + fxt.Test_parse_tmpl_str("{{#time:h|13 AM}}" , "Invalid time. Invalid digit for meridian: 13"); + fxt.Test_parse_tmpl_str("{{#time:h|1 :00 AM}}" , "Invalid time. Text found before 1st colon"); + fxt.Test_parse_tmpl_str("{{#time:h|1 :00:00 AM}}" , "Invalid time. Text found before 2nd colon"); + } +} +class Pft_func_time__fxt { + private final Xop_fxt fxt; + public Pft_func_time__fxt(Xop_fxt fxt) {this.fxt = fxt;} + public void Test__parse_w_offset_mins(int offset, String raw, String expd) { + try { + DateAdp.Timezone_offset_test = offset * 60; + fxt.Test_parse_tmpl_str(raw, expd); + } + finally { + DateAdp.Timezone_offset_test = Int_.Min_value; + } + } } diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_.java index 4f8415fe8..ad2957890 100644 --- a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_.java +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_.java @@ -40,6 +40,7 @@ class Pxd_itm_ { , Tid_unit_relative = 11 // next, previous , Tid_unixtime = 12 // @123 , Tid_iso8601_t = 13 // T + , Tid_meridian = 14 // PM , Tid_dash = Byte_ascii.Dash , Tid_dot = Byte_ascii.Dot , Tid_slash = Byte_ascii.Slash @@ -101,5 +102,6 @@ class Pft_func_time_log { , Invalid_second = Gfo_msg_itm_.new_warn_(owner, "Invalid second: ~{0}") , Invalid_date = Gfo_msg_itm_.new_warn_(owner, "Invalid date: ~{0}") , Invalid_timezone = Gfo_msg_itm_.new_warn_(owner, "Invalid timezone: ~{0}") + , Invalid_time = Gfo_msg_itm_.new_warn_(owner, "Invalid time. ~{0}") ; } diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_int.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_int.java index 0f3e6ebd9..9c2e100dd 100644 --- a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_int.java +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_int.java @@ -40,7 +40,7 @@ class Pxd_itm_int extends Pxd_itm_base implements Pxd_itm_int_interface { public int Val() {return val;} public Pxd_itm_int Val_(int v) {val = v; return this;} private int val; public boolean Val_is_adj() {return val_is_adj;} public void Val_is_adj_(boolean v) {val_is_adj = v;} private boolean val_is_adj; public int Xto_int_or(int or) {return val;} - public int Digits() {return digits;} private int digits; + public int Digits() {return digits;} private int digits; // NOTE: digits exists primarily for year evaluation; EX: 11 vs 2011 @Override public boolean Time_ini(Pxd_date_bldr bldr) { int seg_idx = this.Seg_idx(); if (seg_idx == Pxd_itm_base.Seg_idx_skip) return true; diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_meridian.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_meridian.java new file mode 100644 index 000000000..3b636aef3 --- /dev/null +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_itm_meridian.java @@ -0,0 +1,113 @@ +/* +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.xowa.xtns.pfuncs.times; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.pfuncs.*; +import gplx.core.brys.*; +class Pxd_itm_meridian extends Pxd_itm_base implements Pxd_itm_prototype { + private final boolean pm; + public Pxd_itm_meridian(int ary_idx, boolean pm) {this.Ctor(ary_idx); this.pm = pm;} + @Override public byte Tkn_tid() {return Pxd_itm_.Tid_meridian;} + @Override public int Eval_idx() {return 19;} + @Override public boolean Time_ini(Pxd_date_bldr bldr) {return true;} + public Pxd_itm MakeNew(int ary_idx) {return new Pxd_itm_meridian(ary_idx, pm);} + @Override public boolean Eval(Pxd_parser state) { + Pxd_itm[] tkns = state.Tkns(); + + // init some vars + Pxd_itm itm = null; + boolean colon_found = false; + + // get previous item before meridian, skipping ws + int itm_idx; + for (itm_idx = this.Ary_idx() - 1; itm_idx > -1; --itm_idx) { + itm = tkns[itm_idx]; + if (itm.Tkn_tid() != Pxd_itm_.Tid_ws) + break; + } + + // null-check to handle error cases like "AM" + if (itm == null) { + return Fail(state, "Nothing found before meridian"); + } + else { + // itm is not int, then err; EX: "monday AM" + if (itm.Tkn_tid() != Pxd_itm_.Tid_int) { + return Fail(state, "Text found before meridian"); + } + // itm is an int + else { + // int_itm may be minute / seconds if there are colons; EX "12AM" vs "12:00AM" or "12:00:00AM" + if (itm_idx > 0 && tkns[itm_idx - 1].Tkn_tid() == Pxd_itm_.Tid_colon) { + colon_found = true; + itm_idx--; + // make sure int exists before ":" + if (itm_idx > 0 && tkns[itm_idx - 1].Tkn_tid() == Pxd_itm_.Tid_int) { + itm = tkns[itm_idx - 1]; + itm_idx--; + // check again for ":" + if (itm_idx > 0 && tkns[itm_idx - 1].Tkn_tid() == Pxd_itm_.Tid_colon) { + itm_idx--; + // make sure int exists before ":" + if (itm_idx > 0 && tkns[itm_idx - 1].Tkn_tid() == Pxd_itm_.Tid_int) { + itm = tkns[itm_idx - 1]; + } + else { + return Fail(state, "Text found before 2nd colon"); + } + } + } + else { + return Fail(state, "Text found before 1st colon"); + } + } + + // hour found + Pxd_itm_int hour_itm = (Pxd_itm_int)itm; + int hour = hour_itm.Val(); + + // invalid digit; fail + if (hour == 0 || hour > 12) { + return Fail(state, "Invalid digit for meridian: " + Int_.To_str(hour)); + } + else { + // update hour + if (pm) { + // convert "1 PM" -> hour=13 + hour_itm.Val_(hour + 12); + } + else { // convert "12 AM" -> 00:00 + if (hour == 12) { + hour_itm.Val_(0); + } + } + + // if no colon, eval int now else int will get eval'd as year; EX: "12 AM" + if (!colon_found) + return Pxd_eval_seg.Eval_as_h(state, hour_itm); + else { + // colon exists; don't need to do anything else b/c parser will handle normally; i.e.: "12:00 PM" is just like "12:00" + } + } + } + } + + return true; + } + private boolean Fail(Pxd_parser state, String err) { + // MW just throws "Invalid time."; add extra description message for debugging purposes + state.Err_set(Pft_func_time_log.Invalid_time, Bfr_arg_.New_bry(err)); + return false; + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_parser.java b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_parser.java index b0cc9745d..39b89eee1 100644 --- a/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_parser.java +++ b/400_xowa/src/gplx/xowa/xtns/pfuncs/times/Pxd_parser.java @@ -222,6 +222,8 @@ class Pxd_parser_ { Init_relative(); trie.Add_obj(Pxd_itm_unixtime.Name_const, new Pxd_itm_unixtime(-1, -1)); trie.Add_obj(Pxd_itm_iso8601_t.Name_const, new Pxd_itm_iso8601_t(-1, -1)); + Init_meridian(Bool_.N, "am", "a.m", "am.", "a.m."); + Init_meridian(Bool_.Y, "pm", "p.m", "pm.", "p.m."); } private static void Init_reg_months(String[] names) { for (int i = 0; i < names.length; i++) @@ -255,6 +257,11 @@ class Pxd_parser_ { trie.Add_obj("previous", Pxd_itm_unit_relative.Prev); trie.Add_obj("this", Pxd_itm_unit_relative.This); } + private static void Init_meridian(boolean is_pm, String... ary) { + Pxd_itm_meridian meridian = new Pxd_itm_meridian(-1, is_pm); + for (String itm : ary) + trie.Add_obj(itm, meridian); + } } /* NOTE_1:parsing works by completing previous items and then setting current;