views:

1044

answers:

2

Hi, I'm having headache with Time values and multiple time zones. I'm storing new DateTime values in UTC time, but I face problem when I try to modify them using LinqDataSource and GridView.

I can easily show correct time in

<ItemTemplate>
    <asp:Label ID="Label1" runat="server" Text='<%# TimeManager.ToLocalTime((DateTime)Eval("OrderDate")) %>' />
</ItemTemplate>

Currently this will add 1 hour to UTC time stored in DB.

However, binding back to source is not easy. Bind("OrderDate") cannot be modified like TimeManager.ToGlobalTime((DateTime)Bind("OrderDate")).

I was thinking of using OnUpdating event of LinqDataSource to update value to global time, but what if user modified other fields and not date field? Every time he updates record time value would be smaller for one hour.

Comparing old and new values also is not great because user can modify date portion of datetime and not time, which is influenced by time zones?

If I had way to show local time in gridview's all states then I could easily use OnUpdating of LinqDataSource.

Please share your thoughts...

+1  A: 

The way I've handled this sort of thing in the past is to use Eval in the EditItemTemplate as well. Let the user edit the item in local time. Then add OnItemUpdating handler for the gridview and add extract the value of the associated text box, convert it to global time, and add that to the new values dictionary. Bind the original value in (in global time) to a hidden field in the same template, which will populate the old values dictionary with the correct old time. You'll want to do the same thing on insertion in OnItemInserting although you obviously don't need the old value (since there isn't one).

EDIT: Usually I do my updates on a DetailsView not a GridView, thus the ItemUpdating/Inserting instead of RowUpdating/Inserting. Sample code below -- this example uses a pair of dropdown lists that allows the user to specific a location (choose a building and a location in the building, but it actually only maps the location in the database). On the back end it assigns initial values to the dropdowns in OnPreRender (not shown) and extracts the LocationID database field value from the location dropdown on ItemUpdating/Inserting (updating shown). The DetailsView is wrapped in an UpdatePanel and the population of the Location dropdown is done when the building dropdown selection changes. Note that since I'm updating the item (causing an update statement anyway) I don't care if the LocationID field gets overwritten on the update with the same value so I don't bother to keep the old value on the page.

<asp:TemplateField HeaderText="Location:" SortExpression="LocationId">
    <EditItemTemplate>
        <asp:DropDownList runat="server" ID="buildingDropDownList"
                          DataSourceID="buildingDataSource"
                          DataTextField="name"
                          DataValueField="abbreviation"
                          OnSelectedIndexChanged=
                             "buildingDropDownList_SelectedIndexChanged"
                          AutoPostBack="true" />
        <asp:DropDownList runat="server" ID="locationDropDownList"
                          DataSourceID="locationsDataSource"
                          DataTextField="Name"
                          DataValueField="ID">
        </asp:DropDownList>
    </EditItemTemplate>
    <InsertItemTemplate>
        <asp:DropDownList runat="server" ID="buildingDropDownList"
                          DataSourceID="buildingDataSource"
                          DataTextField="name"
                          DataValueField="abbreviation"
                          OnSelectedIndexChanged=
                              "buildingDropDownList_SelectedIndexChanged"
                          AutoPostBack="true"
                          AppendDataBoundItems="true">
            <asp:ListItem Text="Select Building" Value="" />
        </asp:DropDownList>
        <asp:DropDownList runat="server" ID="locationDropDownList"
                          DataSourceID="locationsDataSource"
                          DataTextField="Name"
                          DataValueField="ID"
                          AppendDataBoundItems="true">
            <asp:ListItem Text="Not installed" Value="" />
        </asp:DropDownList>
    </InsertItemTemplate>
    <ItemTemplate>
        <asp:Label ID="locationLabel" runat="server"\
                    Text='<%# Eval("LocationID") == null
                                  ? ""
                                  : Eval("Location.Name") %>'>
        </asp:Label>
    </ItemTemplate>
</asp:TemplateField>

Codebehind:

void editPrinterDetailsView_ItemUpdating( object sender,
                                          DetailsViewUpdateEventArgs e )
{
    // Use a helper method to find the dropdown inside the details view
    // and get the selected value.
    string locationID = ControlHelper
                          .GetDropDownValue( editPrinterDetailsView,
                                             "locationDropDownList" );
    if (locationID == string.Empty)
    {
        locationID = null;
    }
    if (e.NewValues.Contains( "LocationID" ))
    {
        e.NewValues["LocationID"] = locationID;
    }
    else
    {
        e.NewValues.Add( "LocationID", locationID );
    }
    e.OldValues["LocationID"] = -1;
}
tvanfosson
Can you share some code? By ItemUpdating you mean RowUpdating?
Milan
I was thinking a DetailsView and ItemUpdating. I guess I don't usually do my updates from the GridView directly, usually via a drilldown to a DetailsView, but the idea is the same. I'll add a sample to my post.
tvanfosson
+2  A: 

Have you considered making the change to your model? Let's assume that the name of the object you are binding to is CustomerOrder. You could add the following class to your project (in the same namespace as your Linq objects):

public partial class CustomerOrder
{
    public DateTime LocalOrderDate
    {
        get { return TimeManager.ToLocalTime(OrderDate); }
        set { OrderDate = TimeManager.ToUTCTime(value); }
    }
}

Now instead of binding to OrderDate, bind to LocalOrderDate. This will automatically make the UTC / Local Time conversion to the OrderDate.

(Note: I'm assuming you have TimeManager.ToLocalTime() and TimeManager. ToUTCTime() properly defined)

Keltex