Handling Timezones with REST web services

One challenge we’ve been dealing with when switching to a RESTful module of interacting with OpenMRS is problems when the server and the client aren’t in in the same time zone. There doesn’t seem to be any utilities built into AngularJS or Javascript to handle this.

For an example of related thread and issue in the appt scheduling module, see:

Generally, I think the behavior we want is to be always using the server time zone. For instance, if an encounter is stored on the server with a datetime of 2016-01-22 23:00, that would be the time we want to display to the end user, regardless of the time zone setting on the client side.

To handle this, we have an angular serverDate filter (found in the uicommons module) that simply drops the timezone component from a datetime. This solves the “view” component of the problem, but not the “submit” version.

For instance, in PIH Core, we have a date widget that allows editing visit start date and stop date. Before populating the widget with a time received from the server, we strip off the time zone component using the serverDate filter, so the correct date is displayed in the widget. However, when we go to submit a change, the timezone from the client is added to the date, and then server can misinterpret the date.

To fix this, I’ve created a counterpart filter for trimming off the timezone before submitting dates called serverDateForRESTSubmit, which makes sure that the date is in the proper ISO format with the timestamp stripped off. This seems to work–the server appears to interpret the date as being in it’s timezone if there is no specified timestamp.

Does this make sense to people? Can anyone think of something I’m overlooking?

I generally agree with this, if we’re talking about a human user and a browser, and the reference application. But I could imagine some multi-tenant or PHR-driven use case where we might want different behavior.

If the server receives a datetime-without-timezone, it should definitely interpret this in the server’s timezone.

Ideally we should have angular widgets for date +/- time that will correctly generate a valid ISO date+time+timezone. (Someday this can actually even become configurable to let the user choose whether they’re choosing based on the server or client timezone).

But I can see why you might prefer to do a quicker hack to just strip timezones before submitting. And I guess this should work.

Would this be a failure scenario?

Assume today is Jan 1 0900 hrs at the client, Dec 31 2100 hrs at server. We create a visit specifying a start date of today (Jan 1 0900 hrs) The counterpart filter strips off time to send Jan 1 0000 hrs. New visit created in database for Jan 1 0000 hrs.

We create an encounter without specifying a date. (Not sure how reference application does this) Server tries to create an encounter starting now (Dec 31 2100 hrs). ERROR: “The encounter datetime should be between the visit start and stop dates.”

Side Note When we deployed Bahmni in a hospital in Nepal, we had set the server timezone to IST (India Standard Time) by mistake (provisioning scripts defaulted timezone to Kolkata/Asia, which is 15 minutes behind of the Nepal standard Time). The application worked smoothly though, and showed the right times on all screens.

The server stored everything in IST, and all http responses were provided in UTC. At the client, which was configured in NST (Nepal Standard Time), the UTC time was parsed and shown based on their locale. This automatic switching of timezones happened by accident (we did not specifically code for it), and was a pleasant surprise to us.

I agree this is not a use case we need to be thinking about, just wanted to share the experience.

Actually this is exactly the behavior that Mark wants. i.e. if the user chooses “Jan 1 0900 hrs” in the UI this means “Jan 1 0900 hrs AT THE HOSPITAL”, not “Jan 1 0900 hrs MY COMPUTER’S TIME ZONE”.

I agree with Mark that the more common scenario would be that a user’s machine is in another timezone (either because their machine is configured wrong, or because they are actually elsewhere) but they are “thinking in the hospital’s timezone”.

Thanks guys, a few points:

Yes, I think in 95% of the cases we want to be “thinking in the hospital/facilities timezone”. One way to do this is to make sure that the server operates in that time zone, and then the client machine (somehow) has the logic to operate in that timezone, regardless of the time zone of the client machine itself.

@darius thinking through your situation about creating an angular widget that properly handles this, this seems non-trivial to get right, and I was trying to think of the proper way to do this. Any thoughts? It’s like you’d want to keep the backing object in the timezone it received from the server, but still render the display or date picker widget ignoring that time zone.

For example Currently, I get an ISO date string from the server (ie visit.startDatetime) and strip off the time zone, but then when I assign it as a backing object to the date widget it becomes a Date (and therefore, incorrectly, picks up the timezone of the client), so that’s why I need to strip off the time zone again before submitting to the server. Hmm, maybe it would’t be too hard to encapsulate that in a new directive… (sorry, kind of thinking out loud… :slight_smile: ).

It doesn’t feel right that a “solution” for doing better with timezones is to ignore them. This approach seems like it will create an increasingly fragile design around implied timezones.

Wouldn’t it be better to try to increasingly add timezones and then make the server and client work appropriately with them? For example, a header warning users when their timezone differs from the server, widgets that can be configured to use the server’s timezone, etc.

If we need a serverDateForRESTSubmit, I’d rather it add the server’s timezone to explicitly change the timestamps rather than strip timezones and hope the ambiguity is properly interpreted.

I would think that the choice of the time zone depends on the situation at hand, if you have all clients in the same time zone with the server in a separate time zone, then i would assume you need to set the server to use the timezone as that of the clients. If there are multiple clients in different time zones it gets trickier, i would think then you need the widgets and view renderers to be time zone sensitive

The time in the server is already known, and in the client it can be too:

If the REST WS allows dates with time zone it’s a matter of deciding which one to use (can be configurable).

Burke is right that what you’re proposing here is hacky and not the ideal solution. That said, I believe what you’ve done could work in your use case, and that your use case is the most common one. (So I’m not actually suggesting that you have to change anything at this point. When I tried to actually code this the right way it was surprisingly difficult and I gave up.)

The practical problem with your proposal is that your angular model will then include some things that came from the server via REST (and therefore are correctly moved from the server’s timezone to the client’s timezone when becoming javascript Date objects) and things the user entered via the UI (which are incorrectly in the client’s timezone, and you want to strip the timezone from). So you’d need to make sure you only ever apply your serverDateForRESTSubmit filter to a user-entered date and not one you got from REST.

Wyclif is also correct that this would ideally be an admin-configurable setting (e.g. whether the client UI should be in the server’s timezone, or in the client machine’s timezone).

Ideally we would have a date+time picker widget that (depending on configuration that can be in a GP or hardcoded by the dev) may presume you’re picking a date+time in the server’s timezone and hence convert this to the correct client timezone before putting the javascript Date object in the ng-model. (I would try to use moment-timezone for this.)

Someday someone needs to write a date+time picker widget for the Reference Application. I posted about this here, but never created a ticket (and I guess this should be updated to mention timezones):

Probably @pascal’s openmrs js would better address this!

1 Like

Yes, the key as Darius pointed out is that doing things the “right way” is surprisingly difficult, once you start thinking about the complexities.

The fundamental problem, I think, is that generally you want the client to do the “wrong” thing. That is, if the hospital/facility is in, say EST, you want to view and manipulate any dates/times related to that facility as in EST. You, in essence, want to “keep” the dates in EST… and out of the box javascript will fight you tooth and nail by trying to convert the date into the local time zone. If the server time zone equals the facility time zone, then you can try to achieve this the way I did–by stripping off the time zone component. But this will fail if the server time zone is not equal to the facility time zone. Taking a step further, if you ever have a single server for multiple sites in different time zones, you simply can’t have this.

Imagine this summary page:

Appointments scheduled for today:

Boston Clinic Tom Jones - 10:30am [edit time]

Kigali Clinic Mary Beth - 11:45am [edit time]

Obviously, this is a complex use case–viewing info from two facilities in different time zones on the same page, but worth thinking about when designing a solution.

@burke; interestingly, in the appointment scheduling module, we did take the header warning approach as a quick fix, and it was logged as an issue… see the responses on this thread…

I know that neither the simple header nor the stripping of time zones is ideal, but I thought that dropping the timezone might be an improvement, but maybe that is not the consensus opinion. :slightly_smiling:

Mark

It might be okay to have an administrative setting to define the default behavior when server & client are using different timezones (i.e., “default to server timezone” vs. “default to client timezone” when one isn’t chosen explicitly); however, ultimately the decision of which timezone (client or server) should be used within the UI is up to the specific UI. Defaulting to server timezone might make sense in most cases, but not in all. For example, locally collected observations may be better collected using the client’s timezone.

Ultimately, our clients should know both server & client timezone, which is the “default” timezone to use, and our shared time-based widgets should support explicit timezone configuration (i.e., “get time & date using [client|server|UTC-3|default] timezone,” where “UTC-3” implies the widget can be configured to a specific timezone and “default” is… wait for it… the default).

While we may need some short-term workarounds, I think we should be striving toward being explicit. Stripping timezones creates ambiguity & depends on implicit business rules. If we get it “wrong”, at least with explicit behaviors & choices (using timezones), it’s clear what needs to get fixed. If we are stripping timezones and expecting things to work out implicitly, I doubt we’ll ever reach a longterm solution to these issues.

Generally agree that being explicit is better, but I do worry about usability issues as well.

For example, if we’ve got a doctor from, say, Los Angeles, sitting at a clinic in Haiti and trying to schedule an appointment at that clinic for a patient on his laptop (from LA, set to Pacific Standard Time), I imagine them complaining if the software prompts them to tell them what time zone they want to make the appointment in, because “shouldn’t it be obvious it’s the local time”. :slightly_smiling:

Agreed. I wasn’t suggesting we ask the user. A time widget that asks the user for which timezone to use would be a horrible UX. The widget should decide. I’d just rather be in the “you’re right, those appointments should be using server-side timezone so let’s configure that screen appropriately” camp instead of the “we agree, but we’re not quite sure why the timezoneless timestamps are acting that way for you and not others. we’ll add another assumption to our code and hope the stack of cards doesn’t come apart.” camp. :slightly_smiling:

I wouldn’t phrase it this way. Rather, the right thing is:

  • The client should store valid JavaScript dates (which include a timezone)
  • The 80% use case is for the the client to both display date/times and to have a date+/-time picker widget, that works in the server timezone.
  • even in this case any date variable you capture should be a valid JavaScript date (including a timezone)
  • and you should submit full date+time+timezones in any REST POST

(The quicker/hackier solution of stripping the timezone on submit will only work in the case where you know that you’re only stripping+submitting values captured on the client, and never reposting a value that originally came from the server.)

Hi @darius, sorry to revive the thread. I am having a problem here where I need to save an obs via the REST resource. The web service wants me to provide a datetime value. But it seems that the datetime I provide is not quite understood by the REST resource because never it really takes my timezone into account.

In the previous message, you say:

Q: What is the exact format of the datetime to be sent in order for the REST resource to “understand” the timezone?


See an example below: (client is on UTC, server UTC+1)

When I am trying to save a new obs with a datetime of "2016-11-23 20:42:52 +00:00"

the resulting obs_datetime saved on the server (UTC+1) is “2016-11-23 20:42:52”.

And when I later retrieve this obs, the obsDatetime value returned is “2016-11-23T20:42:52.000+0100”. See the +0100?

which is not quite right. :frowning:

Is there any particular format tu use to send dates? Or should I be using a specific JS object type? I’ve tried JS Date and Moment.js with no success.

(if needed, the code is here: https://github.com/mekomsolutions/openmrs-module-lfhcforms/blob/dev/omod/src/main/webapp/resources/scripts/patientHeaderNote/directives/clickToEditObs.js#L122)

For the record, I have found that the format to send a date to the REST service should be (milliseconds are optional):

2016-11-23T20:42:52.000+0100

With a Moment.js date/time, you can use the following format string:

obsDatetime = moment().format("YYYY-MM-DD[T]HH:mm:ss.SSSZZ"); // This is the format to send a timezoned date to the REST service (ex: "2016-12-25T19:02.102+0700")

(See the new commit https://github.com/mekomsolutions/openmrs-module-lfhcforms/blob/143f3241073a395f710b8cb34074cd3b5df5406b/omod/src/main/webapp/resources/scripts/patientHeaderNote/directives/clickToEditObs.js#L122)

@mksrom, I’m glad you were able to figure this out (because it was a long holiday weekend for me…)

Do you mind adding this detail somewhere relevant on this wiki page? https://wiki.openmrs.org/display/docs/REST+Web+Services+API+For+Clients

@darius, I have now updated the documentation with a Date/time with Time Zones section. I allowed myself to use your advice to strongly recommend to send date + time + timezone in any REST POST:

In the new doc article:

… It is strongly recommended to always submit the full date + time + timezone in any REST POST query. …

One thing that worries me is that maybe the server should return an error when the time zone format provided is not correct, instead of saving the obs at the local time. The developer doesn’t even notice that times are in fact not correctly saved because most of the time he works on an local OpenMRS server, on the same time zone.

What do you think ?

@mksrom, there are valid use cases where you want the server to use it’s own timezone. So I wouldn’t want to completely prevent submitting date+time without timezone…

(Or, did you mean something different about “return an error when the time zone format provided is not correct”?)