activerecord-jdbc-alt-adapter 50.3.0-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 (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
+ }