activerecord-jdbc-adapter-ficoh 1.3.21-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +462 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +36 -0
  6. data/CONTRIBUTING.md +49 -0
  7. data/Gemfile +68 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +277 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +298 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +63 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +64 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +22 -0
  43. data/lib/arjdbc/db2.rb +4 -0
  44. data/lib/arjdbc/db2/adapter.rb +802 -0
  45. data/lib/arjdbc/db2/as400.rb +137 -0
  46. data/lib/arjdbc/db2/column.rb +177 -0
  47. data/lib/arjdbc/db2/connection_methods.rb +45 -0
  48. data/lib/arjdbc/derby.rb +3 -0
  49. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  50. data/lib/arjdbc/derby/adapter.rb +567 -0
  51. data/lib/arjdbc/derby/connection_methods.rb +16 -0
  52. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  53. data/lib/arjdbc/discover.rb +104 -0
  54. data/lib/arjdbc/firebird.rb +4 -0
  55. data/lib/arjdbc/firebird/adapter.rb +468 -0
  56. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  57. data/lib/arjdbc/h2.rb +3 -0
  58. data/lib/arjdbc/h2/adapter.rb +335 -0
  59. data/lib/arjdbc/h2/connection_methods.rb +22 -0
  60. data/lib/arjdbc/hsqldb.rb +3 -0
  61. data/lib/arjdbc/hsqldb/adapter.rb +304 -0
  62. data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
  63. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  64. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  65. data/lib/arjdbc/informix.rb +5 -0
  66. data/lib/arjdbc/informix/adapter.rb +160 -0
  67. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  68. data/lib/arjdbc/jdbc.rb +62 -0
  69. data/lib/arjdbc/jdbc/adapter.rb +997 -0
  70. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  71. data/lib/arjdbc/jdbc/arel_support.rb +149 -0
  72. data/lib/arjdbc/jdbc/base_ext.rb +34 -0
  73. data/lib/arjdbc/jdbc/callbacks.rb +52 -0
  74. data/lib/arjdbc/jdbc/column.rb +83 -0
  75. data/lib/arjdbc/jdbc/connection.rb +26 -0
  76. data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
  77. data/lib/arjdbc/jdbc/driver.rb +44 -0
  78. data/lib/arjdbc/jdbc/error.rb +75 -0
  79. data/lib/arjdbc/jdbc/extension.rb +69 -0
  80. data/lib/arjdbc/jdbc/java.rb +13 -0
  81. data/lib/arjdbc/jdbc/type_cast.rb +154 -0
  82. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  83. data/lib/arjdbc/mssql.rb +7 -0
  84. data/lib/arjdbc/mssql/adapter.rb +822 -0
  85. data/lib/arjdbc/mssql/column.rb +207 -0
  86. data/lib/arjdbc/mssql/connection_methods.rb +72 -0
  87. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  88. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  89. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  90. data/lib/arjdbc/mssql/types.rb +343 -0
  91. data/lib/arjdbc/mssql/utils.rb +82 -0
  92. data/lib/arjdbc/mysql.rb +3 -0
  93. data/lib/arjdbc/mysql/adapter.rb +998 -0
  94. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  95. data/lib/arjdbc/mysql/column.rb +167 -0
  96. data/lib/arjdbc/mysql/connection_methods.rb +137 -0
  97. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  98. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  99. data/lib/arjdbc/oracle.rb +4 -0
  100. data/lib/arjdbc/oracle/adapter.rb +968 -0
  101. data/lib/arjdbc/oracle/column.rb +136 -0
  102. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  103. data/lib/arjdbc/postgresql.rb +3 -0
  104. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  105. data/lib/arjdbc/postgresql/adapter.rb +1498 -0
  106. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  107. data/lib/arjdbc/postgresql/base/oid.rb +412 -0
  108. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
  109. data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
  110. data/lib/arjdbc/postgresql/column.rb +640 -0
  111. data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
  112. data/lib/arjdbc/postgresql/explain_support.rb +53 -0
  113. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  114. data/lib/arjdbc/postgresql/oid_types.rb +265 -0
  115. data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
  116. data/lib/arjdbc/railtie.rb +11 -0
  117. data/lib/arjdbc/sqlite3.rb +3 -0
  118. data/lib/arjdbc/sqlite3/adapter.rb +654 -0
  119. data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
  120. data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
  121. data/lib/arjdbc/sybase.rb +2 -0
  122. data/lib/arjdbc/sybase/adapter.rb +47 -0
  123. data/lib/arjdbc/tasks.rb +13 -0
  124. data/lib/arjdbc/tasks/database_tasks.rb +66 -0
  125. data/lib/arjdbc/tasks/databases.rake +91 -0
  126. data/lib/arjdbc/tasks/databases3.rake +239 -0
  127. data/lib/arjdbc/tasks/databases4.rake +39 -0
  128. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  129. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  130. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  131. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  132. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  133. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  134. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  135. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  136. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  137. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  138. data/lib/arjdbc/util/table_copier.rb +108 -0
  139. data/lib/arjdbc/version.rb +8 -0
  140. data/lib/generators/jdbc/USAGE +9 -0
  141. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  142. data/pom.xml +285 -0
  143. data/rails_generators/jdbc_generator.rb +15 -0
  144. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  145. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  146. data/rakelib/01-tomcat.rake +51 -0
  147. data/rakelib/02-test.rake +151 -0
  148. data/rakelib/bundler_ext.rb +11 -0
  149. data/rakelib/db.rake +58 -0
  150. data/rakelib/rails.rake +77 -0
  151. data/src/java/arjdbc/ArJdbcModule.java +288 -0
  152. data/src/java/arjdbc/db2/DB2Module.java +77 -0
  153. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
  154. data/src/java/arjdbc/derby/DerbyModule.java +180 -0
  155. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
  156. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  157. data/src/java/arjdbc/h2/H2Module.java +50 -0
  158. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
  159. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
  160. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
  161. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  162. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  163. data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
  164. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  165. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  166. data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
  167. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
  168. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
  169. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  170. data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
  171. data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
  172. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
  173. data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
  174. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
  175. data/src/java/arjdbc/oracle/OracleModule.java +81 -0
  176. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
  177. data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
  178. data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
  179. data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
  180. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
  181. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
  182. data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
  184. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  185. data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
  186. data/src/java/arjdbc/util/NumberUtils.java +50 -0
  187. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  188. data/src/java/arjdbc/util/QuotingUtils.java +139 -0
  189. data/src/java/arjdbc/util/StringCache.java +60 -0
  190. data/src/java/arjdbc/util/StringHelper.java +155 -0
  191. metadata +288 -0
@@ -0,0 +1,517 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright 2014 Karol Bucek.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+ package arjdbc.util;
25
+
26
+ import java.sql.Date;
27
+ import java.sql.Time;
28
+ import java.sql.Timestamp;
29
+ import java.util.TimeZone;
30
+
31
+ import org.joda.time.DateTime;
32
+ import org.joda.time.DateTimeZone;
33
+ import org.jruby.Ruby;
34
+ import org.jruby.RubyClass;
35
+ import org.jruby.RubyFixnum;
36
+ import org.jruby.RubyFloat;
37
+ import org.jruby.RubyString;
38
+ import org.jruby.RubyTime;
39
+ import org.jruby.runtime.ThreadContext;
40
+ import org.jruby.runtime.builtin.IRubyObject;
41
+ import org.jruby.util.ByteList;
42
+
43
+ import static arjdbc.jdbc.RubyJdbcConnection.getBase;
44
+ import static arjdbc.util.StringHelper.decByte;
45
+
46
+ /**
47
+ * Utilities for handling/converting dates and times.
48
+ * @author kares
49
+ */
50
+ public abstract class DateTimeUtils {
51
+
52
+ @SuppressWarnings("deprecation")
53
+ public static ByteList timeToString(final Time time) {
54
+ final ByteList str = new ByteList(8); // hh:mm:ss
55
+
56
+ int hours = time.getHours();
57
+ int minutes = time.getMinutes();
58
+ int seconds = time.getSeconds();
59
+
60
+ str.append( decByte( hours / 10 ) );
61
+ str.append( decByte( hours % 10 ) );
62
+
63
+ str.append( ':' );
64
+
65
+ str.append( decByte( minutes / 10 ) );
66
+ str.append( decByte( minutes % 10 ) );
67
+
68
+ str.append( ':' );
69
+
70
+ str.append( decByte( seconds / 10 ) );
71
+ str.append( decByte( seconds % 10 ) );
72
+
73
+ return str;
74
+ }
75
+
76
+ @SuppressWarnings("deprecation")
77
+ public static ByteList dateToString(final Date date) {
78
+ final ByteList str = new ByteList(10); // "2000-00-00"
79
+
80
+ int year = date.getYear() + 1900;
81
+ int month = date.getMonth() + 1;
82
+ int day = date.getDate();
83
+
84
+ str.append( decByte( ( year / 1000 ) % 10 ) );
85
+ str.append( decByte( ( year / 100 ) % 10 ) );
86
+ str.append( decByte( ( year / 10 ) % 10 ) );
87
+ str.append( decByte( year % 10 ) );
88
+
89
+ str.append( '-' );
90
+
91
+ str.append( decByte( month / 10 ) );
92
+ str.append( decByte( month % 10 ) );
93
+
94
+ str.append( '-' );
95
+
96
+ str.append( decByte( day / 10 ) );
97
+ str.append( decByte( day % 10 ) );
98
+
99
+ return str;
100
+ }
101
+
102
+ @SuppressWarnings("deprecation")
103
+ public static ByteList timestampToString(final Timestamp timestamp) {
104
+ final ByteList str = new ByteList(29); // yyyy-mm-dd hh:mm:ss.fffffffff
105
+
106
+ int year = timestamp.getYear() + 1900;
107
+ int month = timestamp.getMonth() + 1;
108
+ int day = timestamp.getDate();
109
+ int hours = timestamp.getHours();
110
+ int minutes = timestamp.getMinutes();
111
+ int seconds = timestamp.getSeconds();
112
+ int nanos = timestamp.getNanos();
113
+
114
+ str.append( decByte( ( year / 1000 ) % 10 ) );
115
+ str.append( decByte( ( year / 100 ) % 10 ) );
116
+ str.append( decByte( ( year / 10 ) % 10 ) );
117
+ str.append( decByte( year % 10 ) );
118
+
119
+ str.append( '-' );
120
+
121
+ str.append( decByte( month / 10 ) );
122
+ str.append( decByte( month % 10 ) );
123
+
124
+ str.append( '-' );
125
+
126
+ str.append( decByte( day / 10 ) );
127
+ str.append( decByte( day % 10 ) );
128
+
129
+ if ( hours != 0 || minutes != 0 || seconds != 0 || nanos != 0 ) {
130
+ str.append(' ');
131
+
132
+ str.append( decByte( hours / 10 ) );
133
+ str.append( decByte( hours % 10 ) );
134
+
135
+ str.append( ':' );
136
+
137
+ str.append( decByte( minutes / 10 ) );
138
+ str.append( decByte( minutes % 10 ) );
139
+
140
+ str.append( ':' );
141
+
142
+ str.append( decByte( seconds / 10 ) );
143
+ str.append( decByte( seconds % 10 ) );
144
+
145
+ if ( nanos != 0 ) {
146
+ str.append( '.' );
147
+
148
+ int pow = 100000000; // nanos <= 999999999
149
+ for ( int i = 0; i < 8; i++ ) {
150
+ final int b = nanos / pow;
151
+ if ( b == 0 ) break; // done (no trailing zeros)
152
+ str.append( decByte( b % 10 ) );
153
+ pow = pow / 10;
154
+ }
155
+ }
156
+ }
157
+
158
+ return str;
159
+ }
160
+
161
+ @SuppressWarnings("deprecation")
162
+ public static IRubyObject newTime(final ThreadContext context, final Time time) {
163
+ //if ( time == null ) return context.nil;
164
+ final int hours = time.getHours();
165
+ final int minutes = time.getMinutes();
166
+ final int seconds = time.getSeconds();
167
+ //final int offset = time.getTimezoneOffset();
168
+
169
+ final DateTime dateTime;
170
+ if ( isDefaultTimeZoneUTC(context) ) {
171
+ dateTime = new DateTime(2000, 1, 1, hours, minutes, seconds, 0, DateTimeZone.UTC);
172
+ }
173
+ else {
174
+ dateTime = new DateTime(2000, 1, 1, hours, minutes, seconds, 0);
175
+ }
176
+ return RubyTime.newTime(context.runtime, dateTime);
177
+ }
178
+
179
+ @SuppressWarnings("deprecation")
180
+ public static IRubyObject newTime(final ThreadContext context, final Timestamp timestamp) {
181
+ //if ( time == null ) return context.nil;
182
+
183
+ final int year = timestamp.getYear() + 1900;
184
+ final int month = timestamp.getMonth() + 1;
185
+ final int day = timestamp.getDate();
186
+ final int hours = timestamp.getHours();
187
+ final int minutes = timestamp.getMinutes();
188
+ final int seconds = timestamp.getSeconds();
189
+ final int nanos = timestamp.getNanos(); // max 999-999-999
190
+
191
+ final DateTime dateTime;
192
+ if ( isDefaultTimeZoneUTC(context) ) {
193
+ dateTime = new DateTime(year, month, day, hours, minutes, seconds, 0, DateTimeZone.UTC);
194
+ }
195
+ else {
196
+ dateTime = new DateTime(year, month, day, hours, minutes, seconds, 0);
197
+ }
198
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
199
+ }
200
+
201
+ @SuppressWarnings("deprecation")
202
+ public static IRubyObject newTime(final ThreadContext context, final Date date) {
203
+ //if ( time == null ) return context.nil;
204
+
205
+ final int year = date.getYear() + 1900;
206
+ final int month = date.getMonth() + 1;
207
+ final int day = date.getDate();
208
+
209
+ DateTime dateTime = new DateTime(year, month, day, 0, 0, 0, 0);
210
+ return RubyTime.newTime(context.runtime, dateTime);
211
+ }
212
+
213
+ public static Timestamp convertToTimestamp(final RubyFloat value) {
214
+ final Timestamp timestamp = new Timestamp(value.getLongValue() * 1000); // millis
215
+
216
+ // for usec we shall not use: ((long) floatValue * 1000000) % 1000
217
+ // if ( usec >= 0 ) timestamp.setNanos( timestamp.getNanos() + usec * 1000 );
218
+ // due doubles inaccurate precision it's better to parse to_s :
219
+ final ByteList strValue = ((RubyString) value.to_s()).getByteList();
220
+ final int dot1 = strValue.lastIndexOf('.') + 1, dot4 = dot1 + 3;
221
+ final int len = strValue.getRealSize() - strValue.getBegin();
222
+ if ( dot1 > 0 && dot4 < len ) { // skip .123 but handle .1234
223
+ final int end = Math.min( len - dot4, 3 );
224
+ CharSequence usecSeq = strValue.subSequence(dot4, end);
225
+ final int usec = Integer.parseInt( usecSeq.toString() );
226
+ if ( usec < 10 ) { // 0.1234 ~> 4
227
+ timestamp.setNanos( timestamp.getNanos() + usec * 100 );
228
+ }
229
+ else if ( usec < 100 ) { // 0.12345 ~> 45
230
+ timestamp.setNanos( timestamp.getNanos() + usec * 10 );
231
+ }
232
+ else { // if ( usec < 1000 ) { // 0.123456 ~> 456
233
+ timestamp.setNanos( timestamp.getNanos() + usec );
234
+ }
235
+ }
236
+
237
+ return timestamp;
238
+ }
239
+
240
+ public static IRubyObject getTimeInDefaultTimeZone(final ThreadContext context, IRubyObject value) {
241
+ if ( value.respondsTo("to_time") ) {
242
+ value = value.callMethod(context, "to_time");
243
+ }
244
+ final String method = isDefaultTimeZoneUTC(context) ? "getutc" : "getlocal";
245
+ if ( value.respondsTo(method) ) {
246
+ value = value.callMethod(context, method);
247
+ }
248
+ return value;
249
+ }
250
+
251
+ public static boolean isDefaultTimeZoneUTC(final ThreadContext context) {
252
+ final String defaultTimeZone = getDefaultTimeZone(context);
253
+ if ( defaultTimeZone.length() != 3 ) return false;
254
+ return "utc".equalsIgnoreCase( defaultTimeZone );
255
+ }
256
+
257
+ private static String defaultTimeZone;
258
+
259
+ public static String getDefaultTimeZone(final ThreadContext context) {
260
+ String default_timezone = defaultTimeZone;
261
+ if ( default_timezone == null ) {
262
+ final RubyClass base = getBase(context.runtime);
263
+ default_timezone = base.callMethod(context, "default_timezone").toString(); // :utc
264
+ //synchronized (DateTimeUtils.class) { defaultTimeZone = default_timezone; }
265
+ }
266
+ return default_timezone;
267
+ }
268
+
269
+ public static double adjustTimeFromDefaultZone(final IRubyObject value) {
270
+ // Time's to_f is : ( millis * 1000 + usec ) / 1_000_000.0
271
+ final double time = value.convertToFloat().getDoubleValue(); // to_f
272
+ // NOTE: MySQL assumes default TZ thus need to adjust to match :
273
+ final int offset = TimeZone.getDefault().getOffset((long) time * 1000);
274
+ // Time's to_f is : ( millis * 1000 + usec ) / 1_000_000.0
275
+ return time - ( offset / 1000.0 );
276
+ }
277
+
278
+ public static IRubyObject parseDate(final ThreadContext context, final CharSequence str)
279
+ throws IllegalArgumentException {
280
+ final int len = str.length();
281
+
282
+ int year; int month; int day;
283
+
284
+ int start = nonSpaceIndex(str, 0, len); // Skip leading whitespace
285
+ int end = nonDigitIndex(str, start, len);
286
+
287
+ if ( end >= len ) {
288
+ throw new IllegalArgumentException("unexpected date value: '" + str + "'");
289
+ }
290
+
291
+ // year
292
+ year = extractIntValue(str, start, end);
293
+ start = end + 1; // Skip '-'
294
+
295
+ // month
296
+ end = nonDigitIndex(str, start, len);
297
+ month = extractIntValue(str, start, end);
298
+
299
+ //sep = str.charAt(end);
300
+ //if ( sep != '-' ) {
301
+ // throw new NumberFormatException("expected date to be dash-separated, got '" + sep + "'");
302
+ //}
303
+
304
+ start = end + 1; // Skip '-'
305
+
306
+ // day of month
307
+ end = nonDigitIndex(str, start, len);
308
+ day = extractIntValue(str, start, end);
309
+
310
+ final Ruby runtime = context.runtime;
311
+ return runtime.getClass("Date").
312
+ callMethod(context, "new", new IRubyObject[] {
313
+ RubyFixnum.newFixnum(runtime, year),
314
+ RubyFixnum.newFixnum(runtime, month),
315
+ RubyFixnum.newFixnum(runtime, day)
316
+ });
317
+ }
318
+
319
+ public static RubyTime parseDateTime(final ThreadContext context, final CharSequence str)
320
+ throws IllegalArgumentException {
321
+
322
+ boolean hasDate = false;
323
+ int year = 2000; int month = 1; int day = 1;
324
+ boolean hasTime = false;
325
+ int minute = 0; int hour = 0; int second = 0;
326
+ int millis = 0; long nanos = 0;
327
+
328
+ DateTimeZone zone = null; boolean bcEra = false;
329
+
330
+ // We try to parse these fields in order; all are optional
331
+ // (but some combinations don't make sense, e.g. if you have
332
+ // both date and time then they must be whitespace-separated).
333
+ // At least one of date and time must be present.
334
+
335
+ // leading whitespace
336
+ // yyyy-mm-dd
337
+ // whitespace
338
+ // hh:mm:ss
339
+ // whitespace
340
+ // timezone in one of the formats: +hh, -hh, +hh:mm, -hh:mm
341
+ // whitespace
342
+ // if date is present, an era specifier: AD or BC
343
+ // trailing whitespace
344
+
345
+ final int len = str.length();
346
+
347
+ int start = nonSpaceIndex(str, 0, len); // Skip leading whitespace
348
+ int end = nonDigitIndex(str, start, len);
349
+
350
+ // Possibly read date.
351
+ if ( end < len && str.charAt(end) == '-' ) {
352
+ hasDate = true;
353
+
354
+ // year
355
+ year = extractIntValue(str, start, end);
356
+ start = end + 1; // Skip '-'
357
+
358
+ // month
359
+ end = nonDigitIndex(str, start, len);
360
+ month = extractIntValue(str, start, end);
361
+
362
+ char sep = str.charAt(end);
363
+ if ( sep != '-' ) {
364
+ throw new IllegalArgumentException("expected date to be dash-separated, got '" + sep + "'");
365
+ }
366
+
367
+ start = end + 1; // Skip '-'
368
+
369
+ // day of month
370
+ end = nonDigitIndex(str, start, len);
371
+ day = extractIntValue(str, start, end);
372
+
373
+ start = nonSpaceIndex(str, end, len); // Skip trailing whitespace
374
+ }
375
+
376
+ // Possibly read time.
377
+ if ( start < len && Character.isDigit( str.charAt(start) ) ) {
378
+ hasTime = true;
379
+
380
+ // hours
381
+ end = nonDigitIndex(str, start, len);
382
+ hour = extractIntValue(str, start, end);
383
+
384
+ //sep = str.charAt(end);
385
+ //if ( sep != ':' ) {
386
+ // throw new IllegalArgumentException("expected time to be colon-separated, got '" + sep + "'");
387
+ //}
388
+
389
+ start = end + 1; // Skip ':'
390
+
391
+ // minutes
392
+ end = nonDigitIndex(str, start, len);
393
+ minute = extractIntValue(str, start, end);
394
+
395
+ //sep = str.charAt(end);
396
+ //if ( sep != ':' ) {
397
+ // throw new IllegalArgumentException("expected time to be colon-separated, got '" + sep + "'");
398
+ //}
399
+
400
+ start = end + 1; // Skip ':'
401
+
402
+ // seconds
403
+ end = nonDigitIndex(str, start, len);
404
+ second = extractIntValue(str, start, end);
405
+ start = end;
406
+
407
+ // Fractional seconds.
408
+ if ( start < len && str.charAt(start) == '.' ) {
409
+ end = nonDigitIndex(str, start + 1, len); // Skip '.'
410
+ int numlen = end - (start + 1);
411
+ if (numlen <= 3) {
412
+ millis = extractIntValue(str, start + 1, end);
413
+ for ( ; numlen < 3; ++numlen ) millis *= 10;
414
+ }
415
+ else {
416
+ nanos = extractIntValue(str, start + 1, end);
417
+ for ( ; numlen < 9; ++numlen ) nanos *= 10;
418
+ }
419
+
420
+ start = end;
421
+ }
422
+
423
+ start = nonSpaceIndex(str, start, len); // Skip trailing whitespace
424
+ }
425
+
426
+ // Possibly read timezone.
427
+ char sep = start < len ? str.charAt(start) : '\0';
428
+ if ( sep == '+' || sep == '-' ) {
429
+ int zoneSign = (sep == '-') ? -1 : 1;
430
+ int hoursOffset, minutesOffset, secondsOffset;
431
+
432
+ end = nonDigitIndex(str, start + 1, len); // Skip +/-
433
+ hoursOffset = extractIntValue(str, start + 1, end);
434
+ start = end;
435
+
436
+ if ( start < len && str.charAt(start) == ':' ) {
437
+ end = nonDigitIndex(str, start + 1, len); // Skip ':'
438
+ minutesOffset = extractIntValue(str, start + 1, end);
439
+ start = end;
440
+ } else {
441
+ minutesOffset = 0;
442
+ }
443
+
444
+ secondsOffset = 0;
445
+ if ( start < len && str.charAt(start) == ':' ) {
446
+ end = nonDigitIndex(str, start + 1, len); // Skip ':'
447
+ secondsOffset = extractIntValue(str, start + 1, end);
448
+ start = end;
449
+ }
450
+
451
+ // Setting offset does not seem to work correctly in all
452
+ // cases.. So get a fresh calendar for a synthetic timezone
453
+ // instead
454
+
455
+ int offset = zoneSign * hoursOffset * 60;
456
+ if (offset < 0) {
457
+ offset = offset - Math.abs(minutesOffset);
458
+ } else {
459
+ offset = offset + minutesOffset;
460
+ }
461
+ offset = (offset * 60 + secondsOffset) * 1000;
462
+ zone = DateTimeZone.forOffsetMillis(offset);
463
+
464
+ start = nonSpaceIndex(str, start, len); // Skip trailing whitespace
465
+ }
466
+
467
+ if ( hasDate && start < len ) {
468
+ final char e1 = str.charAt(start);
469
+ if ( e1 == 'A' && str.charAt(start + 1) == 'D' ) {
470
+ bcEra = false; start += 2;
471
+ }
472
+ else if ( e1 == 'B' && str.charAt(start + 1) == 'C' ) {
473
+ bcEra = true; start += 2;
474
+ }
475
+ }
476
+
477
+ if ( start < len ) {
478
+ throw new IllegalArgumentException("trailing junk: '" + str.subSequence(start, len - start) + "' on '" + str + "'");
479
+ }
480
+ if ( ! hasTime && ! hasDate ) {
481
+ throw new IllegalArgumentException("'"+ str +"' has neither date nor time");
482
+ }
483
+
484
+ if ( bcEra ) year = -1 * year;
485
+
486
+ if ( zone == null ) {
487
+ zone = isDefaultTimeZoneUTC(context) ? DateTimeZone.UTC : DateTimeZone.getDefault();
488
+ }
489
+
490
+ DateTime dateTime = new DateTime(year, month, day, hour, minute, second, millis, zone);
491
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
492
+ }
493
+
494
+ @SuppressWarnings("deprecation")
495
+ private static int nonSpaceIndex(final CharSequence str, int beg, int len) {
496
+ for ( int i = beg; i < len; i++ ) {
497
+ if ( ! Character.isSpace( str.charAt(i) ) ) return i;
498
+ }
499
+ return len;
500
+ }
501
+
502
+ private static int nonDigitIndex(final CharSequence str, int beg, int len) {
503
+ for ( int i = beg; i < len; i++ ) {
504
+ if ( ! Character.isDigit( str.charAt(i) ) ) return i;
505
+ }
506
+ return len;
507
+ }
508
+
509
+ private static int extractIntValue(final CharSequence str, int beg, int end) {
510
+ int n = 0;
511
+ for ( int i = beg; i < end; i++ ) {
512
+ n = 10 * n + ( str.charAt(i) - '0' );
513
+ }
514
+ return n;
515
+ }
516
+
517
+ }