Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#8728: Upgrade/datetimefield format #8729

Open
wants to merge 4 commits into
base: 1.10.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
}

@if (Model.ShowDate) { <text>@Html.ValidationMessageFor(m => m.Date)</text> }
@if (Model.ShowTime) { <text>@Html.ValidationMessageFor(m => m.Time)</text> }
@if (Model.ShowTime) { <text>@Html.ValidationMessageFor(m => m.Time)</text> }
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,27 @@ protected override DriverResult Display(ContentPart part, DateTimeField field, s
var options = new DateLocalizationOptions();

// Don't do any time zone conversion if field is semantically a date-only field, because that might mutate the date component.
if (settings.Display == DateTimeFieldDisplays.DateOnly) {
if (field.Display == DateTimeFieldDisplays.DateOnly) {
options.EnableTimeZoneConversion = false;
}

// Don't do any calendar conversion if field is semantically a time-only field, because the date component might we out of allowed boundaries for the current calendar.
if (settings.Display == DateTimeFieldDisplays.TimeOnly) {
if (field.Display == DateTimeFieldDisplays.TimeOnly) {
options.EnableCalendarConversion = false;
options.IgnoreDate = true;
}

var showDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly;
var showTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly;
var showDate = field.Display == DateTimeFieldDisplays.DateAndTime || field.Display == DateTimeFieldDisplays.DateOnly;
var showTime = field.Display == DateTimeFieldDisplays.DateAndTime || field.Display == DateTimeFieldDisplays.TimeOnly;


DateTimeFieldDisplays displayOption = field.Display;
var viewModel = new DateTimeFieldViewModel {
Name = field.DisplayName,
Hint = settings.Hint,
IsRequired = settings.Required,
AllowDisplayOptionsOverride = settings.AllowDisplayOptionsOverride,
DisplayOption = displayOption,
Editor = new DateTimeEditor() {
Date = showDate ? DateLocalizationServices.ConvertToLocalizedDateString(value, options) : null,
Time = showTime ? DateLocalizationServices.ConvertToLocalizedTimeString(value, options) : null,
Expand All @@ -78,30 +82,38 @@ protected override DriverResult Editor(ContentPart part, DateTimeField field, dy
var value = part.IsNew() && field.DateTime == default(DateTime) ? settings.DefaultValue : field.DateTime;
var options = new DateLocalizationOptions();

// If the field's AllowDisplayOptionsOverride setting is set to true both the Date and Time editors will be shown anyway.
var showDate = true;
var showTime = true;

// Don't do any time zone conversion if field is semantically a date-only field, because that might mutate the date component.
if (settings.Display == DateTimeFieldDisplays.DateOnly) {
if (field.Display == DateTimeFieldDisplays.DateOnly) {
options.EnableTimeZoneConversion = false;
}

// Don't do any calendar conversion if field is semantically a time-only field, because the date component might we out of allowed boundaries for the current calendar.
if (settings.Display == DateTimeFieldDisplays.TimeOnly) {
if (field.Display == DateTimeFieldDisplays.TimeOnly) {
options.EnableCalendarConversion = false;
options.IgnoreDate = true;
}

var showDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly;
var showTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly;

// If the field's AllowDisplayOptionsOverride setting is set to false the Date and Time editors are shown based on the Display setting.
if (!settings.AllowDisplayOptionsOverride) {
showDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly;
showTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly;
}

var viewModel = new DateTimeFieldViewModel {
Name = field.DisplayName,
Hint = settings.Hint,
IsRequired = settings.Required,
AllowDisplayOptionsOverride = settings.AllowDisplayOptionsOverride,
DisplayOption = field.Display,
Editor = new DateTimeEditor() {
Date = showDate ? DateLocalizationServices.ConvertToLocalizedDateString(value, options) : null,
Time = showTime ? DateLocalizationServices.ConvertToLocalizedTimeString(value, options) : null,
ShowDate = showDate,
ShowTime = showTime,
}
}
};

return ContentShape("Fields_DateTime_Edit", GetDifferentiator(field, part),
Expand All @@ -117,19 +129,28 @@ protected override DriverResult Editor(ContentPart part, DateTimeField field, IU

var options = new DateLocalizationOptions();

// If the field's AllowDisplayOptionsOverride setting is set to true both the Date and Time editors will be shown anyway
var showDate = true;
var showTime = true;

field.Display = viewModel.DisplayOption;

// Don't do any time zone conversion if field is semantically a date-only field, because that might mutate the date component.
if (settings.Display == DateTimeFieldDisplays.DateOnly) {
if (field.Display == DateTimeFieldDisplays.DateOnly) {
options.EnableTimeZoneConversion = false;
}

// Don't do any calendar conversion if field is semantically a time-only field, because the date component might we out of allowed boundaries for the current calendar.
if (settings.Display == DateTimeFieldDisplays.TimeOnly) {
if (field.Display == DateTimeFieldDisplays.TimeOnly) {
options.EnableCalendarConversion = false;
options.IgnoreDate = true;
}

var showDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly;
var showTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly;
// If the field's AllowDisplayOptionsOverride setting is set to false the Date and Time editors are shown based on the Display setting.
if (!settings.AllowDisplayOptionsOverride) {
showDate = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.DateOnly;
showTime = settings.Display == DateTimeFieldDisplays.DateAndTime || settings.Display == DateTimeFieldDisplays.TimeOnly;
}

DateTime? value = null;

Expand All @@ -146,11 +167,11 @@ protected override DriverResult Editor(ContentPart part, DateTimeField field, IU
// Hackish workaround to make sure a time-only field with an entered time equivalent to
// 00:00 UTC doesn't get stored as a full DateTime.MinValue in the database, resulting
// in it being interpreted as an empty value when subsequently retrieved.
if (value.HasValue && settings.Display == DateTimeFieldDisplays.TimeOnly && value == DateTime.MinValue) {
if (value.HasValue && field.Display == DateTimeFieldDisplays.TimeOnly && value == DateTime.MinValue) {
value = value.Value.AddDays(1);
}

if (settings.Required && (!value.HasValue || (settings.Display != DateTimeFieldDisplays.TimeOnly && value.Value.Date == DateTime.MinValue))) {
if (settings.Required && (!value.HasValue || (field.Display != DateTimeFieldDisplays.TimeOnly && value.Value.Date == DateTime.MinValue))) {
updater.AddModelError(GetPrefix(field, part), T("{0} is required.", T(field.DisplayName)));
}

Expand All @@ -171,7 +192,8 @@ protected override void Exporting(ContentPart part, DateTimeField field, ExportC
protected override void Describe(DescribeMembersContext context) {
context
.Member(null, typeof(DateTime), T("Value"), T("The date and time value of the field."))
.Enumerate<DateTimeField>(() => field => new[] { field.DateTime });
.Enumerate<DateTimeField>(() => field => new[] { field.DateTime });
}
}
}

23 changes: 21 additions & 2 deletions src/Orchard.Web/Modules/Orchard.Fields/Fields/DateTimeField.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using Orchard.ContentManagement;
using Orchard.ContentManagement.FieldStorage;
using Orchard.Fields.Settings;
using Orchard.UI.Notify;

namespace Orchard.Fields.Fields {
public class DateTimeField : ContentField {
Expand All @@ -10,15 +12,15 @@ public DateTime DateTime {
get {
var settings = this.PartFieldDefinition.Settings.GetModel<DateTimeFieldSettings>();
var value = Storage.Get<DateTime>();
if (settings.Display == DateTimeFieldDisplays.DateOnly) {
if (Display == DateTimeFieldDisplays.DateOnly) {
return new DateTime(value.Year, value.Month, value.Day);
}
return value;
}

set {
var settings = this.PartFieldDefinition.Settings.GetModel<DateTimeFieldSettings>();
if (settings.Display == DateTimeFieldDisplays.DateOnly) {
if (Display == DateTimeFieldDisplays.DateOnly) {
Storage.Set(new DateTime(value.Year, value.Month, value.Day));
}
else {
Expand All @@ -30,8 +32,25 @@ public DateTime DateTime {
public DateTimeFieldDisplays Display {
get {
var settings = this.PartFieldDefinition.Settings.GetModel<DateTimeFieldSettings>();
if (settings.AllowDisplayOptionsOverride) {
if (Enum.TryParse(DisplayOption, out DateTimeFieldDisplays displayOption)){
return displayOption;
}
}
return settings.Display;
}
set {
DisplayOption = value.ToString();
}
}

protected string DisplayOption {
get {
return Storage.Get<string>("DisplayOption");
}
set {
Storage.Set("DisplayOption", value ?? String.Empty);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public override IEnumerable<TemplateViewModel> PartFieldEditorUpdate(ContentPart
builder.WithSetting("DateTimeFieldSettings.Required", model.Required.ToString(CultureInfo.InvariantCulture));
model.DefaultValue = model.Editor == null ? model.DefaultValue : _dateLocalizationServices.ConvertFromLocalizedString(model.Editor.Date, model.Editor.Time);
builder.WithSetting("DateTimeFieldSettings.DefaultValue", model.DefaultValue.HasValue ? model.DefaultValue.Value.ToString(CultureInfo.InvariantCulture) : String.Empty);
builder.WithSetting("DateTimeFieldSettings.AllowDisplayOptionsOverride", model.AllowDisplayOptionsOverride.ToString(CultureInfo.InvariantCulture));

yield return DefinitionTemplate(model);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public class DateTimeFieldSettings {
public bool Required { get; set; }
public DateTime? DefaultValue { get; set; }
public DateTimeEditor Editor { get; set; }
public bool AllowDisplayOptionsOverride { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using Orchard.Core.Common.ViewModels;
using Orchard.Fields.Settings;


namespace Orchard.Fields.ViewModels {
public class DateTimeFieldViewModel {
public string Name { get; set; }
public string Hint { get; set; }
public bool IsRequired { get; set; }
public DateTimeEditor Editor { get; set; }
public bool AllowDisplayOptionsOverride { get; set; }
public DateTimeFieldDisplays DisplayOption { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
@using Orchard.Fields.Settings;

<fieldset>
<label for="@Html.FieldIdFor(m => m.Display)" class="forcheckbox">@T("Display options")</label>
<label for="@Html.FieldIdFor(m => m.Display)" class="forcheckbox">@T("Display options")</label>
<select id="@Html.FieldIdFor(m => m.Display)" name="@Html.FieldNameFor(m => m.Display)">
@Html.SelectOption(DateTimeFieldDisplays.DateAndTime, Model.Display == DateTimeFieldDisplays.DateAndTime, T("Date and time").ToString())
@Html.SelectOption(DateTimeFieldDisplays.DateOnly, Model.Display == DateTimeFieldDisplays.DateOnly, T("Date only").ToString())
@Html.SelectOption(DateTimeFieldDisplays.TimeOnly, Model.Display == DateTimeFieldDisplays.TimeOnly, T("Time only").ToString())
</select>

</select>
<div>
@Html.CheckBoxFor(m => m.AllowDisplayOptionsOverride) <label for="@Html.FieldIdFor(m => m.AllowDisplayOptionsOverride)" class="forcheckbox">@T("Allow override")</label>
<span class="hint">@T("Check to allow users to specify the Display options value in each content.")</span>
</div>
@Html.ValidationMessageFor(m => m.Display)
</fieldset>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
@model Orchard.Fields.ViewModels.DateTimeFieldViewModel
@using Orchard.Fields.Settings;


<fieldset>
<label for="@Html.FieldIdFor(m => Model.Editor.Date)" @if (Model.IsRequired) { <text> class="required" </text> }>@Model.Name</label>
@Html.EditorFor(m => m.Editor)
@if (Model.AllowDisplayOptionsOverride) {
<select id="@Html.FieldIdFor(m => m.DisplayOption)" name="@Html.FieldNameFor(m => m.DisplayOption)">
@Html.SelectOption(DateTimeFieldDisplays.DateAndTime, Model.DisplayOption == DateTimeFieldDisplays.DateAndTime, T("Date and time").ToString())
@Html.SelectOption(DateTimeFieldDisplays.DateOnly, Model.DisplayOption == DateTimeFieldDisplays.DateOnly, T("Date only").ToString())
@Html.SelectOption(DateTimeFieldDisplays.TimeOnly, Model.DisplayOption == DateTimeFieldDisplays.TimeOnly, T("Time only").ToString())
</select>
}

@if (HasText(Model.Hint)) {
<span class="hint">@Model.Hint</span>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ public string ConvertToLocalizedTimeString(DateTime? date, DateLocalizationOptio
var parts = DateTimeParts.FromDateTime(dateValue, offset);

// INFO: No calendar conversion in this method - we expect the date component to be DateTime.MinValue and irrelevant anyway.

return _dateFormatter.FormatDateTime(parts, _dateTimeFormatProvider.LongTimeFormat);
}

Expand Down Expand Up @@ -210,13 +209,28 @@ public string ConvertToLocalizedString(DateTime? date, string format, DateLocali
// conversion cannot wrap DateTime.MinValue around to the previous day, resulting in
// an undefined result. Instead we convert the date to a hard-coded date of 2000-01-01
// before the conversion, and back to the original date after.
if (!hasDate) {

// If the DateTime that's being converted has a date but we only need to consider its Time (ignoreDate = true)
// it's necessary to perform the trickery explained before.
// In the same situation in some cases, the Date value will change when converting from local time to UTC
// (e.g. local time is UTC+2 and the time is set to 1 AM).
// To prevent this we save the original date value and, in those cases, use it to make sure the DateTime value is the same after the conversion.
var backupDate = new DateTime(dateValue.Year, dateValue.Month, dateValue.Day);
if (!hasDate || options.IgnoreDate) {
dateValue = new DateTime(2000, 1, 1, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
}

dateValue = ConvertFromSiteTimeZone(dateValue);

if (!hasDate) {
dateValue = new DateTime(DateTime.MinValue.Year, DateTime.MinValue.Month, DateTime.MinValue.Day, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
}
else if (options.IgnoreDate) {
// If we have to ignore the date during the conversions we use the hard-coded date of 2000-01-01 but we still adjust the backupDate value
// based on the result of the conversion and use its newly calculated value as the date.
backupDate = backupDate.AddDays((dateValue.Date - new DateTime(2000, 1, 1)).Days);
dateValue = new DateTime(backupDate.Year, backupDate.Month, backupDate.Day, dateValue.Hour, dateValue.Minute, dateValue.Second, dateValue.Millisecond, dateValue.Kind);
}
}

if (options.EnableTimeZoneConversion)
Expand Down Expand Up @@ -276,4 +290,4 @@ protected virtual TimeZoneInfo CurrentTimeZone {
return date == DateTime.MinValue ? new DateTime?() : new DateTime?(date);
}
}
}
}