Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
protobuf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
protobuf
Commits
1fc48592
Commit
1fc48592
authored
Jan 15, 2016
by
Jon Skeet
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes to JSON timestamp/duration representations
parent
c74676f0
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
139 additions
and
35 deletions
+139
-35
JsonFormatterTest.cs
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+25
-3
JsonParserTest.cs
csharp/src/Google.Protobuf.Test/JsonParserTest.cs
+0
-1
DurationTest.cs
...p/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
+25
-5
TimestampTest.cs
.../src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
+23
-0
JsonFormatter.cs
csharp/src/Google.Protobuf/JsonFormatter.cs
+17
-12
JsonParser.cs
csharp/src/Google.Protobuf/JsonParser.cs
+7
-11
DurationPartial.cs
csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
+23
-0
TimestampPartial.cs
...rp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
+19
-3
No files found.
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
View file @
1fc48592
...
@@ -315,6 +315,15 @@ namespace Google.Protobuf
...
@@ -315,6 +315,15 @@ namespace Google.Protobuf
[
Test
]
[
Test
]
[
TestCase
(
"1970-01-01T00:00:00Z"
,
0
)]
[
TestCase
(
"1970-01-01T00:00:00Z"
,
0
)]
[
TestCase
(
"1970-01-01T00:00:00.000000001Z"
,
1
)]
[
TestCase
(
"1970-01-01T00:00:00.000000010Z"
,
10
)]
[
TestCase
(
"1970-01-01T00:00:00.000000100Z"
,
100
)]
[
TestCase
(
"1970-01-01T00:00:00.000001Z"
,
1000
)]
[
TestCase
(
"1970-01-01T00:00:00.000010Z"
,
10000
)]
[
TestCase
(
"1970-01-01T00:00:00.000100Z"
,
100000
)]
[
TestCase
(
"1970-01-01T00:00:00.001Z"
,
1000000
)]
[
TestCase
(
"1970-01-01T00:00:00.010Z"
,
10000000
)]
[
TestCase
(
"1970-01-01T00:00:00.100Z"
,
100000000
)]
[
TestCase
(
"1970-01-01T00:00:00.100Z"
,
100000000
)]
[
TestCase
(
"1970-01-01T00:00:00.100Z"
,
100000000
)]
[
TestCase
(
"1970-01-01T00:00:00.120Z"
,
120000000
)]
[
TestCase
(
"1970-01-01T00:00:00.120Z"
,
120000000
)]
[
TestCase
(
"1970-01-01T00:00:00.123Z"
,
123000000
)]
[
TestCase
(
"1970-01-01T00:00:00.123Z"
,
123000000
)]
...
@@ -350,6 +359,14 @@ namespace Google.Protobuf
...
@@ -350,6 +359,14 @@ namespace Google.Protobuf
[
TestCase
(
0
,
0
,
"0s"
)]
[
TestCase
(
0
,
0
,
"0s"
)]
[
TestCase
(
1
,
0
,
"1s"
)]
[
TestCase
(
1
,
0
,
"1s"
)]
[
TestCase
(-
1
,
0
,
"-1s"
)]
[
TestCase
(-
1
,
0
,
"-1s"
)]
[
TestCase
(
0
,
1
,
"0.000000001s"
)]
[
TestCase
(
0
,
10
,
"0.000000010s"
)]
[
TestCase
(
0
,
100
,
"0.000000100s"
)]
[
TestCase
(
0
,
1000
,
"0.000001s"
)]
[
TestCase
(
0
,
10000
,
"0.000010s"
)]
[
TestCase
(
0
,
100000
,
"0.000100s"
)]
[
TestCase
(
0
,
1000000
,
"0.001s"
)]
[
TestCase
(
0
,
10000000
,
"0.010s"
)]
[
TestCase
(
0
,
100000000
,
"0.100s"
)]
[
TestCase
(
0
,
100000000
,
"0.100s"
)]
[
TestCase
(
0
,
120000000
,
"0.120s"
)]
[
TestCase
(
0
,
120000000
,
"0.120s"
)]
[
TestCase
(
0
,
123000000
,
"0.123s"
)]
[
TestCase
(
0
,
123000000
,
"0.123s"
)]
...
@@ -362,14 +379,19 @@ namespace Google.Protobuf
...
@@ -362,14 +379,19 @@ namespace Google.Protobuf
[
TestCase
(
0
,
-
100000000
,
"-0.100s"
)]
[
TestCase
(
0
,
-
100000000
,
"-0.100s"
)]
[
TestCase
(
1
,
100000000
,
"1.100s"
)]
[
TestCase
(
1
,
100000000
,
"1.100s"
)]
[
TestCase
(-
1
,
-
100000000
,
"-1.100s"
)]
[
TestCase
(-
1
,
-
100000000
,
"-1.100s"
)]
// Non-normalized examples
[
TestCase
(
1
,
2123456789
,
"3.123456789s"
)]
[
TestCase
(
1
,
-
100000000
,
"0.900s"
)]
public
void
DurationStandalone
(
long
seconds
,
int
nanoseconds
,
string
expected
)
public
void
DurationStandalone
(
long
seconds
,
int
nanoseconds
,
string
expected
)
{
{
Assert
.
AreEqual
(
WrapInQuotes
(
expected
),
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
}.
ToString
());
Assert
.
AreEqual
(
WrapInQuotes
(
expected
),
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
}.
ToString
());
}
}
[
Test
]
[
TestCase
(
1
,
2123456789
)]
[
TestCase
(
1
,
-
100000000
)]
public
void
DurationStandalone_NonNormalized
(
long
seconds
,
int
nanoseconds
,
string
expected
)
{
Assert
.
Throws
<
InvalidOperationException
>(()
=>
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
}.
ToString
());
}
[
Test
]
[
Test
]
public
void
DurationField
()
public
void
DurationField
()
{
{
...
...
csharp/src/Google.Protobuf.Test/JsonParserTest.cs
View file @
1fc48592
...
@@ -745,7 +745,6 @@ namespace Google.Protobuf
...
@@ -745,7 +745,6 @@ namespace Google.Protobuf
[
TestCase
(
"--0.123456789s"
,
Description
=
"Double minus sign"
)]
[
TestCase
(
"--0.123456789s"
,
Description
=
"Double minus sign"
)]
// Violate upper/lower bounds in various ways
// Violate upper/lower bounds in various ways
[
TestCase
(
"315576000001s"
,
Description
=
"Integer part too large"
)]
[
TestCase
(
"315576000001s"
,
Description
=
"Integer part too large"
)]
[
TestCase
(
"315576000000.000000001s"
,
Description
=
"Integer part is upper bound; non-zero fraction"
)]
[
TestCase
(
"3155760000000s"
,
Description
=
"Integer part too long (positive)"
)]
[
TestCase
(
"3155760000000s"
,
Description
=
"Integer part too long (positive)"
)]
[
TestCase
(
"-3155760000000s"
,
Description
=
"Integer part too long (negative)"
)]
[
TestCase
(
"-3155760000000s"
,
Description
=
"Integer part too long (negative)"
)]
public
void
Duration_Invalid
(
string
jsonValue
)
public
void
Duration_Invalid
(
string
jsonValue
)
...
...
csharp/src/Google.Protobuf.Test/WellKnownTypes/DurationTest.cs
View file @
1fc48592
...
@@ -50,11 +50,6 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -50,11 +50,6 @@ namespace Google.Protobuf.WellKnownTypes
// Rounding is towards 0
// Rounding is towards 0
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(
2
),
new
Duration
{
Nanos
=
250
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(
2
),
new
Duration
{
Nanos
=
250
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(-
2
),
new
Duration
{
Nanos
=
-
250
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromTicks
(-
2
),
new
Duration
{
Nanos
=
-
250
}.
ToTimeSpan
());
// Non-normalized durations
Assert
.
AreEqual
(
TimeSpan
.
FromSeconds
(
3
),
new
Duration
{
Seconds
=
1
,
Nanos
=
2
*
Duration
.
NanosecondsPerSecond
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromSeconds
(
1
),
new
Duration
{
Seconds
=
3
,
Nanos
=
-
2
*
Duration
.
NanosecondsPerSecond
}.
ToTimeSpan
());
Assert
.
AreEqual
(
TimeSpan
.
FromSeconds
(-
1
),
new
Duration
{
Seconds
=
1
,
Nanos
=
-
2
*
Duration
.
NanosecondsPerSecond
}.
ToTimeSpan
());
}
}
[
Test
]
[
Test
]
...
@@ -100,5 +95,30 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -100,5 +95,30 @@ namespace Google.Protobuf.WellKnownTypes
Assert
.
AreEqual
(
new
Duration
{
Seconds
=
1
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromSeconds
(
1
)));
Assert
.
AreEqual
(
new
Duration
{
Seconds
=
1
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromSeconds
(
1
)));
Assert
.
AreEqual
(
new
Duration
{
Nanos
=
Duration
.
NanosecondsPerTick
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromTicks
(
1
)));
Assert
.
AreEqual
(
new
Duration
{
Nanos
=
Duration
.
NanosecondsPerTick
},
Duration
.
FromTimeSpan
(
TimeSpan
.
FromTicks
(
1
)));
}
}
[
Test
]
[
TestCase
(
0
,
Duration
.
MaxNanoseconds
+
1
)]
[
TestCase
(
0
,
Duration
.
MinNanoseconds
-
1
)]
[
TestCase
(
Duration
.
MinSeconds
-
1
,
0
)]
[
TestCase
(
Duration
.
MaxSeconds
+
1
,
0
)]
[
TestCase
(
1
,
-
1
)]
[
TestCase
(-
1
,
1
)]
public
void
ToTimeSpan_Invalid
(
long
seconds
,
int
nanoseconds
)
{
var
duration
=
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
Assert
.
Throws
<
InvalidOperationException
>(()
=>
duration
.
ToTimeSpan
());
}
[
Test
]
[
TestCase
(
0
,
Duration
.
MaxNanoseconds
)]
[
TestCase
(
0
,
Duration
.
MinNanoseconds
)]
[
TestCase
(
Duration
.
MinSeconds
,
Duration
.
MinNanoseconds
)]
[
TestCase
(
Duration
.
MaxSeconds
,
Duration
.
MaxNanoseconds
)]
public
void
ToTimeSpan_Valid
(
long
seconds
,
int
nanoseconds
)
{
// Only testing that these values don't throw, unlike their similar tests in ToTimeSpan_Invalid
var
duration
=
new
Duration
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
duration
.
ToTimeSpan
();
}
}
}
}
}
csharp/src/Google.Protobuf.Test/WellKnownTypes/TimestampTest.cs
View file @
1fc48592
...
@@ -61,6 +61,29 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -61,6 +61,29 @@ namespace Google.Protobuf.WellKnownTypes
Assert
.
AreEqual
(
new
DateTime
(
1969
,
12
,
31
,
23
,
59
,
59
).
AddMilliseconds
(
1
),
t2
.
ToDateTime
());
Assert
.
AreEqual
(
new
DateTime
(
1969
,
12
,
31
,
23
,
59
,
59
).
AddMilliseconds
(
1
),
t2
.
ToDateTime
());
}
}
[
Test
]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMinValue
-
1
,
Timestamp
.
MaxNanos
)]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMaxValue
+
1
,
0
)]
[
TestCase
(
0
,
-
1
)]
[
TestCase
(
0
,
Timestamp
.
MaxNanos
+
1
)]
public
void
ToDateTime_OutOfRange
(
long
seconds
,
int
nanoseconds
)
{
var
value
=
new
Timestamp
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
Assert
.
Throws
<
InvalidOperationException
>(()
=>
value
.
ToDateTime
());
}
// 1ns larger or smaller than the above values
[
Test
]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMinValue
,
0
)]
[
TestCase
(
Timestamp
.
UnixSecondsAtBclMaxValue
,
Timestamp
.
MaxNanos
)]
[
TestCase
(
0
,
0
)]
[
TestCase
(
0
,
Timestamp
.
MaxNanos
)]
public
void
ToDateTime_ValidBoundaries
(
long
seconds
,
int
nanoseconds
)
{
var
value
=
new
Timestamp
{
Seconds
=
seconds
,
Nanos
=
nanoseconds
};
value
.
ToDateTime
();
}
private
static
void
AssertRoundtrip
(
Timestamp
timestamp
,
DateTime
dateTime
)
private
static
void
AssertRoundtrip
(
Timestamp
timestamp
,
DateTime
dateTime
)
{
{
Assert
.
AreEqual
(
timestamp
,
Timestamp
.
FromDateTime
(
dateTime
));
Assert
.
AreEqual
(
timestamp
,
Timestamp
.
FromDateTime
(
dateTime
));
...
...
csharp/src/Google.Protobuf/JsonFormatter.cs
View file @
1fc48592
...
@@ -485,13 +485,14 @@ namespace Google.Protobuf
...
@@ -485,13 +485,14 @@ namespace Google.Protobuf
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Timestamp
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Timestamp
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Timestamp
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Timestamp
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
// Even if the original message isn't using the built-in classes, we can still build one... and then
// Even if the original message isn't using the built-in classes, we can still build one... and its
// rely on it being normalized.
// conversion will check whether or not it's normalized.
Timestamp
normalized
=
Timestamp
.
Normalize
(
seconds
,
nanos
);
// TODO: Perhaps the diagnostic-only formatter should not throw for non-normalized values?
Timestamp
ts
=
new
Timestamp
{
Seconds
=
seconds
,
Nanos
=
nanos
};
// Use .NET's formatting for the value down to the second, including an opening double quote (as it's a string value)
// Use .NET's formatting for the value down to the second, including an opening double quote (as it's a string value)
DateTime
dateTime
=
normalized
.
ToDateTime
();
DateTime
dateTime
=
ts
.
ToDateTime
();
builder
.
Append
(
dateTime
.
ToString
(
"yyyy'-'MM'-'dd'T'HH:mm:ss"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
(
dateTime
.
ToString
(
"yyyy'-'MM'-'dd'T'HH:mm:ss"
,
CultureInfo
.
InvariantCulture
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
normalized
.
Nanos
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
ts
.
Nanos
));
builder
.
Append
(
"Z\""
);
builder
.
Append
(
"Z\""
);
}
}
...
@@ -502,18 +503,22 @@ namespace Google.Protobuf
...
@@ -502,18 +503,22 @@ namespace Google.Protobuf
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
int
nanos
=
(
int
)
value
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
long
seconds
=
(
long
)
value
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
GetValue
(
value
);
// TODO: Perhaps the diagnostic-only formatter should not throw for non-normalized values?
// Even if the original message isn't using the built-in classes, we can still build one... and then
// Even if the original message isn't using the built-in classes, we can still build one... and then
// rely on it being normalized.
// rely on it being normalized.
Duration
normalized
=
Duration
.
Normalize
(
seconds
,
nanos
);
if
(!
Duration
.
IsNormalized
(
seconds
,
nanos
))
{
throw
new
InvalidOperationException
(
"Non-normalized duration value"
);
}
// The seconds part will normally provide the minus sign if we need it, but not if it's 0...
// The seconds part will normally provide the minus sign if we need it, but not if it's 0...
if
(
normalized
.
Seconds
==
0
&&
normalized
.
N
anos
<
0
)
if
(
seconds
==
0
&&
n
anos
<
0
)
{
{
builder
.
Append
(
'-'
);
builder
.
Append
(
'-'
);
}
}
builder
.
Append
(
normalized
.
S
econds
.
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
(
s
econds
.
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
n
ormalized
.
N
anos
));
AppendNanoseconds
(
builder
,
Math
.
Abs
(
nanos
));
builder
.
Append
(
"s\""
);
builder
.
Append
(
"s\""
);
}
}
...
@@ -598,15 +603,15 @@ namespace Google.Protobuf
...
@@ -598,15 +603,15 @@ namespace Google.Protobuf
// Output to 3, 6 or 9 digits.
// Output to 3, 6 or 9 digits.
if
(
nanos
%
1000000
==
0
)
if
(
nanos
%
1000000
==
0
)
{
{
builder
.
Append
((
nanos
/
1000000
).
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
((
nanos
/
1000000
).
ToString
(
"d
3
"
,
CultureInfo
.
InvariantCulture
));
}
}
else
if
(
nanos
%
1000
==
0
)
else
if
(
nanos
%
1000
==
0
)
{
{
builder
.
Append
((
nanos
/
1000
).
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
((
nanos
/
1000
).
ToString
(
"d
6
"
,
CultureInfo
.
InvariantCulture
));
}
}
else
else
{
{
builder
.
Append
(
nanos
.
ToString
(
"d"
,
CultureInfo
.
InvariantCulture
));
builder
.
Append
(
nanos
.
ToString
(
"d
9
"
,
CultureInfo
.
InvariantCulture
));
}
}
}
}
}
}
...
...
csharp/src/Google.Protobuf/JsonParser.cs
View file @
1fc48592
...
@@ -854,28 +854,24 @@ namespace Google.Protobuf
...
@@ -854,28 +854,24 @@ namespace Google.Protobuf
try
try
{
{
long
seconds
=
long
.
Parse
(
secondsText
,
CultureInfo
.
InvariantCulture
);
long
seconds
=
long
.
Parse
(
secondsText
,
CultureInfo
.
InvariantCulture
)
*
multiplier
;
int
nanos
=
0
;
int
nanos
=
0
;
if
(
subseconds
!=
""
)
if
(
subseconds
!=
""
)
{
{
// This should always work, as we've got 1-9 digits.
// This should always work, as we've got 1-9 digits.
int
parsedFraction
=
int
.
Parse
(
subseconds
.
Substring
(
1
));
int
parsedFraction
=
int
.
Parse
(
subseconds
.
Substring
(
1
));
nanos
=
parsedFraction
*
SubsecondScalingFactors
[
subseconds
.
Length
];
nanos
=
parsedFraction
*
SubsecondScalingFactors
[
subseconds
.
Length
]
*
multiplier
;
}
}
if
(
seconds
>=
Duration
.
MaxSeconds
)
if
(
!
Duration
.
IsNormalized
(
seconds
,
nanos
)
)
{
{
// Allow precisely 315576000000 seconds, but prohibit even 1ns more.
throw
new
InvalidProtocolBufferException
(
$"Invalid Duration value:
{
token
.
StringValue
}
"
);
if
(
seconds
>
Duration
.
MaxSeconds
||
nanos
>
0
)
{
throw
new
InvalidProtocolBufferException
(
"Invalid Duration value: "
+
token
.
StringValue
);
}
}
}
message
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
SetValue
(
message
,
seconds
*
multiplier
);
message
.
Descriptor
.
Fields
[
Duration
.
SecondsFieldNumber
].
Accessor
.
SetValue
(
message
,
seconds
);
message
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
SetValue
(
message
,
nanos
*
multiplier
);
message
.
Descriptor
.
Fields
[
Duration
.
NanosFieldNumber
].
Accessor
.
SetValue
(
message
,
nanos
);
}
}
catch
(
FormatException
)
catch
(
FormatException
)
{
{
throw
new
InvalidProtocolBufferException
(
"Invalid Duration value: "
+
token
.
StringValue
);
throw
new
InvalidProtocolBufferException
(
$"Invalid Duration value:
{
token
.
StringValue
}
"
);
}
}
}
}
...
...
csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs
View file @
1fc48592
...
@@ -57,15 +57,38 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -57,15 +57,38 @@ namespace Google.Protobuf.WellKnownTypes
/// </summary>
/// </summary>
public
const
long
MinSeconds
=
-
315576000000L
;
public
const
long
MinSeconds
=
-
315576000000L
;
internal
const
int
MaxNanoseconds
=
NanosecondsPerSecond
-
1
;
internal
const
int
MinNanoseconds
=
-
NanosecondsPerSecond
+
1
;
internal
static
bool
IsNormalized
(
long
seconds
,
int
nanoseconds
)
{
// Simple boundaries
if
(
seconds
<
MinSeconds
||
seconds
>
MaxSeconds
||
nanoseconds
<
MinNanoseconds
||
nanoseconds
>
MaxNanoseconds
)
{
return
false
;
}
// We only have a problem is one is strictly negative and the other is
// strictly positive.
return
Math
.
Sign
(
seconds
)
*
Math
.
Sign
(
nanoseconds
)
!=
-
1
;
}
/// <summary>
/// <summary>
/// Converts this <see cref="Duration"/> to a <see cref="TimeSpan"/>.
/// Converts this <see cref="Duration"/> to a <see cref="TimeSpan"/>.
/// </summary>
/// </summary>
/// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks>
/// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks>
/// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns>
/// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns>
/// <exception cref="InvalidOperationException">This value isn't a valid normalized duration, as
/// described in the documentation.</exception>
public
TimeSpan
ToTimeSpan
()
public
TimeSpan
ToTimeSpan
()
{
{
checked
checked
{
{
if
(!
IsNormalized
(
Seconds
,
Nanos
))
{
throw
new
InvalidOperationException
(
"Duration was not a valid normalized duration"
);
}
long
ticks
=
Seconds
*
TimeSpan
.
TicksPerSecond
+
Nanos
/
NanosecondsPerTick
;
long
ticks
=
Seconds
*
TimeSpan
.
TicksPerSecond
+
Nanos
/
NanosecondsPerTick
;
return
TimeSpan
.
FromTicks
(
ticks
);
return
TimeSpan
.
FromTicks
(
ticks
);
}
}
...
...
csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs
View file @
1fc48592
...
@@ -37,9 +37,17 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -37,9 +37,17 @@ namespace Google.Protobuf.WellKnownTypes
public
partial
class
Timestamp
public
partial
class
Timestamp
{
{
private
static
readonly
DateTime
UnixEpoch
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
DateTimeKind
.
Utc
);
private
static
readonly
DateTime
UnixEpoch
=
new
DateTime
(
1970
,
1
,
1
,
0
,
0
,
0
,
DateTimeKind
.
Utc
);
private
static
readonly
long
BclSecondsAtUnixEpoch
=
UnixEpoch
.
Ticks
/
TimeSpan
.
TicksPerSecond
;
// Constants determined programmatically, but then hard-coded so they can be constant expressions.
internal
static
readonly
long
UnixSecondsAtBclMinValue
=
-
BclSecondsAtUnixEpoch
;
private
const
long
BclSecondsAtUnixEpoch
=
62135596800
;
internal
static
readonly
long
UnixSecondsAtBclMaxValue
=
(
DateTime
.
MaxValue
.
Ticks
/
TimeSpan
.
TicksPerSecond
)
-
BclSecondsAtUnixEpoch
;
internal
const
long
UnixSecondsAtBclMaxValue
=
253402300799
;
internal
const
long
UnixSecondsAtBclMinValue
=
-
BclSecondsAtUnixEpoch
;
internal
const
int
MaxNanos
=
Duration
.
NanosecondsPerSecond
-
1
;
private
bool
IsNormalized
=>
Nanos
>=
0
&&
Nanos
<=
MaxNanos
&&
Seconds
>=
UnixSecondsAtBclMinValue
&&
Seconds
<=
UnixSecondsAtBclMaxValue
;
/// <summary>
/// <summary>
/// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>.
/// Returns the difference between one <see cref="Timestamp"/> and another, as a <see cref="Duration"/>.
...
@@ -99,8 +107,14 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -99,8 +107,14 @@ namespace Google.Protobuf.WellKnownTypes
/// <see cref="DateTime"/> value precisely on a second.
/// <see cref="DateTime"/> value precisely on a second.
/// </remarks>
/// </remarks>
/// <returns>This timestamp as a <c>DateTime</c>.</returns>
/// <returns>This timestamp as a <c>DateTime</c>.</returns>
/// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
/// incorrectly normalized or is outside the valid range.</exception>
public
DateTime
ToDateTime
()
public
DateTime
ToDateTime
()
{
{
if
(!
IsNormalized
)
{
throw
new
InvalidOperationException
(
@"Timestamp contains invalid values: Seconds={Seconds}; Nanos={Nanos}"
);
}
return
UnixEpoch
.
AddSeconds
(
Seconds
).
AddTicks
(
Nanos
/
Duration
.
NanosecondsPerTick
);
return
UnixEpoch
.
AddSeconds
(
Seconds
).
AddTicks
(
Nanos
/
Duration
.
NanosecondsPerTick
);
}
}
...
@@ -114,6 +128,8 @@ namespace Google.Protobuf.WellKnownTypes
...
@@ -114,6 +128,8 @@ namespace Google.Protobuf.WellKnownTypes
/// <see cref="DateTimeOffset"/> value precisely on a second.
/// <see cref="DateTimeOffset"/> value precisely on a second.
/// </remarks>
/// </remarks>
/// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns>
/// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns>
/// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
/// incorrectly normalized or is outside the valid range.</exception>
public
DateTimeOffset
ToDateTimeOffset
()
public
DateTimeOffset
ToDateTimeOffset
()
{
{
return
new
DateTimeOffset
(
ToDateTime
(),
TimeSpan
.
Zero
);
return
new
DateTimeOffset
(
ToDateTime
(),
TimeSpan
.
Zero
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment