powershell/resources/runtime/csharp/json/Iso/IsoDate.cs (148 lines of code) (raw):
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
using System;
using System.Text;
namespace Carbon.Data
{
public struct IsoDate
{
public int Year { get; set; } // 0-3000
public int Month { get; set; } // 1-12
public int Day { get; set; } // 1-31
public int Hour { get; set; } // 0-24
public int Minute { get; set; } // 0-60 (60 is a special case)
public int Second { get; set; } // 0-60 (60 is used for leap seconds)
public double Millisecond { get; set; } // 0-999.9...
public TimeSpan Offset { get; set; }
public DateTimeKind Kind { get; set; }
public TimeSpan TimeOfDay => new TimeSpan(Hour, Minute, Second);
public DateTime ToDateTime()
{
if (Kind == DateTimeKind.Utc || Offset == TimeSpan.Zero)
{
return new DateTime(Year, Month, Day, Hour, Minute, Second, (int)Millisecond, DateTimeKind.Utc);
}
return ToDateTimeOffset().DateTime;
}
public DateTimeOffset ToDateTimeOffset()
{
return new DateTimeOffset(
Year,
Month,
Day,
Hour,
Minute,
Second,
(int)Millisecond,
Offset
);
}
public DateTime ToUtcDateTime()
{
return ToDateTimeOffset().UtcDateTime;
}
public override string ToString()
{
var sb = new StringBuilder();
// yyyy-MM-dd
sb.Append($"{Year}-{Month:00}-{Day:00}");
if (TimeOfDay > new TimeSpan(0))
{
sb.Append($"T{Hour:00}:{Minute:00}");
if (TimeOfDay.Seconds > 0)
{
sb.Append($":{Second:00}");
}
}
if (Offset.Ticks == 0)
{
sb.Append('Z'); // UTC
}
else
{
if (Offset.Ticks >= 0)
{
sb.Append('+');
}
sb.Append($"{Offset.Hours:00}:{Offset.Minutes:00}");
}
return sb.ToString();
}
public static IsoDate FromDateTimeOffset(DateTimeOffset date)
{
return new IsoDate {
Year = date.Year,
Month = date.Month,
Day = date.Day,
Hour = date.Hour,
Minute = date.Minute,
Second = date.Second,
Offset = date.Offset,
Kind = date.Offset == TimeSpan.Zero ? DateTimeKind.Utc : DateTimeKind.Unspecified
};
}
private static readonly char[] timeSeperators = { ':', '.' };
public static IsoDate Parse(string text)
{
var tzIndex = -1;
var timeIndex = text.IndexOf('T');
var builder = new IsoDate { Day = 1, Month = 1 };
// TODO: strip the time zone offset off the end
string dateTime = text;
string timeZone = null;
if (dateTime.IndexOf('Z') > -1)
{
tzIndex = dateTime.LastIndexOf('Z');
builder.Kind = DateTimeKind.Utc;
}
else if (dateTime.LastIndexOf('+') > 10)
{
tzIndex = dateTime.LastIndexOf('+');
}
else if (dateTime.LastIndexOf('-') > 10)
{
tzIndex = dateTime.LastIndexOf('-');
}
if (tzIndex > -1)
{
timeZone = dateTime.Substring(tzIndex);
dateTime = dateTime.Substring(0, tzIndex);
}
string date = (timeIndex == -1) ? dateTime : dateTime.Substring(0, timeIndex);
var dateParts = date.Split(Seperator.Dash); // '-'
for (int i = 0; i < dateParts.Length; i++)
{
var part = dateParts[i];
switch (i)
{
case 0: builder.Year = int.Parse(part); break;
case 1: builder.Month = int.Parse(part); break;
case 2: builder.Day = int.Parse(part); break;
}
}
if (timeIndex > -1)
{
string[] timeParts = dateTime.Substring(timeIndex + 1).Split(timeSeperators);
for (int i = 0; i < timeParts.Length; i++)
{
var part = timeParts[i];
switch (i)
{
case 0: builder.Hour = int.Parse(part); break;
case 1: builder.Minute = int.Parse(part); break;
case 2: builder.Second = int.Parse(part); break;
case 3: builder.Millisecond = double.Parse("0." + part) * 1000; break;
}
}
}
if (timeZone != null && timeZone != "Z")
{
var hours = int.Parse(timeZone.Substring(1, 2));
var minutes = int.Parse(timeZone.Substring(4, 2));
if (timeZone[0] == '-')
{
hours = -hours;
minutes = -minutes;
}
builder.Offset = new TimeSpan(hours, minutes, 0);
}
return builder;
}
}
/*
YYYY # eg 1997
YYYY-MM # eg 1997-07
YYYY-MM-DD # eg 1997-07-16
YYYY-MM-DDThh:mmTZD # eg 1997-07-16T19:20+01:00
YYYY-MM-DDThh:mm:ssTZD # eg 1997-07-16T19:20:30+01:00
YYYY-MM-DDThh:mm:ss.sTZD # eg 1997-07-16T19:20:30.45+01:00
where:
YYYY = four-digit year
MM = two-digit month (01=January, etc.)
DD = two-digit day of month (01 through 31)
hh = two digits of hour (00 through 23) (am/pm NOT allowed)
mm = two digits of minute (00 through 59)
ss = two digits of second (00 through 59)
s = one or more digits representing a decimal fraction of a second
TZD = time zone designator (Z or +hh:mm or -hh:mm)
*/
}