Question Status

Suggested Answer
M Morris asked a question on 9 Feb 2012 6:14 AM

We have several issues with how AX 2009 handles date and time stamping transactions due to the fact the time difference between where our AX application server is located and one goup of users.

The time difference is 9 hours.  During the first couple hours of the users business day the issue we're seeing is the following:

1. they create a sales order which is stamped using the session date and time for that user.

2. If they advance the order to the packing slip stage, we have noticed that the packing slip is using the AOS date and time which in this case is yesterday.

3. in the end it looks like the order was shipped before the order was actually created.

4. This same issues with date and time occurs during our period close process where transactions are not permitted for the first few hours of the users business day because posting date used is the AOS date which is still in the previous period.

As we add addiitional user groups, this issue will only become more apparent.  Are there ways to manage this other than telling the business sorry, you can't run transaction for the first few hours of your business day or having them manually change the dates for two hours every day?

 

 

Reply
Suggested Answer
Kent Carstens responded on 10 Feb 2012 5:43 AM

I would suggest configuring your AX app server to match the date/time of your users.

That would be the best option I believe.  Other then that, you could modify AX to set date and times of your orders to that of your app server but that would be a heavy modification and most likely not recommended.  Look into UTCDateTime functionality.  Every date/time that goes into fields is converted to the users preferred time zones.  So in actuality, the two matchup but you don't see that in the user interface.  Read the below link for some good info:

community.dynamics.com/.../conversion-from-utcdatetime-to-date-or-time.aspx

Also, there is perhaps a way to turn off the conversion but it would still leave a problem where you would see an order created now that looks like it was created 9 hours ago.

Hope this helps somewhat.

Kind regards.

Reply
M Morris responded on 10 Feb 2012 7:23 AM

Thanks, this is actually the solution we were considering.  But we were curious if there might be another possibility before adopting this option.

Reply
Barry Bayliss responded on 14 Feb 2012 3:18 PM

We are experiencing the same issue (currently working on a potential solution, without needing to rewrite large amounts of base code).  So far I have a solution for the systemDateGet() call, still working on solving the timeNow() call issue.   When I have a working solution that does not required large amounts of base code rewrites, I will let you know.    

Kent, setting the AOS time/date settings for one group of users will not work for international businesses with multiple offices using 1 AOS as there will always be an issue with the systemDateGet() and timeNow() calls.  If these were properly based on UTC time then there would be no issue (HINT: Microsoft a solution to the problem that needs to be fixed at the kernel level without major base code rewrites).  

Reply
M Morris responded on 15 Feb 2012 12:59 PM

Barry,

Thanks for ther reply.  I would definitely be interested in ways to address this programmatically and agree...Microsoft needs to address this as it appears that AX 2012 has the same issue.

Reply
Kent Carstens responded on 16 Feb 2012 5:49 AM

Barry,

I agree.  I bet MS would recommend using multiple AOS's however.  I would like to point out that if you are writting custom code and using systemDateGet() or timeNow() calls, you should be using UTCDateTimeUtil instead (in your scenario)

i.e.
public static void testDateTimeConversion()
{
    utcDateTime dateTime;
    date dateInUserTimeZone;
    TimeOfDay timeInUserTimeZone;

    dateTime = DateTimeUtil::utcNow();

    dateInUserTimeZone = DateTimeUtil::date(DateTimeUtil::applyTimeZoneOffset(dateTime, DateTimeUtil::getUserPreferredTimeZone()));
    timeInUserTimeZone = DateTimeUtil::time(DateTimeUtil::applyTimeZoneOffset(dateTime, DateTimeUtil::getUserPreferredTimeZone()));

    dateTime = DateTimeUtil::newDateTime(dateInUserTimeZone, timeInUserTimeZone, DateTimeUtil::getUserPreferredTimeZone());
}

https://community.dynamics.com/product/ax/axtechnical/b/axdaily/archive/2010/02/08/conversion-from-utcdatetime-to-date-or-time.aspx

Reply
Barry Bayliss responded on 16 Feb 2012 5:33 PM

Kent,

Ideally I agree.  There are other factors to take into account.  When I have finished testing, I will post the changes I've had to make.  (This site lost the answer I just wrote with an outline of the changes I've already made).

Hoping to have a complete solution by early next week.

Barry.

Reply
Barry Bayliss responded on 21 Feb 2012 4:26 PM

It seems the site is unable to handle the solution in one reply, so I will break it into parts.

Here is a quick update.  Let me know if there are any issues or situations that need to be addressed.


USING LOCAL TIME

Introduction:
The following is a work in progress update, as the solution has yet to be fully tested in all situations.

Problem:
If the user has a different time zone to the AOS, the timeNow() and systemDateGet() functions are returning the AOS details when the local date time is required. 

Clarifiers:
When the local date time is changed within Axapta, the systemDateGet() function will return the local date, however the timeNow() function still returns the AOS time.

Coding changes:
A number of coding changes have been made to handle a number of different situations.  Comments will be added where an explanation may be required.

The brackets were changed to [ and ] to allow this to be posted.


CLASS:GLOBAL

One requirement I was given was to allow the system to handle multiple sites within a comany that may have different time zones.  Presently this functionality is not required.

static server void setSessionDateTime(
 inventSiteId  inventSiteId = '',
 utcDateTime  reference = dateTimeUtil::utcNow())
[
    str                             sql;
    sqlStatementExecutePermission   perm;
    connection                      conn        = new UserConnection();
    timeZone                        timeZone;
    int                             ret;
    ;

    if (inventSiteId)
    [
        timeZone = inventSite::find(inventSiteId).Timezone;
    ]
    else
    [
        timeZone = dateTimeUtil::getCompanyTimeZone();
    ]

 //This is to get around the kernel validation of changing timezones when the user has more than one session open.

    sql     = strfmt("Update userInfo set preferredTimeZone = %1 where userInfo.id = '%2'", enum2int(timeZone), curUserId());
    perm    = new SQLStatementExecutePermission(sql);
    perm.assert();

    ret = conn.createStatement().executeUpdate(sql);

    dateTimeUtil::setUserPreferredTimeZone(timeZone);
    dateTimeUtil::setSystemDateTime(reference);

    CodeAccessPermission::revertAssert();
]

static int localTime()
[
    utcDateTime tmp;
    ;
    setSessionDateTime();

    tmp = dateTimeUtil::applyTimeZoneOffset( dateTimeUtil::utcNow(), dateTimeUtil::getCompanyTimeZone());
    return dateTimeUtil::time(tmp);
]

The following method was implemented as a cross check to ensure that systemDateGet() returns the expected value.

static date localDate()
[
    utcDateTime tmp;
    ;
    setSessionDateTime();

    tmp = dateTimeUtil::applyTimeZoneOffset( dateTimeUtil::utcNow(), dateTimeUtil::getCompanyTimeZone());
    return dateTimeUtil::date(tmp);
]


CLASS:APPLICATION

Modify the method 'setDefaultCompany'.  Add the line 'setSessionDateTime();' directly after the super call.  This is to allow the time to change when the user changes company (another requirement I was given).


CLASS:INFO

So that the system uses the correct date/time from the start of the session.

void startupPost()
[
    ;
    setSessionDateTime();
]

Modify the method 'canViewAlertInbox()' adding the line 'setSessionDateTime();' as the first line.  This is to handle if the user has multiple forms open for differnet companies.

 

Locationisation dependant changes:

Depending on your service pack and locationisations, you will need to change a function of objects to use the localTime() function, replacing timeNow().  IMPORTANT NOTE: Do not change the class BatchRun to use the new localTime function as this will stop it working correctly.

In our system there were around 260 instances that could be changed.  If you do not use all modules and locationisations the actual number of lines you need to change will be less.

 

Final note:

There are a number of today() calls in the code.  I have not yet gone through each line to ensure it is coded correctly, i.e. using today() instead of systemDateGet(). 


Known issues:

I have come across a situation where the timezone change function did not work competely as expected.  This was when one session was doing a database synchronisation and another session was opened in a different company.  As normal users will never be able to do this, I have not spent much time on its solution at this stage.  It is something I do intend to resolve.

 

 

 

Reply
Barry Bayliss responded on 5 Mar 2012 3:07 PM

Quick update.  We have put the solution into our live environment and I am waiting on feedback for any issues that may occur.

There were a number of objects that needed to be changed that referenced: timeNow() and Today().  It appears that the Today() function has been incorrectly used in places.

Reply
Barry Bayliss responded on 7 Mar 2012 6:03 PM

A problem raised with the solution is that it does not work correctly in batch mode.  Two workaround:

1. Move the setSession code to a new class and set the called on setting to server and change the code as follows

static server void setSessionDateTime(utcDateTime reference = dateTimeUtil::utcNow())

[

   str                             sql;

   sqlStatementExecutePermission   perm;

   connection                      conn        = new UserConnection();

   timeZone                        timeZone;

   int                             ret;

   session                         session = new Session();

   ;

   if (session.clientKind() != clientType::WorkerThread)

   [

       timeZone = dateTimeUtil::getCompanyTimeZone();

       sql     = strfmt("Update userInfo set preferredTimeZone = %1 where userInfo.id = '%2'", enum2int(timeZone), curUserId());

       perm    = new SQLStatementExecutePermission(sql);

       perm.assert();

       ret = conn.createStatement().executeUpdate(sql);

       if (timeZone != dateTimeUtil::getUserPreferredTimeZone())

       [

           dateTimeUtil::setUserPreferredTimeZone(timeZone, true);

           dateTimeUtil::setSystemDateTime(reference);

       ]

       CodeAccessPermission::revertAssert();

   ]

]

Unfortunately this will mean use of incorrect dates.

2. Rewrite system code and replace systemDateGet() with localDate().

This is the option I am implementing.

Reply
James Hesselschwerdt responded on 13 Mar 2013 8:21 AM

Kent,

You mention that MS would recommend multiple AOS to address the time zone issue.

Since the use of Virtual Servers ease the hardware cost component, are there other concerns for the option of setting AOS1 and AOS2 as EST and AOS3 and AOS4 as PST and then directing the users to the AOS that matches their localization?  If all the AOS are pointing to the same SQL, and the Legal Entity has the Time Zone set as EST.

The issue is the west coast Inventory movement transactions that occur after 9:00pm PST / Midnight EST posting to the next day.

Thanks for your (or anyone else) reply on this configuration.

Reply
Barry Bayliss responded on 13 Mar 2013 4:36 PM

James,

We have been using my solution (code modifications) for over a year with no issues.  We operate globally and had previously experienced the issue you raised.  We currently have only 1 AOS.

The only change that was made from above was to use the below to get the UTCDateTime for speed issues.

   System.DateTime utcDate;

   ;

   utcDate = System.DateTime::get_UtcNow();

Have you currently purchased multiple AOS licences?  Let me know if you get the multiple AOS timezone working.

Barry.

Reply
Suggested Answer
Kent Carstens responded on 10 Feb 2012 5:43 AM

I would suggest configuring your AX app server to match the date/time of your users.

That would be the best option I believe.  Other then that, you could modify AX to set date and times of your orders to that of your app server but that would be a heavy modification and most likely not recommended.  Look into UTCDateTime functionality.  Every date/time that goes into fields is converted to the users preferred time zones.  So in actuality, the two matchup but you don't see that in the user interface.  Read the below link for some good info:

community.dynamics.com/.../conversion-from-utcdatetime-to-date-or-time.aspx

Also, there is perhaps a way to turn off the conversion but it would still leave a problem where you would see an order created now that looks like it was created 9 hours ago.

Hope this helps somewhat.

Kind regards.

Reply