Skip to content

Commit

Permalink
#7 Support timestamptz
Browse files Browse the repository at this point in the history
  • Loading branch information
tomyeh committed Jul 3, 2014
1 parent e8d1fcc commit 138423f
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 5 deletions.
8 changes: 7 additions & 1 deletion lib/connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,15 @@ class _Connection implements Connection {
case _PG_DATE:
return DateTime.parse(UTF8.decode(data));

case _PG_TIMESTAMPZ:
var str = UTF8.decode(data),
cc = str[str.length - 3];
if (cc == '+' || cc == '-')
str += ":00"; //convert to ISO 8601 (2012-02-27 13:27:00.123+02:00)
return DateTime.parse(str).toLocal();

// Not implemented yet - return a string.
case _PG_MONEY:
case _PG_TIMESTAMPZ:
case _PG_TIMETZ:
case _PG_TIME:
case _PG_INTERVAL:
Expand Down
9 changes: 5 additions & 4 deletions lib/format_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ _formatDateTime(DateTime datetime, String type) {
throw new Exception('Unexpected type: $type.'); //TODO exception type
}

if (t == 'timestamptz')
datetime = datetime.toUtc();

pad(i) {
var s = i.toString();
return s.length == 1 ? '0$s' : s;
Expand Down Expand Up @@ -111,10 +114,8 @@ _formatDateTime(DateTime datetime, String type) {
}
}

if (t == 'timestamptz') {
// Add timezone offset.
throw new Exception('Not implemented'); //TODO
}
if (t == 'timestamptz')
sb.write("Z");

return "'${sb.toString()}'";
}
Expand Down
12 changes: 12 additions & 0 deletions test/postgresql_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ main() {
);
});

test('Select timestamptz with milliseconds', () {
final DateTime time = new DateTime(1979, 12, 20, 9, 0, 12);

This comment has been minimized.

Copy link
@xxgreg

xxgreg Jul 7, 2014

I just noticed that this doesn't actually specify the millisecond field:
new DateTime(1979, 12, 20, 9, 0, 12);

You may have meant to write:
new DateTime(1979, 12, 20, 9, 0, 0, 12);

This is also buggy in the timestamp test, I didn't notice it in the other commit. I even copy and pasted it myself. Doh!

conn.execute('create temporary table dart_unit_test (a timestamptz)');
conn.execute("insert into dart_unit_test values (@time)", {"time": time});

conn.query('select a from dart_unit_test').toList().then(
expectAsync1((rows) {
expect(rows[0][0], equals(time));
})
);
});

test('Select DateTime', () {

conn.execute('create temporary table dart_unit_test (a date)');
Expand Down

4 comments on commit 138423f

@xxgreg
Copy link

@xxgreg xxgreg commented on 138423f Jul 7, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. I'm not 100% sure that the timezone handling is correct here. Are you sure that you should convert the datetime to UTC? (See format_value.dart:84)

Another option would be if the DateTime object is a localtime, to pass the localtime including the timezone name. The database will then convert the timestamp to UTC when storing it, and display it in the localtime of the database server. I think this is the idea behind the timestamptz type, but I'm not very familiar with it.

Here are some snippets from the Dart and Postgresql docs.

https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-core.DateTime

A DateTime object is in the local time zone unless explicitly created in the UTC time zone. Use isUtc to determine whether a DateTime object is based in UTC. Use the methods toLocal and toUtc to get the equivalent date/time value specified in the other time zone. Use timeZoneName to get an abbreviated name of the time zone for the DateTime object.

http://www.postgresql.org/docs/9.3/static/datatype-datetime.html#AEN5808

For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.

@tomyeh
Copy link
Owner Author

@tomyeh tomyeh commented on 138423f Jul 7, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting to UTC is a trick to simplify the formatting. Alternatively, we can format it under the local time and with a proper offset. However, they shall be the same, since Postgresql will convert it to UTC anyway and store it in UTC (for timestamptz).

Please be noticed that I just tested it on my applications. It could be just a quick fix for my environment. Please consider the pull request just a suggestion. It is more than welcome if you reject it and come out with a more complete solution.

@xxgreg
Copy link

@xxgreg xxgreg commented on 138423f Jul 7, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note - if you're looking for the quickest solution, you can pass the timestamp as a string instead of a DateTime object.

Regarding the UTC conversion, I think there is a problem if the application server and database server are running in different timezones. However I'm not sure if that is a valid use case or not. I might check what other database drivers do, and match their behaviour.

Another problem I noticed, is if the timestamptz value is null, then connection.dart:535 will throw.

I can come up with a fix for these issues if you like, but it will require waiting for a day or two. Let me know what you'd rather do. I'm happy either way. Thanks for your help. ;)

@tomyeh
Copy link
Owner Author

@tomyeh tomyeh commented on 138423f Jul 7, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing a string is something I'd like to avoid:)

if the application server and database server are running in different timezones.

Good question. In my implementation, it shall work if timestamptz is specified (such as @time:timestampz), since it forced to convert to UTC (with a proper time zone name, Z). However, if timestamptz is not specified, formatValue has no information but either assume it is timestamp or timestamptz. If assuming timestamp (which is the current implementation), it won't work since it is formatted in local time. If assuming timestamptz, I'm not sure if it broke the code using timestamp (I didn't use timestamp at all).

I can come up with a fix for these issues...

Yes, please. I can count on my aggressive and less stable version before the official one comes out.

Please sign in to comment.