Пример #1
0
        public override void WriteTo(TextWriter writer, TextEncoder encoder, CultureInfo cultureInfo)
        {
            AssertWriteToParameters(writer, encoder, cultureInfo);

            // Output the value in the General Short Date/Long Time format.
            var outputDate = LavaDateTime.ToString(_value, "G", cultureInfo);

            writer.Write(outputDate);
        }
Пример #2
0
        /// <summary>
        /// Process the specified input template and verify the output against an expected DateTime result.
        /// </summary>
        /// <param name="expectedDateTime"></param>
        /// <param name="inputTemplate"></param>
        /// <param name="maximumDelta"></param>
        public void AssertTemplateOutputDate(ILavaEngine engine, DateTime?expectedDateTime, string inputTemplate, TimeSpan?maximumDelta = null)
        {
            AssertDateIsUtc(expectedDateTime);

            var outputString = GetTemplateOutput(engine, inputTemplate);

            var outputDateUtc = LavaDateTime.ParseToUtc(outputString, null);

            WriteOutputToDebug(engine, outputString);

            Assert.That.IsNotNull(outputDateUtc, $"Template Output does not represent a valid DateTime. [Output=\"{ outputString }\"]");

            if (maximumDelta != null)
            {
                DateTimeAssert.AreEqual(expectedDateTime, outputDateUtc, maximumDelta.Value);
            }
            else
            {
                DateTimeAssert.AreEqual(expectedDateTime, outputDateUtc);
            }
        }
        /// <summary>
        /// Returns the difference between two date/time values, measured in the specified interval.
        /// </summary>
        /// <param name="startDate">The start date.</param>
        /// <param name="endDate">The end date.</param>
        /// <param name="interval">The interval in which the difference is measured.</param>
        /// <returns></returns>
        public static Int64?DateDiff(object startDate, object endDate, string interval)
        {
            var startDto = GetDateTimeOffsetFromInputParameter(startDate, null);
            var endDto   = GetDateTimeOffsetFromInputParameter(endDate, null);

            if (startDto != null && endDto != null)
            {
                var difference = endDto.Value - startDto.Value;

                switch (interval)
                {
                case "d":
                    return(( Int64 )difference.TotalDays);

                case "h":
                    return(( Int64 )difference.TotalHours);

                case "m":
                    return(( Int64 )difference.TotalMinutes);

                case "M":
                    return(( Int64 )GetMonthsBetween(startDto.Value, endDto.Value));

                case "Y":
                    // Return the difference between the dates as the number of whole years.
                    var years = LavaDateTime.ConvertToRockDateTime(endDto.Value).TotalYears(LavaDateTime.ConvertToRockDateTime(startDto.Value));
                    return(years);

                case "s":
                    return(( Int64 )difference.TotalSeconds);

                default:
                    return(null);
                }
            }
            else
            {
                return(null);
            }
        }
Пример #4
0
        public void EventScheduledInstanceCommand_ForSampleDataKnownEvents_ReturnsExpectedEventData()
        {
            var input = @"
{% eventscheduledinstance eventid:'Customs & Classics Car Show' startdate:'2018-4-1' maxoccurrences:1 %}
    {% for item in EventScheduledInstances %}
        Name={{ item.Name }}<br>
        Date={{item.Date }}<br>
        Time={{ item.Time }}<br>
        DateTime={{ item.DateTime | Date:'yyyy-MM-ddTHH:mm:sszzz' }}
    {% endfor %}
{% endeventscheduledinstance %}
";

            var rockTimeOffset = LavaDateTime.ConvertToRockDateTime(new DateTime(2021, 9, 1, 0, 0, 0, DateTimeKind.Unspecified)).ToString("zzz");

            var expectedOutput = @"
Name=Customs & Classics Car Show<br>Date=16/04/2018<br>Time=9:24 AM<br>DateTime=2018-04-16T09:24:13<offset>
";

            expectedOutput = expectedOutput.Replace("<offset>", rockTimeOffset);

            TestHelper.AssertTemplateOutput(expectedOutput, input);
        }
        public void CalendarEventsCommand_ForSampleDataKnownEvents_ReturnsExpectedEventData()
        {
            var input          = @"
{% calendarevents calendarid:'Internal' startdate:'2021-1-1' maxoccurrences:2 %}
    {% for item in EventScheduledInstances %}
        Name={{ item.Name }}<br>
        Date={{item.Date }}<br>
        Time={{ item.Time }}<br>
        DateTime={{ item.DateTime | Date:'yyyy-MM-ddTHH:mm:sszzz' }}
        <hr>
    {% endfor %}
{% endcalendarevents %}
";
            var rockTimeOffset = LavaDateTime.ConvertToRockDateTime(new DateTime(2021, 1, 1, 0, 0, 0, DateTimeKind.Unspecified)).ToString("zzz");

            var expectedOutput = @"
Name=Rock Solid Finances Class<br>Date=2/01/2021<br>Time=4:30 PM<br>DateTime=2021-01-02T16:30:00<offset><hr>
Name=Rock Solid Finances Class<br>Date=3/01/2021<br>Time=12:00 PM<br>DateTime=2021-01-03T12:00:00<offset><hr>
";

            expectedOutput = expectedOutput.Replace("<offset>", rockTimeOffset);

            TestHelper.AssertTemplateOutput(expectedOutput, input);
        }
        /// <summary>
        /// Converts an input value to a DateTimeOffset.
        /// </summary>
        /// <param name="input">The date.</param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        private static DateTimeOffset?GetDateTimeOffsetFromInputParameter(object input, DateTimeOffset?defaultValue)
        {
            if (input is String inputString)
            {
                if (inputString.Trim().ToLower() == "now")
                {
                    return(LavaDateTime.NowOffset);
                }
                else
                {
                    return(LavaDateTime.ParseToOffset(inputString, defaultValue));
                }
            }
            else if (input is DateTime dt)
            {
                return(LavaDateTime.ConvertToDateTimeOffset(dt));
            }
            else if (input is DateTimeOffset inputDateTimeOffset)
            {
                return(inputDateTimeOffset);
            }

            return(defaultValue);
        }
Пример #7
0
        /// <summary>
        /// Process the specified input template and verify the output against an expected DateTime result.
        /// </summary>
        /// <param name="expectedDateTime"></param>
        /// <param name="inputTemplate"></param>
        /// <param name="maximumDelta"></param>
        public void AssertTemplateOutputDate(ILavaEngine engine, DateTimeOffset?expectedDateTime, string inputTemplate, TimeSpan?maximumDelta = null)
        {
            var outputString = GetTemplateOutput(engine, inputTemplate);

            var outputDateUtc = LavaDateTime.ParseToOffset(outputString, null);

            WriteOutputToDebug(engine, outputString);

            Assert.That.IsNotNull(outputDateUtc, $"Template Output does not represent a valid DateTime. [Output=\"{ outputString }\"]");

            try
            {
                if (maximumDelta != null)
                {
                    DateTimeAssert.AreEqual(expectedDateTime, outputDateUtc, maximumDelta.Value);
                }
                else
                {
                    DateTimeAssert.AreEqual(expectedDateTime, outputDateUtc);
                }
            }
            catch (Exception ex)
            {
                var info = $@"
Test Environment:
LavaEngine = { engine.EngineName },
LocalDateTime = {DateTimeOffset.Now},
LocalTimeZoneName = {TimeZoneInfo.Local.DisplayName},
LocalTimeZoneOffset = { TimeZoneInfo.Local.BaseUtcOffset }
RockDateTime = { LavaDateTime.NowOffset },
RockTimeZoneName = { RockDateTime.OrgTimeZoneInfo.DisplayName },
RockTimeZoneOffset = { RockDateTime.OrgTimeZoneInfo.BaseUtcOffset }
";
                throw new Exception($"Lava Date/Time test failed.\n{ info }", ex);
            }
        }
Пример #8
0
        /// <summary>
        /// Register value converters for Types that are not natively handled by Fluid.
        /// </summary>
        private void RegisterValueConverters()
        {
            var templateOptions = GetTemplateOptions();

            /* [2021-06-24] DL
             * Value Converters can have a significant impact on rendering performance.
             * Wherever possible, a conversion function should:
             * 1. Process all conversions related to a specific Type domain, to avoid the need to execute similar code in multiple converters.
             * 2. Order from most to least frequently executed and least to most expensive execution time.
             * 3. Return a FluidValue as quickly as possible, to avoid executing subsequent value converters in the collection.
             */

            // TODO: Fluid executes value converters prior to internal converters.
            // Performance test this implementation with short-circuiting for basic types.
            templateOptions.ValueConverters.Add((value) =>
            {
                // If the value is an Enum, render the value name.
                if (value is Enum e)
                {
                    return(new LavaEnumValue(e));
                }

                // This converter cannot process the value.
                return(null);
            });

            // Substitute the default Fluid DateTimeValue with an implementation that renders in the General DateTime format
            // rather than in UTC format.
            templateOptions.ValueConverters.Add((value) =>
            {
                if (value is DateTime dt)
                {
                    // Convert the DateTime to a DateTimeOffset value.
                    // MinValue/MaxValue are substituted directly because they are not timezone dependent.
                    if (dt == DateTime.MinValue)
                    {
                        return(new LavaDateTimeValue(DateTimeOffset.MinValue));
                    }
                    else if (dt == DateTime.MaxValue)
                    {
                        return(new LavaDateTimeValue(DateTimeOffset.MaxValue));
                    }
                    else
                    {
                        // Assume that the DateTime is expressed in Rock time.
                        return(new LavaDateTimeValue(LavaDateTime.NewDateTimeOffset(dt.Ticks)));
                    }
                }
                else if (value is DateTimeOffset dto)
                {
                    return(new LavaDateTimeValue(dto));
                }

                // This converter cannot process the value.
                return(null);
            });

            // DBNull is required to process results from the Sql command.
            // If this type is not registered, Fluid throws some seemingly unrelated exceptions.
            templateOptions.ValueConverters.Add((value) => value is System.DBNull ? FluidValue.Create(null, templateOptions) : null);

            // Converter for a Dictionary with a non-string key type; wraps the dictionary in a proxy so it can be accessed
            // with a key that is not a string type.
            // If this converter is not registered, any attempt to access a non-standard dictionary by key returns a null value.
            templateOptions.ValueConverters.Add((value) =>
            {
                // If the value is not a dictionary, this converter is not applicable.
                if (!(value is IDictionary))
                {
                    return(value);
                }

                // If the value is a standard Liquid-compatible dictionary,
                // return the appropriate Fluid wrapper to short-circuit further conversion attempts.
                if (value is IDictionary <string, object> liquidDictionary)
                {
                    return(new DictionaryValue(new ObjectDictionaryFluidIndexable <object>(liquidDictionary, _templateOptions)));
                }

                var valueType = value.GetType();

                // If the value is derived from LavaDataObject, no conversion is needed.
                if (typeof(LavaDataObject).IsAssignableFrom(valueType))
                {
                    return(value);
                }

                // If this is a generic dictionary with a string key type, no conversion is needed.
                if (valueType.IsGenericType &&
                    valueType.GetGenericTypeDefinition() == typeof(Dictionary <,>))
                {
                    var keyType = valueType.GetGenericArguments()[0];

                    if (keyType == typeof(string))
                    {
                        return(value);
                    }
                }

                // Wrap the dictionary in a proxy so it can be accessed with a key that is not a string type.
                return(new LavaDataObject(value));
            });
        }
        /* [2021-07-31] DL
         *
         * Lava Date filters may return DateTime, DateTimeOffset, or string values according to their purpose.
         * Where possible, a filter should return a DateTime value specified in UTC, or a DateTimeOffset.
         * Local DateTime values may give unexpected results if the Rock timezone setting is different from the server timezone.
         * Where a date string is accepted as an input parameter, the Rock timezone is implied unless a timezone is specified.
         */

        /// <summary>
        /// Formats a date using a .NET date format string
        /// </summary>
        /// <param name="input"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static string Date(object input, string format = null)
        {
            if (input == null)
            {
                return(null);
            }

            string output;

            // If input is "now", use the current Rock date/time as the input value.
            if (input.ToString().ToLower() == "now")
            {
                // To correctly include the Rock configured timezone, we need to use a DateTimeOffset.
                // The DateTime object can only represent local server time or UTC time.
                input = LavaDateTime.NowOffset;
            }

            // Use the General Short Date/Long Time format by default.
            if (string.IsNullOrWhiteSpace(format))
            {
                format = "G";
            }
            // Consider special 'Standard Date' and 'Standard Time' formats.
            else if (format == "sd")
            {
                format = "d";
            }
            else if (format == "st")
            {
                format = "t";
            }
            // If the format string is a single character, add a space to produce a valid custom format string.
            // (refer http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx#UsingSingleSpecifiers)
            else if (format.Length == 1)
            {
                format = " " + format;
            }

            if (input is DateTimeOffset inputDateTimeOffset)
            {
                // Preserve the value of the specified offset and return the formatted datetime value.
                output = inputDateTimeOffset.ToString(format).Trim();
            }
            else
            {
                // Convert the input to a valid Rock DateTimeOffset if possible.
                DateTimeOffset?inputDateTime;

                if (input is DateTime dt)
                {
                    inputDateTime = LavaDateTime.ConvertToRockOffset(dt);
                }
                else
                {
                    inputDateTime = LavaDateTime.ParseToOffset(input.ToString(), null);
                }

                if (!inputDateTime.HasValue)
                {
                    // Not a valid date, so return the input unformatted.
                    output = input.ToString().Trim();
                }
                else
                {
                    output = LavaDateTime.ToString(inputDateTime.Value, format).Trim();
                }
            }
            return(output);
        }
Пример #10
0
 /// <summary>
 /// Process the specified input template and verify the output against an expected DateTime result.
 /// </summary>
 /// <param name="expectedOutput"></param>
 /// <param name="inputTemplate"></param>
 /// <param name="maximumDelta"></param>
 public void AssertTemplateOutputDate(string expectedRockDateTimeOutput, string inputTemplate, TimeSpan?maximumDelta = null)
 {
     AssertTemplateOutputDate(LavaDateTime.ParseToOffset(expectedRockDateTimeOutput), inputTemplate, maximumDelta);
 }