diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/MomentSharp.Tests/MomentSharp.Tests.csproj b/MomentSharp.Tests/MomentSharp.Tests.csproj new file mode 100644 index 0000000..42b0b6e --- /dev/null +++ b/MomentSharp.Tests/MomentSharp.Tests.csproj @@ -0,0 +1,93 @@ + + + + Debug + AnyCPU + {0BCFE0D9-F655-4D1B-A8F5-9D4C3732DB10} + Library + Properties + MomentSharp.Tests + MomentSharp.Tests + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {7df82747-df0d-4969-91ff-30d5f8feb7fd} + MomentSharp + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/MomentSharp.Tests/Parse.cs b/MomentSharp.Tests/Parse.cs new file mode 100644 index 0000000..1ffd2ef --- /dev/null +++ b/MomentSharp.Tests/Parse.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MomentSharp.Tests +{ + [TestClass] + public class Parse + { + [TestMethod] + public void String() + { + var expected = new DateTime(1995, 12, 25); + var actual = new Moment(true) { Year = 1995, Month = 12, Day = 25 }.DateTime(); + + Assert.AreEqual(expected, actual, "Not the same"); + } + } +} diff --git a/MomentSharp.Tests/Properties/AssemblyInfo.cs b/MomentSharp.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..41249fb --- /dev/null +++ b/MomentSharp.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MomemntSharp.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MomemntSharp.Tests")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("53461c32-285c-49af-ae14-1ba52e20758d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MomentSharp.sln b/MomentSharp.sln new file mode 100644 index 0000000..2c4ad77 --- /dev/null +++ b/MomentSharp.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MomentSharp", "MomentSharp\MomentSharp.csproj", "{7DF82747-DF0D-4969-91FF-30D5F8FEB7FD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MomentSharp.Tests", "MomentSharp.Tests\MomentSharp.Tests.csproj", "{0BCFE0D9-F655-4D1B-A8F5-9D4C3732DB10}" +EndProject +Global + GlobalSection(TeamFoundationVersionControl) = preSolution + SccNumberOfProjects = 3 + SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} + SccTeamFoundationServer = https://paladincloudware.visualstudio.com/defaultcollection + SccProjectUniqueName0 = MomentSharp\\MomentSharp.csproj + SccProjectName0 = MomentSharp + SccLocalPath0 = MomentSharp + SccLocalPath1 = . + SccProjectUniqueName2 = MomentSharp.Tests\\MomentSharp.Tests.csproj + SccProjectName2 = MomentSharp.Tests + SccLocalPath2 = MomentSharp.Tests + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DF82747-DF0D-4969-91FF-30D5F8FEB7FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DF82747-DF0D-4969-91FF-30D5F8FEB7FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DF82747-DF0D-4969-91FF-30D5F8FEB7FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DF82747-DF0D-4969-91FF-30D5F8FEB7FD}.Release|Any CPU.Build.0 = Release|Any CPU + {0BCFE0D9-F655-4D1B-A8F5-9D4C3732DB10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BCFE0D9-F655-4D1B-A8F5-9D4C3732DB10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BCFE0D9-F655-4D1B-A8F5-9D4C3732DB10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0BCFE0D9-F655-4D1B-A8F5-9D4C3732DB10}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/MomentSharp/Annotations.cs b/MomentSharp/Annotations.cs new file mode 100644 index 0000000..28c7281 --- /dev/null +++ b/MomentSharp/Annotations.cs @@ -0,0 +1,933 @@ +using System; +using System.Diagnostics; + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace JetBrains.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage + /// + /// + /// [CanBeNull] public object Test() { return null; } + /// public void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null + /// + /// + /// [NotNull] public object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Indicates that collection or enumerable value does not contain null elements + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Indicates that collection or enumerable value can contain null elements + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form + /// + /// + /// [StringFormatMethod("message")] + /// public void ShowError(string message, params object[] args) { /* do something */ } + /// public void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Delegate)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute(string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute(string name) + { + Name = name; + } + + [NotNull] + public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of + /// + /// + /// public void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// private string _name; + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute(string parameterName) + { + ParameterName = parameterName; + } + + public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) + /// for method output means that the methos doesn't return normally.
+ /// canbenull annotation is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, + /// or use single attribute with rows separated by semicolon.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + public string Contract { get; private set; } + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// public class Foo { + /// private string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// class UsesNoEquality { + /// public void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// public class ComponentAttribute : Attribute { } + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// public class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] + public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly + /// (e.g. via reflection, in external library), so this symbol + /// will not be marked as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.All)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute( + ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper + /// to not mark symbols marked with such attributes as unused + /// (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute( + ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] + public ImplicitUseKindFlags UseKindFlags { get; private set; } + [UsedImplicitly] + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used + Access = 1, + /// Indicates implicit assignment to a member + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used + Members = 2, + /// Entity marked with attribute and all its members considered used + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used + /// + [MeansImplicitUse] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled + /// when the invoked method is on stack. If the parameter is a delegate, + /// indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated + /// while the method is executed + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute + /// + /// + /// [Pure] private int Multiply(int x, int y) { return x * y; } + /// public void Foo() { + /// const int a = 2, b = 2; + /// Multiply(a, b); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + public PathReferenceAttribute([PathReference] string basePath) + { + BasePath = basePath; + } + + public string BasePath { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String) + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + public AspMvcActionAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcAreaAttribute : PathReferenceAttribute + { + public AspMvcAreaAttribute() { } + public AspMvcAreaAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String) + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + public AspMvcControllerAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String) + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcPartialViewAttribute : PathReferenceAttribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcSupressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String) + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object) + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcViewAttribute : PathReferenceAttribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + public HtmlElementAttributesAttribute(string name) + { + Name = name; + } + + public string Name { get; private set; } + } + + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] + public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String) + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method invocation affects content of the collection + /// + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection + None = 0, + /// Method only reads content of the collection but does not modify it + Read = 1, + /// Method can change content of the collection but does not add new elements + ModifyExistingContent = 2, + /// Method can add new elements to the collection + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute + /// + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true + IS_TRUE = 0, + /// Marked parameter should be evaluated to false + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be + /// treated as ItemsControl-derived type, to enable inner items DataContext + /// type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attibute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute(string tagName, Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + public string TagName { get; private set; } + public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute(string name) + { + Name = name; + } + + public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute(string type, string fieldName) + { + Type = type; + FieldName = fieldName; + } + + public string Type { get; private set; } + public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns. + /// + [AttributeUsage(AttributeTargets.All)] + [Conditional("JETBRAINS_ANNOTATIONS")] + public sealed class NoReorder : Attribute { } +} \ No newline at end of file diff --git a/MomentSharp/Bubble.cs b/MomentSharp/Bubble.cs new file mode 100644 index 0000000..6ca4810 --- /dev/null +++ b/MomentSharp/Bubble.cs @@ -0,0 +1,60 @@ +namespace MomentSharp +{ + class Bubble + { + internal static void Millisecond(ref Moment moment) + { + while (moment.Millisecond >= 1000) + { + moment.Millisecond = moment.Millisecond - 1000; + moment.Second++; + } + } + + internal static void Second(ref Moment moment) + { + while (moment.Second >= 60) + { + moment.Second = moment.Second - 60; + moment.Minute++; + } + } + + internal static void Minute(ref Moment moment) + { + while (moment.Minute >= 60) + { + moment.Minute = moment.Minute - 60; + moment.Hour++; + } + } + + internal static void Hour(ref Moment moment) + { + while (moment.Hour >= 24) + { + moment.Hour = moment.Hour - 24; + moment.Hour++; + } + } + + internal static void Day(ref Moment moment) + { + var daysThisMonth = moment.DateTime().DaysInMonth(); + while (moment.Day >= daysThisMonth) + { + moment.Day = moment.Day - daysThisMonth; + moment.Month++; + } + } + + internal static void Month(ref Moment moment) + { + while (moment.Month >= 12) + { + moment.Month = moment.Month - 12; + moment.Year++; + } + } + } +} diff --git a/MomentSharp/DateTimeParts.cs b/MomentSharp/DateTimeParts.cs new file mode 100644 index 0000000..0d093e7 --- /dev/null +++ b/MomentSharp/DateTimeParts.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MomentSharp +{ + public enum DateTimeParts + { + Year, + Month, + Quarter, + Day, + Hour, + Minute, + Second, + Millisecond, + Week, + /// + /// Do NOT use in your code. This is meant to be used in default parameters + /// + None + } +} diff --git a/MomentSharp/Display.cs b/MomentSharp/Display.cs new file mode 100644 index 0000000..dd4e9d8 --- /dev/null +++ b/MomentSharp/Display.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MomentSharp.Globalization; + +namespace MomentSharp +{ + public static class Display + { + /// + /// moment([2007, 0, 29]).fromNow(); // 4 years ago + /// moment([2007, 0, 29]).fromNow(showSuffix: false); // 4 years + /// Emulates http://momentjs.com/docs/#/displaying/fromnow/ + /// + /// + /// Uses DateTime.UtcNow if not provided + /// Shows "number part ago" or "in number part" + /// + public static string From(this Moment moment, DateTime dateTime = default(DateTime), bool showSuffix = true) + { + var local = moment.Language; + if (dateTime == default(DateTime)) dateTime = DateTime.UtcNow; + + var timeSpan = (moment.DateTime() - dateTime); + var isFuture = timeSpan.TotalSeconds > 0; + + if (timeSpan.TotalSeconds.InRange(0, 45)) // 0-45 seconds + { + return local.Translate(RelativeTime.Seconds, timeSpan.Seconds, showSuffix, isFuture); + } + if (timeSpan.TotalSeconds.InRange(46, 90)) // 46-90 seconds + { + return local.Translate(RelativeTime.Minute, timeSpan.Minutes, showSuffix, isFuture); + } + if (timeSpan.TotalSeconds.InRange(91, 60 * 45)) //91 seconds to 45 minutes + { + return local.Translate(RelativeTime.Minutes, timeSpan.Minutes, showSuffix, isFuture); + } + if (timeSpan.TotalMinutes.InRange(46, 90)) //46 minutes to 90 minutes + { + return local.Translate(RelativeTime.Hour, timeSpan.Hours, showSuffix, isFuture); + } + if (timeSpan.TotalMinutes.InRange(91, 60 * 22)) //91 minutes to 22 hours + { + return local.Translate(RelativeTime.Hours, timeSpan.Hours, showSuffix, isFuture); + } + if (timeSpan.TotalHours.InRange(23, 36)) //23-36 hours + { + return local.Translate(RelativeTime.Day, timeSpan.Days, showSuffix, isFuture); + } + if (timeSpan.TotalHours.InRange(37, 24*25)) //37 hours to 25 days + { + return local.Translate(RelativeTime.Days, timeSpan.Days, showSuffix, isFuture); + } + if (timeSpan.TotalDays.InRange(26, 45)) //26-45 days + { + return local.Translate(RelativeTime.Month, 1, showSuffix, isFuture); + } + if (timeSpan.TotalDays.InRange(46, 345)) // 46-345 days + { + return local.Translate(RelativeTime.Months, (int)Math.Abs(timeSpan.Days / 30.4), showSuffix, isFuture); + } + if (timeSpan.TotalDays.InRange(346, 547)) //346-547 days (1.5 years) + { + return local.Translate(RelativeTime.Year, 1, showSuffix, isFuture); + } + if (timeSpan.TotalDays.InRange(548, 7305)) + { + return local.Translate(RelativeTime.Years, Math.Abs(timeSpan.Days / 365), showSuffix, isFuture); + } + throw new Exception("Couldn't find an exceptable range to return"); + } + + /// + /// Will format a date with different strings depending on how close to referenceTime's date (today by default) the date is. + /// Emulates: http://momentjs.com/docs/#/displaying/calendar-time/ + /// + /// + /// Uses DateTime.UtcNow if not provided + /// + public static string Calendar(this Moment moment, DateTime dateTime = default(DateTime)) + { + var local = moment.Language; + if (dateTime == default(DateTime)) dateTime = DateTime.Now; + + var timeSpan = (moment.DateTime() - dateTime).TotalDays; + + if (timeSpan < -6) return local.Translate(Globalization.Calendar.SameElse, moment.DateTime()); + if (timeSpan < -1) return local.Translate(Globalization.Calendar.LastWeek, dateTime); + if (timeSpan < 0) return local.Translate(Globalization.Calendar.LastDay, dateTime); + if (timeSpan < 1) return local.Translate(Globalization.Calendar.SameDay, dateTime); + if (timeSpan < 2) return local.Translate(Globalization.Calendar.NextDay, dateTime); + if (timeSpan < 7) return local.Translate(Globalization.Calendar.NextWeek, dateTime); + return local.Translate(Globalization.Calendar.SameElse, moment.DateTime()); + } + + /// + /// Will format a date with different strings depending on how close to referenceTime's date (today by default) the date is. + /// Emulates: http://momentjs.com/docs/#/displaying/calendar-time/ + /// + /// + /// Uses DateTime.UtcNow if not provided + /// + public static string Calendar(this Moment moment, Moment compareMoment) + { + return moment.Calendar(compareMoment.DateTime()); + } + + /// + /// Returns the number of seconds since the Unix Epoch + /// + /// + /// Defaults to UTC if not provided. For valid parameters see TimeZoneInfo.GetSystemTimeZones() + /// + public static int UnixTimeStamp(this DateTime dateTime, string timeZoneId = "UTC") + { + dateTime.ToUTC(timeZoneId); + var unixTimestamp = (Int32)(dateTime.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; + return unixTimestamp; + } + + /// + /// Get the number of days in this . + /// + /// + /// + public static int DaysInMonth(this DateTime dateTime) + { + return DateTime.DaysInMonth(dateTime.Year, dateTime.Month); + } + + /// + /// Get the number of days in this . + /// + /// + /// + public static int DaysInMonth(this Moment moment) + { + return moment.DateTime().DaysInMonth(); + } + + internal static bool InRange(this double numberToCheck, int bottom, int top) + { + numberToCheck = Math.Abs(numberToCheck); + return (numberToCheck > bottom && numberToCheck < top); + } + } +} \ No newline at end of file diff --git a/MomentSharp/Globalization/Calendar.cs b/MomentSharp/Globalization/Calendar.cs new file mode 100644 index 0000000..363e4c0 --- /dev/null +++ b/MomentSharp/Globalization/Calendar.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MomentSharp.Globalization +{ + /// + /// Calendar parts. + /// Meant to emulate http://momentjs.com/docs/#/displaying/calendar-time/ + /// + public enum Calendar + { + SameDay, + NextDay, + NextWeek, + LastDay, + LastWeek, + SameElse + } +} diff --git a/MomentSharp/Globalization/ILocalize.cs b/MomentSharp/Globalization/ILocalize.cs new file mode 100644 index 0000000..9719382 --- /dev/null +++ b/MomentSharp/Globalization/ILocalize.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace MomentSharp.Globalization +{ + /// + /// Base class for implementing language translations. + /// + public interface ILocalize + { + LongDateFormat LongDateFormat { get;} + + string Translate(Calendar calendar, DateTime dateTime); + + string Translate(RelativeTime relativeTime, int number, bool showSuffix, bool isFuture); + } + + /// + /// Extra for formats from Momentjs. Some of these are may already exist in DateTime.ToString(*) + /// + public class LongDateFormat + { + public string Lt { get; set; } + public string Lts { get; set; } + public string L { get; set; } + public string Ll { get; set; } + public string Lll { get; set; } + public string Llll { get; set; } + } +} diff --git a/MomentSharp/Globalization/Languages/De.cs b/MomentSharp/Globalization/Languages/De.cs new file mode 100644 index 0000000..630472b --- /dev/null +++ b/MomentSharp/Globalization/Languages/De.cs @@ -0,0 +1,90 @@ +using System; + +namespace MomentSharp.Globalization.Languages +{ + public class De : ILocalize + { + private readonly LongDateFormat _longDateFormat; + + public De() + { + _longDateFormat = new LongDateFormat + { + Lt = "HH:mm", + Lts = "HH:mm:ss", + L = "dd.MM.yyyy", + Ll = "d. MMMM yyyy" + }; + + _longDateFormat.Lll = String.Format("d. MMMM yyyy {0}", _longDateFormat.Lt); + _longDateFormat.Llll = String.Format("dddd, d. MMMM yyyy {0}", _longDateFormat.Lt); + } + + public LongDateFormat LongDateFormat + { + get { return _longDateFormat; } + } + + public string Translate(Calendar calendar, DateTime dateTime) + { + switch (calendar) + { + case Calendar.SameDay: + return String.Format("Heute um {0}", dateTime.ToString(LongDateFormat.Lt)); + case Calendar.NextDay: + return String.Format("Morgen um {0}", dateTime.ToString(LongDateFormat.Lt)); + case Calendar.NextWeek: + return String.Format("{0} um {1} Uhr", dateTime.ToString("dddd"), dateTime.ToString(LongDateFormat.Lt)); + case Calendar.LastDay: + return String.Format("Gestern um {0} Uhr", dateTime.ToString(LongDateFormat.Lt)); + case Calendar.LastWeek: + return String.Format("letzten {0} um {1} Uhr", dateTime.ToString("dddd"), dateTime.ToString(LongDateFormat.Lt)); + case Calendar.SameElse: + return dateTime.ToString(LongDateFormat.L); + } + return ""; + } + + public string Translate(RelativeTime relativeTime, int number, bool showSuffix, bool isFuture) + { + var results = String.Empty; + switch (relativeTime) + { + case RelativeTime.Seconds: + results = "ein paar Sekunden"; + break; + case RelativeTime.Minute: + results = showSuffix ? "einer Minute" : "eine Minute"; + break; + case RelativeTime.Minutes: + results = String.Format("{0} Minuten", number); + break; + case RelativeTime.Hour: + results = showSuffix ? "einer Stunde" : "eine Stunde"; + break; + case RelativeTime.Hours: + results = String.Format("{0} Stunden", number); + break; + case RelativeTime.Day: + results = showSuffix ? "einem Tag" : "ein Tag"; + break; + case RelativeTime.Days: + results = String.Format("{0} {1}", number, showSuffix ? "Tagen" : "Tage"); + break; + case RelativeTime.Month: + results = showSuffix ? "einem Monat" : "ein Monat"; + break; + case RelativeTime.Months: + results = String.Format("{0} {1}", number, showSuffix ? "Monaten" : "Monate"); + break; + case RelativeTime.Year: + results = showSuffix ? "einem Jahr" : "ein Jahr"; + break; + case RelativeTime.Years: + results = String.Format("{0} {1}", number, showSuffix ? "Jahren" : "Jahre"); + break; + } + return !showSuffix ? results : String.Format(isFuture ? "in {0}" : "vor {0}", results); + } + } +} diff --git a/MomentSharp/Globalization/Languages/EnUs.cs b/MomentSharp/Globalization/Languages/EnUs.cs new file mode 100644 index 0000000..269a555 --- /dev/null +++ b/MomentSharp/Globalization/Languages/EnUs.cs @@ -0,0 +1,90 @@ +using System; + +namespace MomentSharp.Globalization.Languages +{ + public class EnUs : ILocalize + { + private readonly LongDateFormat _longDateFormat; + + public EnUs() + { + _longDateFormat = new LongDateFormat + { + Lt = "h:mm tt", + Lts = "h:mm:s tt", + L = "M/d/yyyy", + Ll = "MMMM d, yyyy" + }; + + _longDateFormat.Lll = String.Format("MMMM d, yyyy {0}", _longDateFormat.Lt); + _longDateFormat.Llll = String.Format("dddd, MMMM d, yyyy {0}", _longDateFormat.Lt); + } + + public LongDateFormat LongDateFormat + { + get { return _longDateFormat; } + } + + public string Translate(Calendar calendar, DateTime dateTime) + { + switch (calendar) + { + case Calendar.SameDay: + return String.Format("Today at {0}", dateTime.ToString(LongDateFormat.Lt)); + case Calendar.NextDay: + return String.Format("Tomorrow at {0}", dateTime.ToString(LongDateFormat.Lt)); + case Calendar.NextWeek: + return String.Format("{0} at {1}", dateTime.ToString("dddd"), dateTime.ToString(LongDateFormat.Lt)); + case Calendar.LastDay: + return String.Format("Yesterday at {0}", dateTime.ToString(LongDateFormat.Lt)); + case Calendar.LastWeek: + return String.Format("{0} at {1}", dateTime.ToString("dddd"), dateTime.ToString(LongDateFormat.Lt)); + case Calendar.SameElse: + return dateTime.ToString(LongDateFormat.L); + } + return ""; + } + + public string Translate(RelativeTime relativeTime, int number, bool showSuffix, bool isFuture) + { + var results = String.Empty; + switch (relativeTime) + { + case RelativeTime.Seconds: + results = "a few seconds"; + break; + case RelativeTime.Minute: + results = "a minute"; + break; + case RelativeTime.Minutes: + results = String.Format("{0} minutes", number); + break; + case RelativeTime.Hour: + results = "an hour"; + break; + case RelativeTime.Hours: + results = String.Format("{0} hours", number); + break; + case RelativeTime.Day: + results = "a day"; + break; + case RelativeTime.Days: + results = String.Format("{0} days", number); + break; + case RelativeTime.Month: + results = "a month"; + break; + case RelativeTime.Months: + results = String.Format("{0} months", number); + break; + case RelativeTime.Year: + results = "a year"; + break; + case RelativeTime.Years: + results = String.Format("{0} years", number); + break; + } + return !showSuffix ? results : String.Format(isFuture ? "in {0}" : "{0} ago", results); + } + } +} \ No newline at end of file diff --git a/MomentSharp/Globalization/RelativeTime.cs b/MomentSharp/Globalization/RelativeTime.cs new file mode 100644 index 0000000..d6d5347 --- /dev/null +++ b/MomentSharp/Globalization/RelativeTime.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MomentSharp.Globalization +{ + /// + /// Relative Time parts + /// Meant to emulate: http://momentjs.com/docs/#/displaying/fromnow/ + /// + public enum RelativeTime + { + Seconds, + Minute, + Minutes, + Hour, + Hours, + Day, + Days, + Month, + Months, + Year, + Years + }; +} diff --git a/MomentSharp/Manipulate.cs b/MomentSharp/Manipulate.cs new file mode 100644 index 0000000..d7edbe4 --- /dev/null +++ b/MomentSharp/Manipulate.cs @@ -0,0 +1,228 @@ +using System; +using System.Globalization; +using Pure = JetBrains.Annotations.PureAttribute; + +namespace MomentSharp +{ + public static class Manipulate + { + /// + /// Get the start of from DateTime.UtcNow. E.g. StartOf(DateTimeParts.Year) + /// return a new at the start of the current year. + /// + /// + /// + public static DateTime StartOf(DateTimeParts part) + { + return StartOf(DateTime.UtcNow, part); + } + + /// + /// Get the start of this at . E.g. DateTime.Now.StartOf(DateTimeParts.Year) + /// return a new at the start of the current year. + /// + /// + /// + /// + public static DateTime StartOf(this DateTime dateTime, DateTimeParts part) + { + switch (part) + { + case DateTimeParts.Year: + return new DateTime(dateTime.Year, 1, 1); + case DateTimeParts.Month: + return new DateTime(dateTime.Year, dateTime.Month, 1); + case DateTimeParts.Quarter: + return new DateTime(dateTime.Year, ((dateTime.Month - 1) / 3) * 3 + 1, 1); + case DateTimeParts.Week: + dateTime = dateTime.StartOf(DateTimeParts.Day); + var ci = CultureInfo.CurrentCulture; + var first = (int)ci.DateTimeFormat.FirstDayOfWeek; + var current = (int)dateTime.DayOfWeek; + return first <= current ? + dateTime.AddDays(-1 * (current - first)) : + dateTime.AddDays(first - current - 7); + case DateTimeParts.Day: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0); + case DateTimeParts.Hour: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, 0); + case DateTimeParts.Minute: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0, 0); + case DateTimeParts.Second: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, 0); + default: + throw new ArgumentException("No valid part was provided", "part"); + } + } + + /// + /// Get the end of from DateTime.UtcNow. E.g. EndOf(DateTimeParts.Year) + /// return a new at the end of the current year. + /// + /// + /// + public static DateTime EndOf(DateTimeParts part) + { + return EndOf(DateTime.UtcNow, part); + } + + /// + /// Get the end of from DateTime.UtcNow. E.g. EndOf(DateTimeParts.Year) + /// return a new at the end of the current year. + /// + /// + /// + /// + public static DateTime EndOf(this DateTime dateTime, DateTimeParts part) + { + switch (part) + { + case DateTimeParts.Year: + return StartOf(dateTime,DateTimeParts.Year).AddYears(1).AddSeconds(-1); + case DateTimeParts.Month: + return StartOf(dateTime, DateTimeParts.Month).AddMonths(1).AddSeconds(-1); + case DateTimeParts.Quarter: + return StartOf(dateTime, DateTimeParts.Quarter).AddMonths(3).AddSeconds(-1); + case DateTimeParts.Week: + return StartOf(dateTime, DateTimeParts.Week).AddDays(7).AddSeconds(-1); + case DateTimeParts.Day: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59, 0); + case DateTimeParts.Hour: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 59, 59, 0); + case DateTimeParts.Minute: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 59, 0); + case DateTimeParts.Second: + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, 999); + default: + throw new ArgumentException("No valid part was provided", "part"); + } + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// + [Pure] + public static DateTime SetYear(this DateTime dateTime, int year) + { + return new DateTime(year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// If set to true, it will bubble up the next part. E.g. 90 seconds becomes 1:30 minutes + /// + [Pure] + public static DateTime SetMonth(this DateTime dateTime, int month, bool bubble = false) + { + if (!bubble) + return new DateTime(dateTime.Year, month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + + var moment = dateTime.Moment(); + moment.Month = month; + + return moment.DateTime(true); + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// If set to true, it will bubble up the next part. E.g. 90 seconds becomes 1:30 minutes + /// + [Pure] + public static DateTime SetDay(this DateTime dateTime, int day, bool bubble = false) + { + if (!bubble) + return new DateTime(dateTime.Year, dateTime.Month, day, dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + + var moment = dateTime.Moment(); + moment.Day = day; + + return moment.DateTime(true); + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// If set to true, it will bubble up the next part. E.g. 90 seconds becomes 1:30 minutes + /// + [Pure] + public static DateTime SetHour(this DateTime dateTime, int hour, bool bubble = false) + { + if (!bubble) + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond); + + var moment = dateTime.Moment(); + moment.Hour = hour; + + return moment.DateTime(true); + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// If set to true, it will bubble up the next part. E.g. 90 seconds becomes 1:30 minutes + /// + [Pure] + public static DateTime SetMinute(this DateTime dateTime, int minute, bool bubble = false) + { + if (!bubble) + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, minute, dateTime.Second, dateTime.Millisecond); + + var moment = dateTime.Moment(); + moment.Minute = minute; + + return moment.DateTime(true); + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// If set to true, it will bubble up the next part. E.g. 90 seconds becomes 1:30 minutes + /// + [Pure] + public static DateTime SetSecond(this DateTime dateTime, int second, bool bubble = false) + { + if (!bubble) + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, second, dateTime.Millisecond); + + var moment = dateTime.Moment(); + moment.Second = second; + + return moment.DateTime(true); + } + + /// + /// Set the Year of this to , leaving all other all other parts the same. + /// + /// + /// + /// If set to true, it will bubble up the next part. E.g. 90 seconds becomes 1:30 minutes + /// + [Pure] + public static DateTime SetMillisecond(this DateTime dateTime, int millisecond, bool bubble = false) + { + if (!bubble) + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, millisecond); + + var moment = dateTime.Moment(); + moment.Millisecond = millisecond; + + return moment.DateTime(true); + } + + } +} diff --git a/MomentSharp/Moment.cs b/MomentSharp/Moment.cs new file mode 100644 index 0000000..aee8f78 --- /dev/null +++ b/MomentSharp/Moment.cs @@ -0,0 +1,62 @@ +using System; +using MomentSharp.Globalization; +using MomentSharp.Globalization.Languages; + +namespace MomentSharp +{ + public class Moment + { + /// + /// Get's a new Moment defaulting values to DateTime.UtcNow, unless is true in which values will be set to the min value + /// + /// use min values instead of UtcNow + public Moment(bool zero = false) + { + if (zero) + { + Year = DateTime.MinValue.Year; + Month = 1; + Day = 1; + Hour = 0; + Minute = 0; + Second = 0; + Millisecond = 0; + } + else + { + var now = DateTime.UtcNow; + Year = now.Year; + Month = now.Month; + Day = now.Day; + Hour = now.Hour; + Minute = now.Minute; + Second = now.Second; + Millisecond = now.Millisecond; + } + Language = SetLanguageByCulture(); + } + + private static ILocalize SetLanguageByCulture() + { + var culture = System.Threading.Thread.CurrentThread.CurrentCulture.ToString().Replace("-", ""); + switch (culture) + { + case "enUS": + return new EnUs(); + case "de": + return new De(); + } + return new EnUs(); + } + + public int Year { get; set; } + public int Month { get; set; } + public int Day { get; set; } + public int Hour { get; set; } + public int Minute { get; set; } + public int Second { get; set; } + public int Millisecond { get; set; } + + public ILocalize Language { get; set; } + } +} diff --git a/MomentSharp/MomentSharp.csproj b/MomentSharp/MomentSharp.csproj new file mode 100644 index 0000000..5d6186a --- /dev/null +++ b/MomentSharp/MomentSharp.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {7DF82747-DF0D-4969-91FF-30D5F8FEB7FD} + Library + Properties + MomentSharp + MomentSharp + v4.5 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MomentSharp/Parse.cs b/MomentSharp/Parse.cs new file mode 100644 index 0000000..bf1938f --- /dev/null +++ b/MomentSharp/Parse.cs @@ -0,0 +1,95 @@ +using System; + +namespace MomentSharp +{ + public static class Parse + { + /// + /// Converts javascript/Unix timestamp to DateTime + /// + /// TimeStamp in seconds + /// DateTime in UTC + public static DateTime UnixToDateTime(this double unixTimeStamp) + { + var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0); + dateTime = dateTime.AddSeconds(unixTimeStamp).ToUniversalTime(); + return dateTime; + } + + /// + /// Converts javascript/Unix timestamp to DateTime + /// + /// TimeStamp in seconds + /// DateTime in UTC + public static DateTime UnixToDateTime(this int unixTimeStamp) + { + return UnixToDateTime((double)unixTimeStamp); + } + + + /// + /// Convert this object to a + /// + /// A Moment Object + /// Whether or not to bubble to the next part. E.g. 90 seconds to 1 minute and 30 seconds. + /// If false, will throw exception given the example. + /// + public static DateTime DateTime(this Moment moment, bool bubble = false) + { + if (!bubble) + return new DateTime(moment.Year, moment.Month, moment.Day, moment.Hour, moment.Minute, moment.Second, + moment.Millisecond); + + Bubble.Millisecond(ref moment); + Bubble.Second(ref moment); + Bubble.Minute(ref moment); + Bubble.Hour(ref moment); + Bubble.Day(ref moment); + Bubble.Month(ref moment); + return new DateTime(moment.Year, moment.Month, moment.Day, moment.Hour, moment.Minute, moment.Second, moment.Millisecond); + } + + /// + /// Converts this to a object + /// + /// + /// + public static Moment Moment(this DateTime dateTime) + { + return new Moment + { + Year = dateTime.Year, + Month = dateTime.Month, + Day = dateTime.Day, + Hour = dateTime.Hour, + Minute = dateTime.Minute, + Second = dateTime.Second, + Millisecond = dateTime.Millisecond + }; + } + + /// + /// Converts this to UTC + /// + /// + /// For valid parameters see TimeZoneInfo.GetSystemTimeZones() + /// + public static DateTime ToUTC(this DateTime dateTime, string fromTimeZoneId) + { + return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, fromTimeZoneId, TimeZoneInfo.Utc.Id); + } + + /// + /// Converts this UTC time to another time zone + /// + /// + /// For valid parameters see TimeZoneInfo.GetSystemTimeZones() + /// + public static DateTime ToTimeZone(this DateTime dateTime, string toTimeZoneId) + { + dateTime = System.DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); + + return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, toTimeZoneId); + } + } +} \ No newline at end of file diff --git a/MomentSharp/Properties/AssemblyInfo.cs b/MomentSharp/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..74cae27 --- /dev/null +++ b/MomentSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MomentSharp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MomentSharp")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b308cdad-3139-4ddd-9a21-372fd1074db2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MomentSharp/Query.cs b/MomentSharp/Query.cs new file mode 100644 index 0000000..cdd429e --- /dev/null +++ b/MomentSharp/Query.cs @@ -0,0 +1,65 @@ +using System; + +namespace MomentSharp +{ + public static class Query + { + /// + /// Check if this DateTime is before , optionally at + /// + /// + /// + /// + /// + /// 10/20/2010 isBefore 12/31/2010, DateTimeParts.Year = false + /// 10/20/2010 isBefore 01/01/2011, DateTimeParts.Year = true + public static bool IsBefore(this DateTime dateTime, DateTime compareDateTime, DateTimeParts part = DateTimeParts.None) + { + if (part == DateTimeParts.None) return dateTime < compareDateTime; + + return dateTime.EndOf(part) < compareDateTime; + } + + /// + /// Check if this DateTime is after , optionally at + /// + /// + /// + /// + /// + public static bool IsAfter(this DateTime dateTime, DateTime compareDateTime, DateTimeParts part = DateTimeParts.None) + { + if (part == DateTimeParts.None) return dateTime > compareDateTime; + + return compareDateTime < dateTime.StartOf(part); + } + + /// + /// Check if this DateTime is the same as , optionally at + /// + /// + /// + /// + /// + /// + public static bool IsSame(this DateTime dateTime, DateTime compareDateTime, DateTimeParts part = DateTimeParts.None) + { + if (part == DateTimeParts.None) return dateTime == compareDateTime; + + return dateTime.StartOf(part) <= compareDateTime && compareDateTime <= dateTime.EndOf(part); + } + + /// + /// Check if this DateTime is between and , optionally at + /// + /// + /// + /// + /// + /// + public static bool IsBetween(this DateTime dateTime, DateTime fromDate, DateTime toDate, DateTimeParts part = DateTimeParts.None) + { + return dateTime.IsAfter(fromDate, part) && dateTime.IsBefore(toDate, part); + } + } +}