I wrote some code to convert between these formats(jQuery UI datepicker, Timepicker, moment.js, DateTime class in.Net) for my requirements. The basic idea is to parse a source format string into literals and tokens with special meanings, then generate another format string according to the destination rules.
My code comes in C# and typescript, you can use either or both. There are some syntax ignored, such as week of year in moment.js due to lack of support in most other rules, but I think what got left is sufficient for my needs. I also created my own Standard rule, I'll stick to it in my future projects and only convert to local format at the last minute. Just remove it if you don't like it. You can also easily create other rules if required.
The C# version is tested more than the typescript version, however I can't say that there isn't any bugs. Use it at your own risk.
Here is how you use the C# version:
var j = @"literal d dd D DD o oo m mm M MM y yy @' d dd'";
var m = "[literal ]D DD ffffd ffffdd DDD DDDD M MM MMM MMMM YY YYYY X[ d dd]";
Assert.AreEqual(m, DateTimeFormatRules.Convert(j, DateTimeFormatRules.JqueryDatePicker, DateTimeFormatRules.MomentJs));
Assert.AreEqual(j, DateTimeFormatRules.Convert(m, DateTimeFormatRules.MomentJs, DateTimeFormatRules.JqueryDatePicker));
And the C# source code:
///
/// Represents a set of date/time formatting rules.
/// Properties could be null if the rule is not supported.
///
public abstract class DateTimeFormatRules
{
///
/// 1 ~ 31
///
public abstract string DayOfMonthShort { get; }
///
/// 01 ~ 31
///
public abstract string DayOfMonthLong { get; }
///
/// Mon ~ Sun
///
public abstract string DayOfWeekShort { get; }
///
/// Monday ~ Sunday
///
public abstract string DayOfWeekLong { get; }
///
/// 1 ~ 366
///
public abstract string DayOfYearShort { get; }
///
/// 001 ~ 366
///
public abstract string DayOfYearLong { get; }
///
/// 1 ~ 12
///
public abstract string MonthOfYearShort { get; }
///
/// 01 ~ 12
///
public abstract string MonthOfYearLong { get; }
///
/// Jan ~ Dec
///
public abstract string MonthNameShort { get; }
///
/// January ~ December
///
public abstract string MonthNameLong { get; }
///
/// 00 ~ 99
///
public abstract string YearShort { get; }
///
/// 0000 ~ 9999
///
public abstract string YearLong { get; }
///
/// AM ~ PM
///
public abstract string AmPm { get; }
///
/// 0 ~ 23
///
public abstract string Hour24Short { get; }
///
/// 00 ~ 23
///
public abstract string Hour24Long { get; }
///
/// 1 ~ 12
///
public abstract string Hour12Short { get; }
///
/// 01 ~ 12
///
public abstract string Hour12Long { get; }
///
/// 0 ~ 59
///
public abstract string MinuteShort { get; }
///
/// 00 ~ 59
///
public abstract string MinuteLong { get; }
///
/// 0 ~ 59
///
public abstract string SecondShort { get; }
///
/// 00 ~ 59
///
public abstract string SecondLong { get; }
///
/// 0 ~ 9
///
public abstract string FractionalSecond1 { get; }
///
/// 00 ~ 99
///
public abstract string FractionalSecond2 { get; }
///
/// 000 ~ 999
///
public abstract string FractionalSecond3 { get; }
///
/// +08:00
///
public abstract string TimeZone { get; }
///
/// 1384935963160
///
public abstract string UnixTimestamp { get; }
public abstract string MakeLiteral(string literal);
public abstract string ReadEscapedPart(string format, int startIndex, out int length);
public static DateTimeFormatRules Standard
{
get { return StandardDateTimeFormatRules.Instance; }
}
public static DateTimeFormatRules DotNet
{
get { return DotNetDateTimeFormatRules.Instance; }
}
public static DateTimeFormatRules MomentJs
{
get { return MomentJsDateTimeFormatRules.Instance; }
}
public static DateTimeFormatRules JqueryDatePicker
{
get { return JQueryUIDatePickerFormatRules.Instance; }
}
public static DateTimeFormatRules JqueryTimePicker
{
get { return JQueryUITimePickerFormatRules.Instance; }
}
public static string Convert(string format, DateTimeFormatRules sourceRules, DateTimeFormatRules destRules)
{
if (sourceRules == destRules)
return format;
var sb = new StringBuilder(format.Length);
var index = 0;
while (index < format.Length)
{
string literal;
int next;
var token = sourceRules.LocateNextToken(format, index, out next, out literal);
if (literal.Length > 0)
sb.Append(destRules.MakeLiteral(literal));
if (token.Length > 0)
sb.Append(destRules.AllTokens[sourceRules.TokenMap[token]]);
index = next;
}
return sb.ToString();
}
///
/// Locate next token in the format string
///
/// The input format string
/// The beginning index in format string to search
/// where to search next time
/// literal string before the token
/// The token, if any; otherwise string.Empty
private string LocateNextToken(string format, int begin, out int nextBegin, out string literal)
{
var sb = new StringBuilder(format.Length);
var index = begin;
while (index < format.Length)
{
int length;
var escaped = ReadEscapedPart(format, index, out length);
if (length > 0)
{
sb.Append(escaped);
index += length;
continue;
}
var token = TryingSequence
.FirstOrDefault(x => format.IndexOf(x, index, StringComparison.Ordinal) == index);
if (token == null)
{
sb.Append(format[index]);
index++;
continue;
}
nextBegin = index + token.Length;
literal = sb.ToString();
return token;
}
nextBegin = index;
literal = sb.ToString();
return "";
}
private string[] _tokens;
private string[] AllTokens
{
get
{
return _tokens ?? (_tokens = new[]
{
DayOfMonthShort, DayOfMonthLong,
DayOfWeekShort, DayOfWeekLong,
DayOfYearShort, DayOfYearLong,
MonthOfYearShort, MonthOfYearLong,
MonthNameShort, MonthNameLong,
YearShort, YearLong,
AmPm,
Hour24Short, Hour24Long,
Hour12Short, Hour12Long,
MinuteShort, MinuteLong,
SecondShort, SecondLong,
FractionalSecond1, FractionalSecond2, FractionalSecond3,
TimeZone,
UnixTimestamp,
});
}
}
private Dictionary _tokenMap;
private Dictionary TokenMap
{
get
{
return _tokenMap ?? (_tokenMap = AllTokens
.Select((token, index) => new
{
index,
token,
})
.Where(x => x.token != null)
.ToDictionary(x => x.token, x => x.index));
}
}
private string[] _tryingSequence;
private string[] TryingSequence
{
get
{
return _tryingSequence ?? (_tryingSequence = TokenMap.Keys
.OrderByDescending(x => x.Length)
.ToArray());
}
}
}
///
/// My own standard date time format rules
///
internal class StandardDateTimeFormatRules : DateTimeFormatRules
{
private StandardDateTimeFormatRules()
{
}
public static StandardDateTimeFormatRules Instance = new StandardDateTimeFormatRules();
public override string DayOfMonthShort
{
get { return "d"; }
}
public override string DayOfMonthLong
{
get { return "dd"; }
}
public override string DayOfWeekShort
{
get { return "ffffd"; }
}
public override string DayOfWeekLong
{
get { return "ffffdd"; }
}
public override string DayOfYearShort
{
get { return "D"; }
}
public override string DayOfYearLong
{
get { return "DD"; }
}
public override string MonthOfYearShort
{
get { return "M"; }
}
public override string MonthOfYearLong
{
get { return "MM"; }
}
public override string MonthNameShort
{
get { return "MMM"; }
}
public override string MonthNameLong
{
get { return "MMMM"; }
}
public override string YearShort
{
get { return "yy"; }
}
public override string YearLong
{
get { return "yyyy"; }
}
public override string AmPm
{
get { return "tt"; }
}
public override string Hour24Short
{
get { return "H"; }
}
public override string Hour24Long
{
get { return "HH"; }
}
public override string Hour12Short
{
get { return "h"; }
}
public override string Hour12Long
{
get { return "hh"; }
}
public override string MinuteShort
{
get { return "m"; }
}
public override string MinuteLong
{
get { return "mm"; }
}
public override string SecondShort
{
get { return "s"; }
}
public override string SecondLong
{
get { return "ss"; }
}
public override string FractionalSecond1
{
get { return "f"; }
}
public override string FractionalSecond2
{
get { return "ff"; }
}
public override string FractionalSecond3
{
get { return "fff"; }
}
public override string TimeZone
{
get { return "Z"; }
}
public override string UnixTimestamp
{
get { return "X"; }
}
public char[] ReservedChars
{
get { return "dDMytHhmsfZX\\".ToCharArray(); }
}
public override string MakeLiteral(string literal)
{
if (literal.IndexOfAny(ReservedChars) < 0)
return literal;
var sb = new StringBuilder(literal.Length * 2);
foreach (char c in literal)
{
if (ReservedChars.Contains(c))
sb.Append('\\');
sb.Append(c);
}
return sb.ToString();
}
public override string ReadEscapedPart(string format, int startIndex, out int length)
{
var sb = new StringBuilder();
var index = startIndex;
while (index < format.Length)
{
var c = format[index];
if (c == '\\')
{
sb.Append(index == format.Length - 1 ? '\\' : format[++index]);
index++;
continue;
}
break;
}
length = index - startIndex;
return sb.ToString();
}
}
///
/// Formating rules for .net DateTime.ToString(format)
///
internal class DotNetDateTimeFormatRules : DateTimeFormatRules
{
private DotNetDateTimeFormatRules()
{
}
public static DotNetDateTimeFormatRules Instance = new DotNetDateTimeFormatRules();
public override string DayOfMonthShort
{
get { return "d"; }
}
public override string DayOfMonthLong
{
get { return "dd"; }
}
public override string DayOfWeekShort
{
get { return "ffffd"; }
}
public override string DayOfWeekLong
{
get { return "ffffdd"; }
}
public override string DayOfYearShort
{
get { return null; }
}
public override string DayOfYearLong
{
get { return null; }
}
public override string MonthOfYearShort
{
get { return "M"; }
}
public override string MonthOfYearLong
{
get { return "MM"; }
}
public override string MonthNameShort
{
get { return "MMM"; }
}
public override string MonthNameLong
{
get { return "MMMM"; }
}
public override string YearShort
{
get { return "yy"; }
}
public override string YearLong
{
get { return "yyyy"; }
}
public override string AmPm
{
get { return "tt"; }
}
public override string Hour24Short
{
get { return "H"; }
}
public override string Hour24Long
{
get { return "HH"; }
}
public override string Hour12Short
{
get { return "h"; }
}
public override string Hour12Long
{
get { return "hh"; }
}
public override string MinuteShort
{
get { return "m"; }
}
public override string MinuteLong
{
get { return "mm"; }
}
public override string SecondShort
{
get { return "s"; }
}
public override string SecondLong
{
get { return "ss"; }
}
public override string FractionalSecond1
{
get { return "f"; }
}
public override string FractionalSecond2
{
get { return "ff"; }
}
public override string FractionalSecond3
{
get { return "fff"; }
}
public override string TimeZone
{
get { return "zzz"; }
}
public override string UnixTimestamp
{
get { return null; }
}
public char[] ReservedChars
{
get { return "dfFghHKmMstyz'\"".ToCharArray(); }
}
public override string MakeLiteral(string literal)
{
if (literal.IndexOfAny(ReservedChars) < 0)
return literal;
var sb = new StringBuilder(literal.Length * 2);
foreach (char c in literal)
{
if (ReservedChars.Contains(c))
sb.Append('\\');
sb.Append(c);
}
return sb.ToString();
}
public override string ReadEscapedPart(string format, int startIndex, out int length)
{
var sb = new StringBuilder();
var index = startIndex;
while (index < format.Length)
{
var c = format[index];
if (c == '\\')
{
sb.Append(index == format.Length - 1 ? '\\' : format[++index]);
index++;
continue;
}
if (c == '"')
{
while (++index < format.Length)
{
var cc = format[index];
if (cc == '"')
break;
if (cc == '\\')
{
sb.Append(index == format.Length - 1 ? '\\' : format[++index]);
}
else
{
sb.Append(cc);
}
}
index++;
continue;
}
if (c == '\'')
{
while (++index < format.Length)
{
var cc = format[index];
if (cc == '\'')
break;
if (cc == '\\')
{
sb.Append(index == format.Length - 1 ? '\\' : format[++index]);
}
else
{
sb.Append(cc);
}
}
index++;
continue;
}
break;
}
length = index - startIndex;
return sb.ToString();
}
}
internal class MomentJsDateTimeFormatRules : DateTimeFormatRules
{
private MomentJsDateTimeFormatRules()
{
}
public static MomentJsDateTimeFormatRules Instance = new MomentJsDateTimeFormatRules();
public override string DayOfMonthShort
{
get { return "D"; }
}
public override string DayOfMonthLong
{
get { return "DD"; }
}
public override string DayOfWeekShort
{
get { return "ffffd"; }
}
public override string DayOfWeekLong
{
get { return "ffffdd"; }
}
public override string DayOfYearShort
{
get { return "DDD"; }
}
public override string DayOfYearLong
{
get { return "DDDD"; }
}
public override string MonthOfYearShort
{
get { return "M"; }
}
public override string MonthOfYearLong
{
get { return "MM"; }
}
public override string MonthNameShort
{
get { return "MMM"; }
}
public override string MonthNameLong
{
get { return "MMMM"; }
}
public override string YearShort
{
get { return "YY"; }
}
public override string YearLong
{
get { return "YYYY"; }
}
public override string AmPm
{
get { return "A"; }
}
public override string Hour24Short
{
get { return "H"; }
}
public override string Hour24Long
{
get { return "HH"; }
}
public override string Hour12Short
{
get { return "h"; }
}
public override string Hour12Long
{
get { return "hh"; }
}
public override string MinuteShort
{
get { return "m"; }
}
public override string MinuteLong
{
get { return "mm"; }
}
public override string SecondShort
{
get { return "s"; }
}
public override string SecondLong
{
get { return "ss"; }
}
public override string FractionalSecond1
{
get { return "S"; }
}
public override string FractionalSecond2
{
get { return "SS"; }
}
public override string FractionalSecond3
{
get { return "SSS"; }
}
public override string TimeZone
{
get { return "Z"; }
}
public override string UnixTimestamp
{
get { return "X"; }
}
public char[] ReservedChars
{
get { return "MoDdeEwWYgGAaHhmsSzZX".ToCharArray(); }
}
public override string MakeLiteral(string literal)
{
literal = literal.Replace("[", "(").Replace("]", ")");
if (literal.IndexOfAny(ReservedChars) < 0)
return literal;
return '[' + literal + ']';
}
public override string ReadEscapedPart(string format, int startIndex, out int length)
{
if (format[startIndex] != '[')
{
length = 0;
return "";
}
var sb = new StringBuilder();
var index = startIndex + 1;
while (index < format.Length)
{
var c = format[index++];
if (c == ']')
break;
sb.Append(c);
}
length = index - startIndex;
return sb.ToString();
}
}
internal class JQueryUIDatePickerFormatRules : DateTimeFormatRules
{
private JQueryUIDatePickerFormatRules()
{
}
public static JQueryUIDatePickerFormatRules Instance = new JQueryUIDatePickerFormatRules();
public override string DayOfMonthShort
{
get { return "d"; }
}
public override string DayOfMonthLong
{
get { return "dd"; }
}
public override string DayOfWeekShort
{
get { return "D"; }
}
public override string DayOfWeekLong
{
get { return "DD"; }
}
public override string DayOfYearShort
{
get { return "o"; }
}
public override string DayOfYearLong
{
get { return "oo"; }
}
public override string MonthOfYearShort
{
get { return "m"; }
}
public override string MonthOfYearLong
{
get { return "mm"; }
}
public override string MonthNameShort
{
get { return "M"; }
}
public override string MonthNameLong
{
get { return "MM"; }
}
public override string YearShort
{
get { return "y"; }
}
public override string YearLong
{
get { return "yy"; }
}
public override string AmPm
{
get { return null; }
}
public override string Hour24Short
{
get { return null; }
}
public override string Hour24Long
{
get { return null; }
}
public override string Hour12Short
{
get { return null; }
}
public override string Hour12Long
{
get { return null; }
}
public override string MinuteShort
{
get { return null; }
}
public override string MinuteLong
{
get { return null; }
}
public override string SecondShort
{
get { return null; }
}
public override string SecondLong
{
get { return null; }
}
public override string FractionalSecond1
{
get { return null; }
}
public override string FractionalSecond2
{
get { return null; }
}
public override string FractionalSecond3
{
get { return null; }
}
public override string TimeZone
{
get { return null; }
}
public override string UnixTimestamp
{
get { return "@"; }
}
public char[] ReservedChars
{
get { return "dDomMy@'".ToCharArray(); }
}
public override string MakeLiteral(string literal)
{
if (literal.IndexOfAny(ReservedChars) < 0)
return literal;
return "'" + literal.Replace("'", "''") + "'";
}
public override string ReadEscapedPart(string format, int startIndex, out int length)
{
if (format[startIndex] != '\'')
{
length = 0;
return "";
}
var sb = new StringBuilder();
var index = startIndex;
while (++index < format.Length)
{
var c = format[index];
if (c == '\'')
{
index++;
if (index == format.Length)
{
break;
}
if (format[index] == '\'')
{
sb.Append(c);
}
else
{
break;
}
}
else
{
sb.Append(c);
}
}
length = index - startIndex;
return sb.ToString();
}
}
///
/// Formating rules for jQuery UI datepicker.
/// The time part is for timepicker (http://trentrichardson.com/examples/timepicker/)
///
internal class JQueryUITimePickerFormatRules : DateTimeFormatRules
{
private JQueryUITimePickerFormatRules()
{
}
public static JQueryUITimePickerFormatRules Instance = new JQueryUITimePickerFormatRules();
public override string DayOfMonthShort
{
get { return null; }
}
public override string DayOfMonthLong
{
get { return null; }
}
public override string DayOfWeekShort
{
get { return null; }
}
public override string DayOfWeekLong
{
get { return null; }
}
public override string DayOfYearShort
{
get { return null; }
}
public override string DayOfYearLong
{
get { return null; }
}
public override string MonthOfYearShort
{
get { return null; }
}
public override string MonthOfYearLong
{
get { return null; }
}
public override string MonthNameShort
{
get { return null; }
}
public override string MonthNameLong
{
get { return null; }
}
public override string YearShort
{
get { return null; }
}
public override string YearLong
{
get { return null; }
}
public override string AmPm
{
get { return "TT"; }
}
public override string Hour24Short
{
get { return "H"; }
}
public override string Hour24Long
{
get { return "HH"; }
}
public override string Hour12Short
{
get { return "h"; }
}
public override string Hour12Long
{
get { return "hh"; }
}
public override string MinuteShort
{
get { return "m"; }
}
public override string MinuteLong
{
get { return "mm"; }
}
public override string SecondShort
{
get { return "s"; }
}
public override string SecondLong
{
get { return "ss"; }
}
public override string FractionalSecond1
{
get { return null; }
}
public override string FractionalSecond2
{
get { return null; }
}
public override string FractionalSecond3
{
get { return "l"; }
}
public override string TimeZone
{
get { return "Z"; }
}
public override string UnixTimestamp
{
get { return null; }
}
public char[] ReservedChars
{
get { return "HhmslctTzZ'".ToCharArray(); }
}
public override string MakeLiteral(string literal)
{
if (literal.IndexOfAny(ReservedChars) < 0)
return literal;
return "'" + literal.Replace("'", "''") + "'";
}
public override string ReadEscapedPart(string format, int startIndex, out int length)
{
if (format[startIndex] != '\'')
{
length = 0;
return "";
}
var sb = new StringBuilder();
var index = startIndex;
while (++index < format.Length)
{
var c = format[index];
if (c == '\'')
{
index++;
if (index == format.Length)
{
break;
}
if (format[index] == '\'')
{
sb.Append(c);
}
else
{
break;
}
}
else
{
sb.Append(c);
}
}
length = index - startIndex;
return sb.ToString();
}
}