{"action":"created","comment":{"url":"https://api.github.com/repos/irmen/Pyrolite/pulls/comments/27425372","id":27425372,"diff_hunk":"@@ -0,0 +1,79 @@\n+package net.razorvine.pickle.objects;\n+\n+import net.razorvine.pickle.IObjectConstructor;\n+import net.razorvine.pickle.PickleException;\n+import net.razorvine.pickle.objects.Tzinfo;\n+import java.util.TimeZone;\n+\n+public class TimeZoneConstructor implements IObjectConstructor {\n+\tpublic static int UTC = 1;\n+\tpublic static int PYTZ = 2;\n+\tpublic static int DATEUTIL_TZUTC = 3;\n+\tpublic static int TZINFO = 4;\n+\n+\tprivate int pythontype;\n+\n+\tpublic TimeZoneConstructor(int pythontype) {\n+\t\tthis.pythontype = pythontype;\n+\t}\n+\t\n+\t@Override\n+\tpublic Object construct(Object[] args) throws PickleException {\n+\tif (this.pythontype == UTC)\n+\t\treturn createUTC();\n+\tif (this.pythontype == PYTZ)\n+\t\treturn createZoneFromPytz(args);\n+\tif (this.pythontype == DATEUTIL_TZUTC)\n+\t\treturn createInfoFromDateutilTzutc(args);\n+\tif (this.pythontype == TZINFO)\n+\t\treturn createInfo(args);\n+\n+\tthrow new PickleException(\"invalid object type\");\n+\t}\n+\n+\tpublic Object reconstruct(Object baseConstructor, Object state) {\n+\t\tif (!(state instanceof Tzinfo))\n+\t\t\tthrow new PickleException(\"invalid pickle data for tzinfo reconstruction; expected emtpy tzinfo state class\");\n+\t\tif (!(baseConstructor instanceof TimeZoneConstructor))\n+\t\t\tthrow new PickleException(\"invalid pickle data for tzinfo reconstruction; expected a TimeZoneConstructor from a known tzinfo subclass\");\n+\n+\t\t// The subclass (this) is reconstructing the state given the base class and state. If it is known that the\n+\t\t// subclass is always UTC, ie dateutil.tz.tzutc, then we can just return the timezone we know matches that.\n+\t\tif (this.pythontype == DATEUTIL_TZUTC) {\n+\t\t\treturn TimeZone.getTimeZone(\"UTC\");\n+\t\t} else {\n+\t\t\tthrow new PickleException(\"unsupported pickle data for tzinfo reconstruction; support for tzinfo subclasses other than tztuc has not been implemented\");\n+\t\t}\n+\t}\n+\n+\tprivate Object createInfo(Object[] args) {\n+\t\t// args is empty, datetime.tzinfo objects are unpickled via setstate, so return an object which is ready to have it's state set\n+\t\treturn new Tzinfo();\n+\t}\n+\n+\tprivate Object createInfoFromDateutilTzutc(Object[] args) {\n+\t\t// In the case of the dateutil.tz.tzutc constructor, which is a python subclass of the datetime.tzinfo class, there is no state\n+\t\t// to set, because the zone is implied by the constructor. Pass the timezone indicated by hte constructor here\n+\t\treturn new Tzinfo(TimeZone.getTimeZone(\"UTC\"));\n+\t}\n+\n+\tprivate Object createZoneFromPytz(Object[] args) {\n+\n+\t\tif (args.length != 4 && args.length != 1)","path":"java/src/net/razorvine/pickle/objects/TimeZoneConstructor.java","position":62,"original_position":62,"commit_id":"caf399c16eb846916bf0713ab22dfd109acbc1e9","original_commit_id":"caf399c16eb846916bf0713ab22dfd109acbc1e9","user":{"login":"irmen","id":1771820,"avatar_url":"https://avatars.githubusercontent.com/u/1771820?v=3","gravatar_id":"","url":"https://api.github.com/users/irmen","html_url":"https://github.com/irmen","followers_url":"https://api.github.com/users/irmen/followers","following_url":"https://api.github.com/users/irmen/following{/other_user}","gists_url":"https://api.github.com/users/irmen/gists{/gist_id}","starred_url":"https://api.github.com/users/irmen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/irmen/subscriptions","organizations_url":"https://api.github.com/users/irmen/orgs","repos_url":"https://api.github.com/users/irmen/repos","events_url":"https://api.github.com/users/irmen/events{/privacy}","received_events_url":"https://api.github.com/users/irmen/received_events","type":"User","site_admin":false},"body":"Reading on, the code and the comment below are talking about either 4 or 1 arg. So it seems that the exception text has a typo.","created_at":"2015-03-30T19:53:53Z","updated_at":"2015-03-30T19:53:53Z","html_url":"https://github.com/irmen/Pyrolite/pull/22#discussion_r27425372","pull_request_url":"https://api.github.com/repos/irmen/Pyrolite/pulls/22","_links":{"self":{"href":"https://api.github.com/repos/irmen/Pyrolite/pulls/comments/27425372"},"html":{"href":"https://github.com/irmen/Pyrolite/pull/22#discussion_r27425372"},"pull_request":{"href":"https://api.github.com/repos/irmen/Pyrolite/pulls/22"}}},"pull_request":{"url":"https://api.github.com/repos/irmen/Pyrolite/pulls/22","id":32071605,"html_url":"https://github.com/irmen/Pyrolite/pull/22","diff_url":"https://github.com/irmen/Pyrolite/pull/22.diff","patch_url":"https://github.com/irmen/Pyrolite/pull/22.patch","issue_url":"https://api.github.com/repos/irmen/Pyrolite/issues/22","number":22,"state":"open","locked":false,"title":"Python Timezones (java only) ","user":{"login":"airhorns","id":158950,"avatar_url":"https://avatars.githubusercontent.com/u/158950?v=3","gravatar_id":"","url":"https://api.github.com/users/airhorns","html_url":"https://github.com/airhorns","followers_url":"https://api.github.com/users/airhorns/followers","following_url":"https://api.github.com/users/airhorns/following{/other_user}","gists_url":"https://api.github.com/users/airhorns/gists{/gist_id}","starred_url":"https://api.github.com/users/airhorns/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airhorns/subscriptions","organizations_url":"https://api.github.com/users/airhorns/orgs","repos_url":"https://api.github.com/users/airhorns/repos","events_url":"https://api.github.com/users/airhorns/events{/privacy}","received_events_url":"https://api.github.com/users/airhorns/received_events","type":"User","site_admin":false},"body":"This adds support for depickling most of the commonly used timezone representations from Python in Java land. \r\n\r\n - Adds a TimeZoneConstructor which converts pickled timezone representations of timezones into java.util.TimeZone objects\r\n - Adds support for the two argument tuple in the DateTimeConstructor, the inner date’s state and the timezone\r\n - Add support for depickling python datetime objects with `dateutil.tz.tzutc` objects as their timezone\r\n\r\nThis is tricky for a couple reasons. For protocol 0 and 1, Python didn’t use `__setstate__` to unpickle objects implemented in C, it used functions from the copy_reg module. So, unlike pytz timezone objects which are implemented in python, the datetime.tzinfo standard library timezone class gets pickled using a “constructor” of `copy_reg._reconstructor` in protocol 0. Further complicating things is that the object that almost everyone uses, `dateutil.tz.tzutc`, is a subclass of this C class. This is pickled as a call to the `_reconstructor` function with three arguments: the subclass or “real” class we are trying to create, the base class which is implemented in C land, and the state that the eventual object we are trying to depickle should have. The reconstructor then allocates a new instance of the base class with `__new__`, but passes the sub class as the class to mark the object as. So the underlying C class does the allocation, but out comes an allocated instance of the fancy sub class. Yeeesh. Then, `_reconstructor` calls the base classes’ `__init__` with the state to actually set the object back up again.\r\n\r\nI opted to not really try to implement this logic cause IMO it’s crazy, and a deprecated protocol. Instead, I short cutted. I implemented a Pyrolite land version of the `_reconstructor` function which checks the “sub class” (given to it as a configured IObjectConstructor of some sort) to see if it knows how to reconstruct an object given the “base class” and “state”, which are from the unpickle stack. It uses reflection to check for a `reconstruct` function on the constructor. I then added an implementation of reconstruct to the TimeZoneConstructor so it can check for the very specific conditions that indicate we’re trying to unpickle a `dateutil.tz.tzutc` object, and just leverage the fact that we know it’s UTC and return a UTC TimeZone there. This is all, which means there is no support for unpickling other objects that are subclasses of C classes, like other things in dateutil, like `dateutil.tz.tzlocal`. This is something we could add in the future but requires making the code even more complex and confusing. I think it can be avoided for now since I am the first person to need this functionality, and I know I don’t need tzlocal support, since I feel as though most people stay away from those objects since they don’t support DST properly, and stick with pytz objects, which were easier to implement in the previous commit.\r\n\r\nFor protocol 2, `__setstate__` is a thing, so I added a Tzinfo object which knows how to be depickled and have it’s state set. The DateTimeConstructor then knows how to be passed a Tzinfo object and ask it for its TimeZone so it can set that on the returned Calendar. Thing is, a `dateutil.tz.tzutc` object doesn’t actually have any state, womp womp. The timezone for it is simply implied by the name of the constructor as far as I can tell. This means that some object somewhere has to know that, so I did something somewhat dirty and make the tzinfo constructor understand when it needs to force itself to UTC. This means that the `__setstate__` is called on the tzinfo object, but with no state, and has no effect, because the object previously learned what java-land TimeZone object it should represent when it was constructed, as that’s the only point in time where we knew the name of the constructor.\r\n\r\nYuck.\r\n\r\n@irmen feel free to have a look whenever it's convienient and please let me know if I can change things around, I have not done much java at all. \r\n\r\n@udnay maybe you could give me a quick code review as well if you have time. \r\n\r\nFixes #19 ","created_at":"2015-03-26T23:29:01Z","updated_at":"2015-03-30T19:53:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"22ed540b7c69be99e31661d2bdcd8892a6d81bf0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/irmen/Pyrolite/pulls/22/commits","review_comments_url":"https://api.github.com/repos/irmen/Pyrolite/pulls/22/comments","review_comment_url":"https://api.github.com/repos/irmen/Pyrolite/pulls/comments{/number}","comments_url":"https://api.github.com/repos/irmen/Pyrolite/issues/22/comments","statuses_url":"https://api.github.com/repos/irmen/Pyrolite/statuses/caf399c16eb846916bf0713ab22dfd109acbc1e9","head":{"label":"airhorns:timezones","ref":"timezones","sha":"caf399c16eb846916bf0713ab22dfd109acbc1e9","user":{"login":"airhorns","id":158950,"avatar_url":"https://avatars.githubusercontent.com/u/158950?v=3","gravatar_id":"","url":"https://api.github.com/users/airhorns","html_url":"https://github.com/airhorns","followers_url":"https://api.github.com/users/airhorns/followers","following_url":"https://api.github.com/users/airhorns/following{/other_user}","gists_url":"https://api.github.com/users/airhorns/gists{/gist_id}","starred_url":"https://api.github.com/users/airhorns/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airhorns/subscriptions","organizations_url":"https://api.github.com/users/airhorns/orgs","repos_url":"https://api.github.com/users/airhorns/repos","events_url":"https://api.github.com/users/airhorns/events{/privacy}","received_events_url":"https://api.github.com/users/airhorns/received_events","type":"User","site_admin":false},"repo":{"id":32898932,"name":"Pyrolite","full_name":"airhorns/Pyrolite","owner":{"login":"airhorns","id":158950,"avatar_url":"https://avatars.githubusercontent.com/u/158950?v=3","gravatar_id":"","url":"https://api.github.com/users/airhorns","html_url":"https://github.com/airhorns","followers_url":"https://api.github.com/users/airhorns/followers","following_url":"https://api.github.com/users/airhorns/following{/other_user}","gists_url":"https://api.github.com/users/airhorns/gists{/gist_id}","starred_url":"https://api.github.com/users/airhorns/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/airhorns/subscriptions","organizations_url":"https://api.github.com/users/airhorns/orgs","repos_url":"https://api.github.com/users/airhorns/repos","events_url":"https://api.github.com/users/airhorns/events{/privacy}","received_events_url":"https://api.github.com/users/airhorns/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/airhorns/Pyrolite","description":"Java and .NET interface to Python's pickle and Pyro protocols","fork":true,"url":"https://api.github.com/repos/airhorns/Pyrolite","forks_url":"https://api.github.com/repos/airhorns/Pyrolite/forks","keys_url":"https://api.github.com/repos/airhorns/Pyrolite/keys{/key_id}","collaborators_url":"https://api.github.com/repos/airhorns/Pyrolite/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/airhorns/Pyrolite/teams","hooks_url":"https://api.github.com/repos/airhorns/Pyrolite/hooks","issue_events_url":"https://api.github.com/repos/airhorns/Pyrolite/issues/events{/number}","events_url":"https://api.github.com/repos/airhorns/Pyrolite/events","assignees_url":"https://api.github.com/repos/airhorns/Pyrolite/assignees{/user}","branches_url":"https://api.github.com/repos/airhorns/Pyrolite/branches{/branch}","tags_url":"https://api.github.com/repos/airhorns/Pyrolite/tags","blobs_url":"https://api.github.com/repos/airhorns/Pyrolite/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/airhorns/Pyrolite/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/airhorns/Pyrolite/git/refs{/sha}","trees_url":"https://api.github.com/repos/airhorns/Pyrolite/git/trees{/sha}","statuses_url":"https://api.github.com/repos/airhorns/Pyrolite/statuses/{sha}","languages_url":"https://api.github.com/repos/airhorns/Pyrolite/languages","stargazers_url":"https://api.github.com/repos/airhorns/Pyrolite/stargazers","contributors_url":"https://api.github.com/repos/airhorns/Pyrolite/contributors","subscribers_url":"https://api.github.com/repos/airhorns/Pyrolite/subscribers","subscription_url":"https://api.github.com/repos/airhorns/Pyrolite/subscription","commits_url":"https://api.github.com/repos/airhorns/Pyrolite/commits{/sha}","git_commits_url":"https://api.github.com/repos/airhorns/Pyrolite/git/commits{/sha}","comments_url":"https://api.github.com/repos/airhorns/Pyrolite/comments{/number}","issue_comment_url":"https://api.github.com/repos/airhorns/Pyrolite/issues/comments{/number}","contents_url":"https://api.github.com/repos/airhorns/Pyrolite/contents/{+path}","compare_url":"https://api.github.com/repos/airhorns/Pyrolite/compare/{base}...{head}","merges_url":"https://api.github.com/repos/airhorns/Pyrolite/merges","archive_url":"https://api.github.com/repos/airhorns/Pyrolite/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/airhorns/Pyrolite/downloads","issues_url":"https://api.github.com/repos/airhorns/Pyrolite/issues{/number}","pulls_url":"https://api.github.com/repos/airhorns/Pyrolite/pulls{/number}","milestones_url":"https://api.github.com/repos/airhorns/Pyrolite/milestones{/number}","notifications_url":"https://api.github.com/repos/airhorns/Pyrolite/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/airhorns/Pyrolite/labels{/name}","releases_url":"https://api.github.com/repos/airhorns/Pyrolite/releases{/id}","created_at":"2015-03-26T00:33:22Z","updated_at":"2015-03-26T00:33:23Z","pushed_at":"2015-03-26T23:23:08Z","git_url":"git://github.com/airhorns/Pyrolite.git","ssh_url":"git@github.com:airhorns/Pyrolite.git","clone_url":"https://github.com/airhorns/Pyrolite.git","svn_url":"https://github.com/airhorns/Pyrolite","homepage":"","size":1426,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"irmen:master","ref":"master","sha":"b5ccb556db57237dfd350b5e4346ed4d5fed6c65","user":{"login":"irmen","id":1771820,"avatar_url":"https://avatars.githubusercontent.com/u/1771820?v=3","gravatar_id":"","url":"https://api.github.com/users/irmen","html_url":"https://github.com/irmen","followers_url":"https://api.github.com/users/irmen/followers","following_url":"https://api.github.com/users/irmen/following{/other_user}","gists_url":"https://api.github.com/users/irmen/gists{/gist_id}","starred_url":"https://api.github.com/users/irmen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/irmen/subscriptions","organizations_url":"https://api.github.com/users/irmen/orgs","repos_url":"https://api.github.com/users/irmen/repos","events_url":"https://api.github.com/users/irmen/events{/privacy}","received_events_url":"https://api.github.com/users/irmen/received_events","type":"User","site_admin":false},"repo":{"id":11037173,"name":"Pyrolite","full_name":"irmen/Pyrolite","owner":{"login":"irmen","id":1771820,"avatar_url":"https://avatars.githubusercontent.com/u/1771820?v=3","gravatar_id":"","url":"https://api.github.com/users/irmen","html_url":"https://github.com/irmen","followers_url":"https://api.github.com/users/irmen/followers","following_url":"https://api.github.com/users/irmen/following{/other_user}","gists_url":"https://api.github.com/users/irmen/gists{/gist_id}","starred_url":"https://api.github.com/users/irmen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/irmen/subscriptions","organizations_url":"https://api.github.com/users/irmen/orgs","repos_url":"https://api.github.com/users/irmen/repos","events_url":"https://api.github.com/users/irmen/events{/privacy}","received_events_url":"https://api.github.com/users/irmen/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/irmen/Pyrolite","description":"Java and .NET interface to Python's pickle and Pyro protocols","fork":false,"url":"https://api.github.com/repos/irmen/Pyrolite","forks_url":"https://api.github.com/repos/irmen/Pyrolite/forks","keys_url":"https://api.github.com/repos/irmen/Pyrolite/keys{/key_id}","collaborators_url":"https://api.github.com/repos/irmen/Pyrolite/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/irmen/Pyrolite/teams","hooks_url":"https://api.github.com/repos/irmen/Pyrolite/hooks","issue_events_url":"https://api.github.com/repos/irmen/Pyrolite/issues/events{/number}","events_url":"https://api.github.com/repos/irmen/Pyrolite/events","assignees_url":"https://api.github.com/repos/irmen/Pyrolite/assignees{/user}","branches_url":"https://api.github.com/repos/irmen/Pyrolite/branches{/branch}","tags_url":"https://api.github.com/repos/irmen/Pyrolite/tags","blobs_url":"https://api.github.com/repos/irmen/Pyrolite/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/irmen/Pyrolite/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/irmen/Pyrolite/git/refs{/sha}","trees_url":"https://api.github.com/repos/irmen/Pyrolite/git/trees{/sha}","statuses_url":"https://api.github.com/repos/irmen/Pyrolite/statuses/{sha}","languages_url":"https://api.github.com/repos/irmen/Pyrolite/languages","stargazers_url":"https://api.github.com/repos/irmen/Pyrolite/stargazers","contributors_url":"https://api.github.com/repos/irmen/Pyrolite/contributors","subscribers_url":"https://api.github.com/repos/irmen/Pyrolite/subscribers","subscription_url":"https://api.github.com/repos/irmen/Pyrolite/subscription","commits_url":"https://api.github.com/repos/irmen/Pyrolite/commits{/sha}","git_commits_url":"https://api.github.com/repos/irmen/Pyrolite/git/commits{/sha}","comments_url":"https://api.github.com/repos/irmen/Pyrolite/comments{/number}","issue_comment_url":"https://api.github.com/repos/irmen/Pyrolite/issues/comments{/number}","contents_url":"https://api.github.com/repos/irmen/Pyrolite/contents/{+path}","compare_url":"https://api.github.com/repos/irmen/Pyrolite/compare/{base}...{head}","merges_url":"https://api.github.com/repos/irmen/Pyrolite/merges","archive_url":"https://api.github.com/repos/irmen/Pyrolite/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/irmen/Pyrolite/downloads","issues_url":"https://api.github.com/repos/irmen/Pyrolite/issues{/number}","pulls_url":"https://api.github.com/repos/irmen/Pyrolite/pulls{/number}","milestones_url":"https://api.github.com/repos/irmen/Pyrolite/milestones{/number}","notifications_url":"https://api.github.com/repos/irmen/Pyrolite/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/irmen/Pyrolite/labels{/name}","releases_url":"https://api.github.com/repos/irmen/Pyrolite/releases{/id}","created_at":"2013-06-28T20:26:32Z","updated_at":"2015-03-26T22:51:22Z","pushed_at":"2015-03-26T23:29:01Z","git_url":"git://github.com/irmen/Pyrolite.git","ssh_url":"git@github.com:irmen/Pyrolite.git","clone_url":"https://github.com/irmen/Pyrolite.git","svn_url":"https://github.com/irmen/Pyrolite","homepage":"","size":1716,"stargazers_count":26,"watchers_count":26,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":12,"mirror_url":null,"open_issues_count":3,"forks":12,"open_issues":3,"watchers":26,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/irmen/Pyrolite/pulls/22"},"html":{"href":"https://github.com/irmen/Pyrolite/pull/22"},"issue":{"href":"https://api.github.com/repos/irmen/Pyrolite/issues/22"},"comments":{"href":"https://api.github.com/repos/irmen/Pyrolite/issues/22/comments"},"review_comments":{"href":"https://api.github.com/repos/irmen/Pyrolite/pulls/22/comments"},"review_comment":{"href":"https://api.github.com/repos/irmen/Pyrolite/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/irmen/Pyrolite/pulls/22/commits"},"statuses":{"href":"https://api.github.com/repos/irmen/Pyrolite/statuses/caf399c16eb846916bf0713ab22dfd109acbc1e9"}}}}