File: System\Windows\Markup\DateTimeValueSerializer.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Xaml\System.Xaml.csproj (System.Xaml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xaml;
 
namespace System.Windows.Markup
{
    /// <summary>
    /// This class converts DateTime values to/from string.
    /// We don't use the DateTimeConverter because it doesn't support
    /// custom cultures, and in Xaml we require the converter to
    /// support en-us culture.
    /// </summary>
    [TypeForwardedFrom("WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
    public class DateTimeValueSerializer : ValueSerializer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.ComponentModel.DateTimeConverter"></see> class.
        /// </summary>
        public DateTimeValueSerializer()
        {
        }
 
        /// <summary>
        /// Indicate that we do convert DateTime's from string.
        /// </summary>
        public override bool CanConvertFromString(string? value, IValueSerializerContext? context) => true;
 
        /// <summary>
        /// Indicate that we do convert a DateTime to string.
        /// </summary>
        public override bool CanConvertToString(object? value, IValueSerializerContext? context) => value is DateTime;
 
        /// <summary>
        /// Converts the given value object to a <see cref="T:System.DateTime"></see>.
        /// </summary>
        public override object ConvertFromString(string value, IValueSerializerContext? context)
        {
            if (value == null)
            {
                throw GetConvertFromException(value);
            }
            if (value.Length == 0)
            {
                return DateTime.MinValue;
            }
 
            // Get a DateTimeFormatInfo and set the formatting style for round-tripping
            // and to trim the string.
            DateTimeFormatInfo dateTimeFormatInfo = (DateTimeFormatInfo)TypeConverterHelper.InvariantEnglishUS.GetFormat(typeof(DateTimeFormatInfo))!;
            const DateTimeStyles DateTimeStyles = DateTimeStyles.RoundtripKind
                      | DateTimeStyles.NoCurrentDateDefault
                      | DateTimeStyles.AllowLeadingWhite
                      | DateTimeStyles.AllowTrailingWhite;
            return DateTime.Parse(value, dateTimeFormatInfo, DateTimeStyles);
        }
 
 
        /// <summary>
        /// Converts the given value object to a <see cref="T:System.DateTime"></see> using the arguments.
        /// </summary>
        public override string ConvertToString(object? value, IValueSerializerContext? context)
        {
            if (value == null || !(value is DateTime dateTime))
            {
                throw GetConvertToException(value, typeof(string));
            }
 
            // Build up the format string to be used in DateTime.ToString()
            var formatString = new StringBuilder("yyyy-MM-dd");
            if (dateTime.TimeOfDay == TimeSpan.Zero)
            {
                // The time portion of this DateTime is exactly at midnight.
                // We don't include the time component if the Kind is unspecified.
                // Otherwise, we're going to be including the time zone info, so'll
                // we'll have to include the time.
                if (dateTime.Kind != DateTimeKind.Unspecified)
                {
                    formatString.Append("'T'HH':'mm");
                }
            }
            else
            {
                long digitsAfterSecond = dateTime.Ticks % 10000000;
                int second = dateTime.Second;
                // We're going to write out at least the hours/minutes
                formatString.Append("'T'HH':'mm");
                if (second != 0 || digitsAfterSecond != 0)
                {
                    // need to write out seconds
                    formatString.Append("':'ss");
                    if (digitsAfterSecond != 0)
                    {
                        // need to write out digits after seconds
                        formatString.Append("'.'FFFFFFF");
                    }
                }
            }
 
            // Add the format specifier that indicates we want the DateTimeKind to be
            // included in the output formulation -- UTC gets written out with a "Z",
            // and Local gets written out with e.g. "-08:00" for Pacific Standard Time.
            formatString.Append('K');
 
            // We've finally got our format string built, we can create the string.
            return dateTime.ToString(formatString.ToString(), TypeConverterHelper.InvariantEnglishUS);
        }
    }
}