activerecord-jdbc-alt-adapter 50.3.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +100 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +92 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +26 -0
  9. data/README.md +240 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +336 -0
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/activerecord-jdbc-alt-adapter.gemspec +56 -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/postgresql_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  31. data/lib/activerecord-jdbc-adapter.rb +1 -0
  32. data/lib/arel/visitors/compat.rb +60 -0
  33. data/lib/arel/visitors/db2.rb +137 -0
  34. data/lib/arel/visitors/derby.rb +112 -0
  35. data/lib/arel/visitors/firebird.rb +79 -0
  36. data/lib/arel/visitors/h2.rb +25 -0
  37. data/lib/arel/visitors/hsqldb.rb +32 -0
  38. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  39. data/lib/arel/visitors/sql_server.rb +225 -0
  40. data/lib/arel/visitors/sql_server/ng42.rb +294 -0
  41. data/lib/arel/visitors/sqlserver.rb +214 -0
  42. data/lib/arjdbc.rb +19 -0
  43. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  44. data/lib/arjdbc/abstract/core.rb +74 -0
  45. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  46. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  47. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  48. data/lib/arjdbc/db2.rb +4 -0
  49. data/lib/arjdbc/db2/adapter.rb +789 -0
  50. data/lib/arjdbc/db2/as400.rb +130 -0
  51. data/lib/arjdbc/db2/column.rb +167 -0
  52. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  53. data/lib/arjdbc/derby.rb +3 -0
  54. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  55. data/lib/arjdbc/derby/adapter.rb +540 -0
  56. data/lib/arjdbc/derby/connection_methods.rb +20 -0
  57. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  58. data/lib/arjdbc/discover.rb +104 -0
  59. data/lib/arjdbc/firebird.rb +4 -0
  60. data/lib/arjdbc/firebird/adapter.rb +434 -0
  61. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  62. data/lib/arjdbc/h2.rb +3 -0
  63. data/lib/arjdbc/h2/adapter.rb +303 -0
  64. data/lib/arjdbc/h2/connection_methods.rb +27 -0
  65. data/lib/arjdbc/hsqldb.rb +3 -0
  66. data/lib/arjdbc/hsqldb/adapter.rb +297 -0
  67. data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
  68. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  69. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  70. data/lib/arjdbc/informix.rb +5 -0
  71. data/lib/arjdbc/informix/adapter.rb +162 -0
  72. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  73. data/lib/arjdbc/jdbc.rb +59 -0
  74. data/lib/arjdbc/jdbc/adapter.rb +475 -0
  75. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  76. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  77. data/lib/arjdbc/jdbc/callbacks.rb +53 -0
  78. data/lib/arjdbc/jdbc/column.rb +97 -0
  79. data/lib/arjdbc/jdbc/connection.rb +14 -0
  80. data/lib/arjdbc/jdbc/connection_methods.rb +37 -0
  81. data/lib/arjdbc/jdbc/error.rb +65 -0
  82. data/lib/arjdbc/jdbc/extension.rb +59 -0
  83. data/lib/arjdbc/jdbc/java.rb +13 -0
  84. data/lib/arjdbc/jdbc/railtie.rb +2 -0
  85. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
  86. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  87. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  88. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  89. data/lib/arjdbc/mssql.rb +7 -0
  90. data/lib/arjdbc/mssql/adapter.rb +384 -0
  91. data/lib/arjdbc/mssql/column.rb +29 -0
  92. data/lib/arjdbc/mssql/connection_methods.rb +79 -0
  93. data/lib/arjdbc/mssql/database_statements.rb +134 -0
  94. data/lib/arjdbc/mssql/errors.rb +6 -0
  95. data/lib/arjdbc/mssql/explain_support.rb +129 -0
  96. data/lib/arjdbc/mssql/extensions.rb +36 -0
  97. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  98. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  99. data/lib/arjdbc/mssql/old_adapter.rb +804 -0
  100. data/lib/arjdbc/mssql/old_column.rb +200 -0
  101. data/lib/arjdbc/mssql/quoting.rb +101 -0
  102. data/lib/arjdbc/mssql/schema_creation.rb +31 -0
  103. data/lib/arjdbc/mssql/schema_definitions.rb +74 -0
  104. data/lib/arjdbc/mssql/schema_statements.rb +329 -0
  105. data/lib/arjdbc/mssql/transaction.rb +69 -0
  106. data/lib/arjdbc/mssql/types.rb +52 -0
  107. data/lib/arjdbc/mssql/types/binary_types.rb +33 -0
  108. data/lib/arjdbc/mssql/types/date_and_time_types.rb +134 -0
  109. data/lib/arjdbc/mssql/types/deprecated_types.rb +40 -0
  110. data/lib/arjdbc/mssql/types/numeric_types.rb +71 -0
  111. data/lib/arjdbc/mssql/types/string_types.rb +56 -0
  112. data/lib/arjdbc/mssql/utils.rb +66 -0
  113. data/lib/arjdbc/mysql.rb +3 -0
  114. data/lib/arjdbc/mysql/adapter.rb +140 -0
  115. data/lib/arjdbc/mysql/connection_methods.rb +166 -0
  116. data/lib/arjdbc/oracle/adapter.rb +863 -0
  117. data/lib/arjdbc/postgresql.rb +3 -0
  118. data/lib/arjdbc/postgresql/adapter.rb +687 -0
  119. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  120. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  121. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  122. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  123. data/lib/arjdbc/postgresql/column.rb +51 -0
  124. data/lib/arjdbc/postgresql/connection_methods.rb +67 -0
  125. data/lib/arjdbc/postgresql/name.rb +24 -0
  126. data/lib/arjdbc/postgresql/oid_types.rb +266 -0
  127. data/lib/arjdbc/railtie.rb +11 -0
  128. data/lib/arjdbc/sqlite3.rb +3 -0
  129. data/lib/arjdbc/sqlite3/adapter.rb +678 -0
  130. data/lib/arjdbc/sqlite3/connection_methods.rb +59 -0
  131. data/lib/arjdbc/sybase.rb +2 -0
  132. data/lib/arjdbc/sybase/adapter.rb +47 -0
  133. data/lib/arjdbc/tasks.rb +13 -0
  134. data/lib/arjdbc/tasks/database_tasks.rb +31 -0
  135. data/lib/arjdbc/tasks/databases.rake +48 -0
  136. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  137. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  138. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  139. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  140. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  141. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  142. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  143. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  144. data/lib/arjdbc/util/table_copier.rb +110 -0
  145. data/lib/arjdbc/version.rb +3 -0
  146. data/lib/generators/jdbc/USAGE +9 -0
  147. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  148. data/lib/jdbc_adapter.rb +2 -0
  149. data/lib/jdbc_adapter/rake_tasks.rb +4 -0
  150. data/lib/jdbc_adapter/version.rb +4 -0
  151. data/pom.xml +114 -0
  152. data/rails_generators/jdbc_generator.rb +15 -0
  153. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  154. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  155. data/rakelib/01-tomcat.rake +51 -0
  156. data/rakelib/02-test.rake +132 -0
  157. data/rakelib/bundler_ext.rb +11 -0
  158. data/rakelib/db.rake +75 -0
  159. data/rakelib/rails.rake +223 -0
  160. data/src/java/arjdbc/ArJdbcModule.java +276 -0
  161. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  162. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  163. data/src/java/arjdbc/derby/DerbyModule.java +178 -0
  164. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  165. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  166. data/src/java/arjdbc/h2/H2Module.java +50 -0
  167. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +85 -0
  168. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  169. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
  170. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  171. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  172. data/src/java/arjdbc/jdbc/ConnectionFactory.java +45 -0
  173. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  174. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  175. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  176. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  177. data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
  178. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3979 -0
  179. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  180. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +508 -0
  181. data/src/java/arjdbc/mysql/MySQLModule.java +152 -0
  182. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  183. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  184. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +455 -0
  185. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  186. data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
  187. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  188. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +192 -0
  189. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +948 -0
  190. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  191. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  192. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  193. data/src/java/arjdbc/util/DateTimeUtils.java +699 -0
  194. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  195. data/src/java/arjdbc/util/QuotingUtils.java +137 -0
  196. data/src/java/arjdbc/util/StringCache.java +63 -0
  197. data/src/java/arjdbc/util/StringHelper.java +145 -0
  198. metadata +269 -0
@@ -0,0 +1,699 @@
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.Chronology;
32
+ import org.joda.time.DateTime;
33
+ import org.joda.time.DateTimeZone;
34
+ import org.joda.time.chrono.ISOChronology;
35
+ import org.jruby.Ruby;
36
+ import org.jruby.RubyFloat;
37
+ import org.jruby.RubyString;
38
+ import org.jruby.RubyTime;
39
+ import org.jruby.javasupport.Java;
40
+ import org.jruby.runtime.Block;
41
+ import org.jruby.runtime.ThreadContext;
42
+ import org.jruby.runtime.builtin.IRubyObject;
43
+ import org.jruby.util.ByteList;
44
+ import org.jruby.util.TypeConverter;
45
+
46
+ import static arjdbc.util.StringHelper.decByte;
47
+
48
+ /**
49
+ * Utilities for handling/converting dates and times.
50
+ * @author kares
51
+ */
52
+ public abstract class DateTimeUtils {
53
+ public static RubyTime toTime(final ThreadContext context, final IRubyObject value) {
54
+ if (!(value instanceof RubyTime)) { // unlikely
55
+ return (RubyTime) TypeConverter.convertToTypeWithCheck(value, context.runtime.getTime(), "to_time");
56
+ }
57
+ return (RubyTime) value;
58
+ }
59
+
60
+ public static DateTime dateTimeInZone(final DateTime dateTime, final DateTimeZone zone) {
61
+ if (zone == dateTime.getZone()) return dateTime;
62
+ return dateTime.withZone(zone);
63
+ }
64
+
65
+ @SuppressWarnings("deprecation")
66
+ public static ByteList timeToString(final Time time) {
67
+ final ByteList str = new ByteList(8); // hh:mm:ss
68
+
69
+ int hours = time.getHours();
70
+ int minutes = time.getMinutes();
71
+ int seconds = time.getSeconds();
72
+
73
+ str.append( decByte( hours / 10 ) );
74
+ str.append( decByte( hours % 10 ) );
75
+
76
+ str.append( ':' );
77
+
78
+ str.append( decByte( minutes / 10 ) );
79
+ str.append( decByte( minutes % 10 ) );
80
+
81
+ str.append( ':' );
82
+
83
+ str.append( decByte( seconds / 10 ) );
84
+ str.append( decByte( seconds % 10 ) );
85
+
86
+ return str;
87
+ }
88
+
89
+ private static final byte[] DUMMY_TIME_PREFIX = { '2','0','0','0','-','0','1','-','0','1' };
90
+
91
+ @SuppressWarnings("deprecation")
92
+ public static ByteList dummyTimeToString(final Timestamp time) {
93
+ final ByteList str = new ByteList(29); // yyyy-mm-dd hh:mm:ss.fffffffff
94
+
95
+ int hours = time.getHours();
96
+ int minutes = time.getMinutes();
97
+ int seconds = time.getSeconds();
98
+ int nanos = time.getNanos();
99
+
100
+ str.append( DUMMY_TIME_PREFIX );
101
+
102
+ str.append( ' ' );
103
+
104
+ formatTime(str, hours, minutes, seconds, nanos);
105
+
106
+ return str;
107
+ }
108
+
109
+ @SuppressWarnings("deprecation")
110
+ public static ByteList dateToString(final Date date) {
111
+ final ByteList str = new ByteList(10); // "2000-00-00"
112
+
113
+ int year = date.getYear() + 1900;
114
+ int month = date.getMonth() + 1;
115
+ int day = date.getDate();
116
+
117
+ str.append( decByte( ( year / 1000 ) % 10 ) );
118
+ str.append( decByte( ( year / 100 ) % 10 ) );
119
+ str.append( decByte( ( year / 10 ) % 10 ) );
120
+ str.append( decByte( year % 10 ) );
121
+
122
+ str.append( '-' );
123
+
124
+ str.append( decByte( month / 10 ) );
125
+ str.append( decByte( month % 10 ) );
126
+
127
+ str.append( '-' );
128
+
129
+ str.append( decByte( day / 10 ) );
130
+ str.append( decByte( day % 10 ) );
131
+
132
+ return str;
133
+ }
134
+
135
+ @SuppressWarnings("deprecation")
136
+ public static ByteList timestampToString(final Timestamp timestamp) {
137
+ final ByteList str = new ByteList(29); // yyyy-mm-dd hh:mm:ss.fffffffff
138
+
139
+ int year = timestamp.getYear() + 1900;
140
+ int month = timestamp.getMonth() + 1;
141
+ int day = timestamp.getDate();
142
+ int hours = timestamp.getHours();
143
+ int minutes = timestamp.getMinutes();
144
+ int seconds = timestamp.getSeconds();
145
+ int nanos = timestamp.getNanos();
146
+
147
+ str.append( decByte( ( year / 1000 ) % 10 ) );
148
+ str.append( decByte( ( year / 100 ) % 10 ) );
149
+ str.append( decByte( ( year / 10 ) % 10 ) );
150
+ str.append( decByte( year % 10 ) );
151
+
152
+ str.append( '-' );
153
+
154
+ str.append( decByte( month / 10 ) );
155
+ str.append( decByte( month % 10 ) );
156
+
157
+ str.append( '-' );
158
+
159
+ str.append( decByte( day / 10 ) );
160
+ str.append( decByte( day % 10 ) );
161
+
162
+ if ( hours != 0 || minutes != 0 || seconds != 0 || nanos != 0 ) {
163
+ str.append( ' ' );
164
+
165
+ formatTime(str, hours, minutes, seconds, nanos);
166
+ }
167
+
168
+ return str;
169
+ }
170
+
171
+ private static final int NANO_DIGITS_RAILS_CAN_MANAGE = 6; // 'normally' would have been 8
172
+
173
+ private static void formatTime(final ByteList str,
174
+ final int hours, final int minutes, final int seconds, final int nanos) {
175
+
176
+ str.append( decByte( hours / 10 ) );
177
+ str.append( decByte( hours % 10 ) );
178
+
179
+ str.append( ':' );
180
+
181
+ str.append( decByte( minutes / 10 ) );
182
+ str.append( decByte( minutes % 10 ) );
183
+
184
+ str.append( ':' );
185
+
186
+ str.append( decByte( seconds / 10 ) );
187
+ str.append( decByte( seconds % 10 ) );
188
+
189
+ if ( nanos != 0 ) {
190
+ str.append( '.' );
191
+ // NOTE: Rails (still) terrible at handling full nanos precision: '12:30:00.99990000'
192
+ int pow = 100000000; // nanos <= 999999999
193
+ for ( int i = 0; i < NANO_DIGITS_RAILS_CAN_MANAGE; i++ ) {
194
+ final int b = nanos / pow;
195
+ if ( b == 0 ) break; // done (no trailing zeros)
196
+ str.append( decByte( b % 10 ) );
197
+ pow = pow / 10;
198
+ }
199
+ }
200
+ }
201
+
202
+ @SuppressWarnings("deprecation")
203
+ public static RubyTime newDummyTime(final ThreadContext context, final Time time, final DateTimeZone defaultZone) {
204
+
205
+ final int hours = time.getHours();
206
+ final int minutes = time.getMinutes();
207
+ final int seconds = time.getSeconds();
208
+ //final int offset = time.getTimezoneOffset();
209
+
210
+ DateTime dateTime = new DateTime(2000, 1, 1, hours, minutes, seconds, defaultZone);
211
+ return RubyTime.newTime(context.runtime, dateTime, 0);
212
+ }
213
+
214
+ @SuppressWarnings("deprecation")
215
+ public static RubyTime newDummyTime(final ThreadContext context, final Timestamp time, final DateTimeZone defaultZone) {
216
+
217
+ final int hours = time.getHours();
218
+ final int minutes = time.getMinutes();
219
+ final int seconds = time.getSeconds();
220
+ final int nanos = time.getNanos(); // max 999-999-999
221
+
222
+ DateTime dateTime = new DateTime(2000, 1, 1, hours, minutes, seconds, defaultZone);
223
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
224
+ }
225
+
226
+ @SuppressWarnings("deprecation")
227
+ public static RubyTime newTime(final ThreadContext context, final Timestamp timestamp, final DateTimeZone defaultZone) {
228
+
229
+ final int year = timestamp.getYear() + 1900;
230
+ final int month = timestamp.getMonth() + 1;
231
+ final int day = timestamp.getDate();
232
+ final int hours = timestamp.getHours();
233
+ final int minutes = timestamp.getMinutes();
234
+ final int seconds = timestamp.getSeconds();
235
+ final int nanos = timestamp.getNanos(); // max 999-999-999
236
+
237
+ DateTime dateTime = new DateTime(year, month, day, hours, minutes, seconds, 0, defaultZone);
238
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
239
+ }
240
+
241
+ @SuppressWarnings("deprecation")
242
+ public static RubyTime newDateAsTime(final ThreadContext context, final Date date, final DateTimeZone zone) {
243
+
244
+ final int year = date.getYear() + 1900;
245
+ final int month = date.getMonth() + 1;
246
+ final int day = date.getDate();
247
+
248
+ DateTime dateTime = new DateTime(year, month, day, 0, 0, 0, 0, zone);
249
+ return RubyTime.newTime(context.runtime, dateTime, 0);
250
+ }
251
+
252
+ @SuppressWarnings("deprecation")
253
+ public static IRubyObject newDate(final ThreadContext context, final Date date, final DateTimeZone zone) {
254
+
255
+ final int year = date.getYear() + 1900;
256
+ final int month = date.getMonth() + 1;
257
+ final int day = date.getDate();
258
+
259
+ return newDate(context, year, month, day, ISOChronology.getInstance(zone));
260
+ }
261
+
262
+ // @Deprecated
263
+ public static Timestamp convertToTimestamp(final RubyFloat value) {
264
+ final Timestamp timestamp = new Timestamp(value.getLongValue() * 1000); // millis
265
+
266
+ // for usec we shall not use: ((long) floatValue * 1000000) % 1000
267
+ // if ( usec >= 0 ) timestamp.setNanos( timestamp.getNanos() + usec * 1000 );
268
+ // due doubles inaccurate precision it's better to parse to_s :
269
+ final ByteList strValue = ((RubyString) value.to_s()).getByteList();
270
+ final int dot1 = strValue.lastIndexOf('.') + 1, dot4 = dot1 + 3;
271
+ final int len = strValue.getRealSize() - strValue.getBegin();
272
+ if ( dot1 > 0 && dot4 < len ) { // skip .123 but handle .1234
273
+ final int end = Math.min( len - dot4, 3 );
274
+ CharSequence usecSeq = strValue.subSequence(dot4, end);
275
+ final int usec = Integer.parseInt( usecSeq.toString() );
276
+ if ( usec < 10 ) { // 0.1234 ~> 4
277
+ timestamp.setNanos( timestamp.getNanos() + usec * 100 );
278
+ }
279
+ else if ( usec < 100 ) { // 0.12345 ~> 45
280
+ timestamp.setNanos( timestamp.getNanos() + usec * 10 );
281
+ }
282
+ else { // if ( usec < 1000 ) { // 0.123456 ~> 456
283
+ timestamp.setNanos( timestamp.getNanos() + usec );
284
+ }
285
+ }
286
+
287
+ return timestamp;
288
+ }
289
+
290
+ public static double adjustTimeFromDefaultZone(final IRubyObject value) {
291
+ // Time's to_f is : ( millis * 1000 + usec ) / 1_000_000.0
292
+ final double time = value.convertToFloat().getDoubleValue(); // to_f
293
+ // NOTE: MySQL assumes default TZ thus need to adjust to match :
294
+ final int offset = TimeZone.getDefault().getOffset((long) time * 1000);
295
+ // Time's to_f is : ( millis * 1000 + usec ) / 1_000_000.0
296
+ return time - ( offset / 1000.0 );
297
+ }
298
+
299
+ public static IRubyObject parseDate(final ThreadContext context, final CharSequence str, final DateTimeZone defaultZone)
300
+ throws IllegalArgumentException {
301
+ final int len = str.length();
302
+
303
+ int year; int month; int day;
304
+
305
+ int start = nonSpaceIndex(str, 0, len); // Skip leading whitespace
306
+ int end = nonDigitIndex(str, start, len);
307
+
308
+ if ( end >= len ) {
309
+ throw new IllegalArgumentException("unexpected date value: '" + str + "'");
310
+ }
311
+
312
+ // year
313
+ year = extractIntValue(str, start, end);
314
+ start = end + 1; // Skip '-'
315
+
316
+ // month
317
+ end = nonDigitIndex(str, start, len);
318
+ month = extractIntValue(str, start, end);
319
+
320
+ //sep = str.charAt(end);
321
+ //if ( sep != '-' ) {
322
+ // throw new NumberFormatException("expected date to be dash-separated, got '" + sep + "'");
323
+ //}
324
+
325
+ start = end + 1; // Skip '-'
326
+
327
+ // day of month
328
+ end = nonDigitIndex(str, start, len);
329
+ day = extractIntValue(str, start, end);
330
+
331
+ return newDate(context, year, month, day, ISOChronology.getInstance(defaultZone));
332
+ }
333
+
334
+ public static IRubyObject parseTime(final ThreadContext context, final CharSequence str, final DateTimeZone defaultZone)
335
+ throws IllegalArgumentException {
336
+ final int len = str.length();
337
+
338
+ int hour; int minute; int second;
339
+ int millis = 0; long nanos = 0;
340
+
341
+ int start = nonSpaceIndex(str, 0, len); // Skip leading whitespace
342
+ int end = nonDigitIndex(str, start, len);
343
+
344
+ if ( end >= len ) {
345
+ throw new IllegalArgumentException("unexpected date value: '" + str + "'");
346
+ }
347
+
348
+ // hours
349
+ hour = extractIntValue(str, start, end);
350
+ start = end + 1; // Skip ':'
351
+
352
+ end = nonDigitIndex(str, start, len);
353
+ // minutes
354
+ minute = extractIntValue(str, start, end);
355
+ start = end + 1; // Skip ':'
356
+
357
+ end = nonDigitIndex(str, start, len);
358
+ // seconds
359
+ second = extractIntValue(str, start, end);
360
+ start = end;
361
+
362
+ // Fractional seconds.
363
+ if ( start < len && str.charAt(start) == '.' ) {
364
+ end = nonDigitIndex(str, start + 1, len); // Skip '.'
365
+ int numlen = end - (start + 1);
366
+ if (numlen <= 3) {
367
+ millis = extractIntValue(str, start + 1, end);
368
+ for ( ; numlen < 3; ++numlen ) millis *= 10;
369
+ }
370
+ else {
371
+ nanos = extractIntValue(str, start + 1, end);
372
+ for ( ; numlen < 9; ++numlen ) nanos *= 10;
373
+ }
374
+ //start = end;
375
+ }
376
+
377
+ DateTime dateTime = new DateTime(2000, 1, 1, hour, minute, second, millis, defaultZone);
378
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
379
+ }
380
+
381
+ public static RubyTime parseDateTime(final ThreadContext context, final CharSequence str, final DateTimeZone defaultZone)
382
+ throws IllegalArgumentException {
383
+
384
+ boolean hasDate = false;
385
+ int year = 2000; int month = 1; int day = 1;
386
+ boolean hasTime = false;
387
+ int minute = 0; int hour = 0; int second = 0;
388
+ int millis = 0; long nanos = 0;
389
+
390
+ DateTimeZone zone = defaultZone; boolean bcEra = false;
391
+
392
+ // We try to parse these fields in order; all are optional
393
+ // (but some combinations don't make sense, e.g. if you have
394
+ // both date and time then they must be whitespace-separated).
395
+ // At least one of date and time must be present.
396
+
397
+ // leading whitespace
398
+ // yyyy-mm-dd
399
+ // whitespace
400
+ // hh:mm:ss
401
+ // whitespace
402
+ // timezone in one of the formats: +hh, -hh, +hh:mm, -hh:mm
403
+ // whitespace
404
+ // if date is present, an era specifier: AD or BC
405
+ // trailing whitespace
406
+
407
+ final int len = str.length();
408
+
409
+ int start = nonSpaceIndex(str, 0, len); // Skip leading whitespace
410
+ int end = nonDigitIndex(str, start, len);
411
+
412
+ // Possibly read date.
413
+ if ( end < len && str.charAt(end) == '-' ) {
414
+ hasDate = true;
415
+
416
+ // year
417
+ year = extractIntValue(str, start, end);
418
+ start = end + 1; // Skip '-'
419
+
420
+ // month
421
+ end = nonDigitIndex(str, start, len);
422
+ month = extractIntValue(str, start, end);
423
+
424
+ char sep = str.charAt(end);
425
+ if ( sep != '-' ) {
426
+ throw new IllegalArgumentException("expected date to be dash-separated, got '" + sep + "'");
427
+ }
428
+
429
+ start = end + 1; // Skip '-'
430
+
431
+ // day of month
432
+ end = nonDigitIndex(str, start, len);
433
+ day = extractIntValue(str, start, end);
434
+
435
+ start = nonSpaceIndex(str, end, len); // Skip trailing whitespace
436
+ }
437
+
438
+ // Possibly read time.
439
+ if ( start < len && Character.isDigit( str.charAt(start) ) ) {
440
+ hasTime = true;
441
+
442
+ // hours
443
+ end = nonDigitIndex(str, start, len);
444
+ hour = extractIntValue(str, start, end);
445
+
446
+ //sep = str.charAt(end);
447
+ //if ( sep != ':' ) {
448
+ // throw new IllegalArgumentException("expected time to be colon-separated, got '" + sep + "'");
449
+ //}
450
+
451
+ start = end + 1; // Skip ':'
452
+
453
+ // minutes
454
+ end = nonDigitIndex(str, start, len);
455
+ minute = extractIntValue(str, start, end);
456
+
457
+ //sep = str.charAt(end);
458
+ //if ( sep != ':' ) {
459
+ // throw new IllegalArgumentException("expected time to be colon-separated, got '" + sep + "'");
460
+ //}
461
+
462
+ start = end + 1; // Skip ':'
463
+
464
+ // seconds
465
+ end = nonDigitIndex(str, start, len);
466
+ second = extractIntValue(str, start, end);
467
+ start = end;
468
+
469
+ // Fractional seconds.
470
+ if ( start < len && str.charAt(start) == '.' ) {
471
+ end = nonDigitIndex(str, start + 1, len); // Skip '.'
472
+ int numlen = end - (start + 1);
473
+ if (numlen <= 3) {
474
+ millis = extractIntValue(str, start + 1, end);
475
+ for ( ; numlen < 3; ++numlen ) millis *= 10;
476
+ }
477
+ else {
478
+ // Make sure we always define millis to work around bug in
479
+ // strftime('%6N') in older version of JRuby (discovered in 9.1.16.0)
480
+ millis = extractIntValue(str, start + 1, start + 4);
481
+ nanos = extractIntValue(str, start + 4, end);
482
+ for ( ; numlen < 9; ++numlen ) nanos *= 10;
483
+ }
484
+
485
+ start = end;
486
+ }
487
+
488
+ start = nonSpaceIndex(str, start, len); // Skip trailing whitespace
489
+ }
490
+
491
+ // Possibly read timezone.
492
+ char sep = start < len ? str.charAt(start) : '\0';
493
+ if ( sep == '+' || sep == '-' ) {
494
+ int zoneSign = (sep == '-') ? -1 : 1;
495
+ int hoursOffset, minutesOffset, secondsOffset;
496
+
497
+ end = nonDigitIndex(str, start + 1, len); // Skip +/-
498
+ hoursOffset = extractIntValue(str, start + 1, end);
499
+ start = end;
500
+
501
+ if ( start < len && str.charAt(start) == ':' ) {
502
+ end = nonDigitIndex(str, start + 1, len); // Skip ':'
503
+ minutesOffset = extractIntValue(str, start + 1, end);
504
+ start = end;
505
+ } else {
506
+ minutesOffset = 0;
507
+ }
508
+
509
+ secondsOffset = 0;
510
+ if ( start < len && str.charAt(start) == ':' ) {
511
+ end = nonDigitIndex(str, start + 1, len); // Skip ':'
512
+ secondsOffset = extractIntValue(str, start + 1, end);
513
+ start = end;
514
+ }
515
+
516
+ // Setting offset does not seem to work correctly in all
517
+ // cases.. So get a fresh calendar for a synthetic timezone
518
+ // instead
519
+
520
+ int offset = zoneSign * hoursOffset * 60;
521
+ if (offset < 0) {
522
+ offset = offset - Math.abs(minutesOffset);
523
+ } else {
524
+ offset = offset + minutesOffset;
525
+ }
526
+ offset = (offset * 60 + secondsOffset) * 1000;
527
+ zone = DateTimeZone.forOffsetMillis(offset);
528
+
529
+ start = nonSpaceIndex(str, start, len); // Skip trailing whitespace
530
+ }
531
+
532
+ if ( hasDate && start < len ) {
533
+ final char e1 = str.charAt(start);
534
+ if ( e1 == 'A' && str.charAt(start + 1) == 'D' ) {
535
+ bcEra = false; start += 2;
536
+ }
537
+ else if ( e1 == 'B' && str.charAt(start + 1) == 'C' ) {
538
+ bcEra = true; start += 2;
539
+ }
540
+ }
541
+
542
+ if ( start < len ) {
543
+ throw new IllegalArgumentException("trailing junk: '" + str.subSequence(start, len - start) + "' on '" + str + "'");
544
+ }
545
+ if ( ! hasTime && ! hasDate ) {
546
+ throw new IllegalArgumentException("'"+ str +"' has neither date nor time");
547
+ }
548
+
549
+ if ( bcEra ) year = -1 * year + 1; // since JODA is treating year 0 as non-existent
550
+
551
+ DateTime dateTime = new DateTime(year, month, day, hour, minute, second, millis, zone);
552
+ return RubyTime.newTime(context.runtime, dateTime, nanos);
553
+ }
554
+
555
+ private static IRubyObject newDate(final ThreadContext context, final int year, final int month, final int day,
556
+ final ISOChronology chronology) {
557
+ // NOTE: JRuby really needs a native date.rb until than its a bit costly going from ...
558
+ // java.sql.Date -> allocating a DateTime proxy, help a bit by shooting at the internals
559
+ //
560
+ // def initialize(dt_or_ajd=0, of=0, sg=ITALY, sub_millis=0)
561
+ // if JODA::DateTime === dt_or_ajd
562
+ // @dt = dt_or_ajd
563
+
564
+ DateTime dateTime = new DateTime(year, month, day, 0, 0, 0, 0, chronology);
565
+
566
+ final Ruby runtime = context.runtime;
567
+ return runtime.getClass("Date").newInstance(context, Java.getInstance(runtime, dateTime), Block.NULL_BLOCK);
568
+ }
569
+
570
+ @SuppressWarnings("deprecation")
571
+ private static int nonSpaceIndex(final CharSequence str, int beg, int len) {
572
+ for ( int i = beg; i < len; i++ ) {
573
+ if ( ! Character.isSpace( str.charAt(i) ) ) return i;
574
+ }
575
+ return len;
576
+ }
577
+
578
+ private static int nonDigitIndex(final CharSequence str, int beg, int len) {
579
+ for ( int i = beg; i < len; i++ ) {
580
+ if ( ! Character.isDigit( str.charAt(i) ) ) return i;
581
+ }
582
+ return len;
583
+ }
584
+
585
+ private static int extractIntValue(final CharSequence str, int beg, int end) {
586
+ int n = 0;
587
+ for ( int i = beg; i < end; i++ ) {
588
+ n = 10 * n + ( str.charAt(i) - '0' );
589
+ }
590
+ return n;
591
+ }
592
+
593
+
594
+ private static final char[] ZEROS = {'0', '0', '0', '0', '0', '0'};
595
+ private static final char[][] NUMBERS;
596
+
597
+ static {
598
+ // maximum value is 60 (seconds)
599
+ NUMBERS = new char[60][];
600
+ for (int i = 0; i < NUMBERS.length; i++) {
601
+ NUMBERS[i] = ((i < 10 ? "0" : "") + Integer.toString(i)).toCharArray();
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Converts a ruby timestamp to a java string, optionally with timezone and timezone adjustment
607
+ * @param context
608
+ * @param value the ruby value, typically a Time
609
+ * @param zone DateTimeZone to adjust to, optional
610
+ * @param withZone include timezone in the string?
611
+ * @return timestamp as string
612
+ */
613
+ public static String timestampTimeToString(final ThreadContext context,
614
+ final IRubyObject value, DateTimeZone zone, boolean withZone) {
615
+ RubyTime timeValue = toTime(context, value);
616
+ DateTime dt = timeValue.getDateTime();
617
+
618
+ StringBuilder sb = new StringBuilder(36);
619
+
620
+ int year = dt.getYear();
621
+ if (year <= 0) {
622
+ year--;
623
+ } else if (zone != null) {
624
+ dt = dateTimeInZone(dt, zone);
625
+ year = dt.getYear();
626
+ }
627
+
628
+ Chronology chrono = dt.getChronology();
629
+ long millis = dt.getMillis();
630
+
631
+ // always use 4 digits for year to avoid short dates being misinterpreted
632
+ sb.append(Math.abs(year));
633
+ int lead = 4 - sb.length();
634
+ if (lead > 0) sb.insert(0, ZEROS, 0, lead);
635
+ sb.append('-');
636
+ sb.append(NUMBERS[chrono.monthOfYear().get(millis)]);
637
+ sb.append('-');
638
+ sb.append(NUMBERS[chrono.dayOfMonth().get(millis)]);
639
+ if (year < 0) sb.append(" BC");
640
+ sb.append(' ');
641
+
642
+ appendTime(sb, chrono, millis, (int) timeValue.getUSec(), withZone);
643
+
644
+ return sb.toString();
645
+ }
646
+
647
+ /**
648
+ * Converts a ruby time to a java string, optionally with timezone and timezone adjustment
649
+ * @param context
650
+ * @param value the ruby value, typically a Time
651
+ * @param zone DateTimeZone to adjust to, optional
652
+ * @param withZone include timezone in the string?
653
+ * @return time as string
654
+ */
655
+ public static String timeString(final ThreadContext context,
656
+ final IRubyObject value, DateTimeZone zone, boolean withZone) {
657
+ StringBuilder sb = new StringBuilder(21);
658
+ RubyTime timeValue = toTime(context, value);
659
+ DateTime dt = timeValue.getDateTime();
660
+ if (zone != null) dt = dateTimeInZone(dt, zone);
661
+
662
+ appendTime(sb, dt.getChronology(), dt.getMillis(), (int) timeValue.getUSec(), withZone);
663
+ return sb.toString();
664
+ }
665
+
666
+ private static void appendTime(StringBuilder sb, Chronology chrono,
667
+ long millis, int usec, boolean withZone) {
668
+ sb.append(NUMBERS[chrono.hourOfDay().get(millis)]);
669
+ sb.append(':');
670
+ sb.append(NUMBERS[chrono.minuteOfHour().get(millis)]);
671
+ sb.append(':');
672
+ sb.append(NUMBERS[chrono.secondOfMinute().get(millis)]);
673
+
674
+ // PG has microsecond resolution. Change when nanos are required
675
+ int micros = chrono.millisOfSecond().get(millis) * 1000 + usec;
676
+ if (micros > 0) {
677
+ sb.append('.');
678
+
679
+ int len = sb.length();
680
+ sb.append(micros);
681
+ int lead = 6 - (sb.length() - len);
682
+ if (lead > 0) sb.insert(len, ZEROS, 0, lead);
683
+
684
+ for (int end = sb.length() - 1; sb.charAt(end) == '0'; end--) {
685
+ sb.setLength(end);
686
+ }
687
+ }
688
+
689
+ if (withZone) {
690
+ int offset = chrono.getZone().getOffset(millis) / 1000;
691
+ int absoff = Math.abs(offset);
692
+ int hours = absoff / 3600;
693
+ int mins = (absoff - hours * 3600) / 60;
694
+
695
+ sb.append(offset < 0 ? '-' : '+');
696
+ sb.append(NUMBERS[hours]).append(':').append(NUMBERS[mins]);
697
+ }
698
+ }
699
+ }